Home Forum

Download And Install App Programmatically

Hello,

I'm currently working on an application that has been built so that it can manage upgrading and downgrading of versions away from the Play Store using my company's web servers. This worked fine until we tried compiling against Nougat.

The following is our code to download and install the new apk file:

// New file
File newAPKFile = new File(context.getExternalFilesDir(null), newfileName);
FileOutputStream fos = new FileOutputStream(newAPKFile);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fos = context.openFileOutput(newAPKFile.getName(), context.MODE_PRIVATE);
} else {
fos = context.openFileOutput(newAPKFile.getName(), context.MODE_WORLD_READABLE| context.MODE_WORLD_WRITEABLE);
}
// Download the new APK file
InputStream is = httpConn.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.flush();
fos.close();
is.close();
// Start the standard installation window
File fileLocation = new File(context.getExternalFilesDir(null), newfileName);
Intent downloadIntent;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileProvider", fileLocation);
downloadIntent = new Intent(Intent.ACTION_VIEW);
downloadIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
downloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
downloadIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
downloadIntent = new Intent(Intent.ACTION_VIEW);
downloadIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
downloadIntent.setDataAndType(Uri.fromFile(fileLocation), "application/vnd.android.package-archive");
}
context.startActivity(downloadIntent);​


This is what I'm getting in logcat:

Process: com.google.android.packageinstaller, PID: 3807
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:318)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{d42d519 3807:com.google.android.packageinstaller/u0a17} (pid=3807, uid=10017) that is not exported from uid 10085
at android.os.Parcel.readException(Parcel.java:1683)
at android.os.Parcel.readException(Parcel.java:1636)
at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:4169)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:5434)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2267)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1515)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1129)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:982)
at android.content.ContentResolver.openInputStream(ContentResolver.java:702)
at com.android.packageinstaller.PackageInstallerActivity$StagingAsyncTask.doInBackground(PackageInstallerActivity.java:732)
at com.android.packageinstaller.PackageInstallerActivity$StagingAsyncTask.doInBackground(PackageInstallerActivity.java:723)
at android.os.AsyncTask$2.call(AsyncTask.java:304)


Anybody got any ideas on this?
 

Related:

#1 Cruzeberry, Sep 13, 2017
I have tried a few things on that page, got me a little further...

I'm now getting "There was a problem parsing the package", with this in logcat:

W/zipro: Error opening archive /data/user_de/0/com.google.android.packageinstaller/cache/package158645152.apk: Invalid file
Is this to do with where I am storing the file? No idea what that .apk file name is, must be autogenerated by the process?
 
#3 Cruzeberry, Sep 14, 2017
We need to see the full stack trace, and also the part of your code which is attempting to unpack the APK.
 
#4 LV426, Sep 14, 2017
Oh ok, as your application hasn't crashed, there won't be a stack trace. But I would like to see the code which is unpacking and installing the APK.
 
#5 LV426, Sep 14, 2017
Hi, the code I'm using is as follows: (I have just been through and added some more debug lines and the size of the file in the location I'm downloading to appears to be 0, so it could be that I'm not saving the file correctly?)

// The following sets the connection for downloading the new apk
URL url = new URL("https://ourserver.com/ourApplication.apk");
URLConnection conn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
int checkConn = httpConn.getResponseCode();

// The following sets up the filename for saving the apk to the device
// Also create a file output stream to write to the file
File newAPKFile = new File(context.getExternalFilesDir(null), newfileName);
FileOutputStream fos = new FileOutputStream(newAPKFile);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fos = context.openFileOutput(newAPKFile.getName(), context.MODE_PRIVATE);
} else {
fos = context.openFileOutput(newAPKFile.getName(), context.MODE_WORLD_READABLE| context.MODE_WORLD_WRITEABLE);
}

// Download the new APK file
InputStream is = httpConn.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.flush();
fos.close();
is.close();

// Start the standard installation window
File fileLocation = new File(context.getExternalFilesDir(null), newfileName);
Intent downloadIntent;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileProvider", fileLocation);
downloadIntent = new Intent(Intent.ACTION_VIEW);
downloadIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
downloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
downloadIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(downloadIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, apkUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
} else {
downloadIntent = new Intent(Intent.ACTION_VIEW);
downloadIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
downloadIntent.setDataAndType(Uri.fromFile(fileLocation), "application/vnd.android.package-archive");
}
context.startActivity(downloadIntent);
 
#6 Cruzeberry, Sep 14, 2017