Reputation: 1464
I made an application that attempts to download an APK update to internal storage from a remote server, and then install the update on the device. I tested it on a API24 device and everything works as expected.
Now I am testing it on a API 19 device (to test the part of the code pertaining to the old way of updating apps) and I keep getting "Parse error when parsing manifest". What puzzles me is that I can install the APK manually on the device and there is no parse error, so I don't think the problem is in the actual manifest file. I checked the length of the downloaded APK file in the internal storage and it appears to be fully downloaded. The code for downloading the APK is the same for all API versions.
Code:
pathToApk = getFilesDir() + "/update.apk";
try (InputStream is = response.body().byteStream();
BufferedInputStream input = new BufferedInputStream(is);
OutputStream os = new FileOutputStream(pathToApk)) {
byte[] data = new byte[1024];
int count;
while ((count = input.read(data)) != -1) {
os.write(data, 0, count);
}
}
File toInstall = new File(pathToApk);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Uri apkUri = Uri.fromFile(toInstall);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
Error:
W/PackageParser: Unable to read AndroidManifest.xml of /data/data/my.package.name/files/update.apk
java.io.FileNotFoundException: AndroidManifest.xml
at android.content.res.AssetManager.openXmlAssetNative(Native Method)
at android.content.res.AssetManager.openXmlBlockAsset(AssetManager.java:501)
at android.content.res.AssetManager.openXmlResourceParser(AssetManager.java:469)
at android.content.pm.PackageParser.parsePackage(PackageParser.java:544)
at com.android.packageinstaller.PackageUtil.getPackageInfo(PackageUtil.java:73)
at com.android.packageinstaller.PackageInstallerActivity.onCreate(PackageInstallerActivity.java:467)
at android.app.Activity.performCreate(Activity.java:5350)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1088)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2320)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5322)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:829)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645)
at dalvik.system.NativeStart.main(Native Method)
11-10 10:16:24.213 4140-4140/? W/PackageInstaller: Parse error when parsing manifest. Discontinuing installation
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="my.package.name">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".main.activities.UpdateActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>
EDIT
See my answer for solution.
Upvotes: 4
Views: 5173
Reputation: 1843
Nicely described HERE
This is how i managed
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N){
outputFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "ABC.apk")
} else {
outputFile = File(filesDir, "ABC.apk")
}
Upvotes: 0
Reputation: 1464
The problem was that the system could not read the APK from the private internal storage of my application on API<=23. The fix was pretty straightforward.
Changed this line:
OutputStream os = new FileOutputStream(pathToApk)
To this:
boolean old = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;
OutputStream os = old ?
openFileOutput("update.apk", Context.MODE_WORLD_READABLE)
: new FileOutputStream(pathToApk)
Upvotes: 1
Reputation: 8237
Add request here .
1.checkSelfPermission
for Manifest.permission.READ_EXTERNAL_STORAGE
if granted , you can do readFile();
If not ,you should requestPermissions
.
2.deal with onRequestPermissionsResult
in your code
/**
* permission code
*/
private static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
/**
* requestPermissions and do something
*
*/
public void requestRead() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
} else {
readFile();
}
}
/**
* do you want to do
*/
public void readFile() {
// do something
}
/**
* onRequestPermissionsResult
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readFile();
} else {
// Permission Denied
Toast.makeText(ToolbarActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
Add permission to the manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Upvotes: 0