• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

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?
 
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?
 
We need to see the full stack trace, and also the part of your code which is attempting to unpack the APK.
 
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.
 
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);
 
Yes, have you tried running your application in debug mode, and interactively debugging it by using breakpoints and stepping through the code.
If you don't know how to do this, please read the following thread as a starting point:

https://androidforums.com/threads/please-read-me-before-posting.987318/

You should also show your code in [code][/code] tags, with proper indentation, to make it as readable as possible.
Thanks.
 
Yeah I've stepped through it, this is the only way I've been able to get the error messages out that I'm seeing above.

Sorry about the unreadable code, I'll do it properly next time
 
For anyone else that struggles with this, I've resolved the issue now! I changed the following:

Code:
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);
}

to this:

Code:
FileOutputStream fos;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
   File newAPKFile = new File(context.getExternalFilesDir(null), newfileName);
   fos = new FileOutputStream(newAPKFile);
} else {
   fos = context.openFileOutput(newfileName, context.MODE_WORLD_READABLE| context.MODE_WORLD_WRITEABLE);
}

The full (working) code is as follows:

Code:
   FileOutputStream fos;
   if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      File newAPKFile = new File(context.getExternalFilesDir(null), newfileName);
      fos = new FileOutputStream(newAPKFile);
   } else {
      fos = context.openFileOutput(newfileName, 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
 Intent downloadIntent;
 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      File fileLocation = new File(context.getExternalFilesDir(null), newfileName);
      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 {
      File fileLocation = new File(context.getFilesDir(), newfileName);
      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);

Hope this helps someone!
 
Hello, thank you very much for the contributions, they have been very helpful. For me it works well the download, but when executing startActivity, I get a window that asks me to "open with", where it gives me several options of applications, how to avoid this?

I'm working on android 7
 
Back
Top Bottom