Programmatically install an apk in Android 7 / api24 answers a similar question, but I want to programmatically install an APK from app internal storage (on an SD card) in Android 5.0 - 6.0 too.
If it weren't from app internal storage, I could startActivity() with an intent whose action is ACTION_VIEW and whose data URI has a file:// scheme (using Uri.fromFile()). But I've tried that and it won't work with app internal storage, which isn't readable by other apps. I'm basing this on log messages like
W/asset: Asset path /storage/C5DF-1113/Android/data/com.example/files/foo.apk is neither a directory nor file (type=1).
W/InstallFlowAnalytics: Failed to hash APK contents
java.io.FileNotFoundException: /storage/C5DF-1113/Android/data/com.example/files/foo.apk: open failed: ENOENT (No such file or directory)
I've tried subclassing ContentProvider, using a content:// scheme. But apparently (and see CommonsWare's answer here), the package installer doesn't support the content:// scheme until Android 7.0. I get log messages like the following in Android 6.0:
I/ActivityManager: START u0 {act=android.intent.action.VIEW dat=content://com.example.provider/internal/foo.apk typ=application/vnd.android.package-archive flg=0x10000001} from uid 10159 on display 0
E/Updates: installNewApk
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=content://com.example.provider/internal/foo.apk typ=application/vnd.android.package-archive flg=0x10000001 }
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1805)
where the only apparent difference between an intent that matches the package installer and an intent that doesn't, is the data URI changing from file://... to content://....
There are answers elsewhere on installing an app silently, which is not a requirement for my project. There are also solutions that use reflection to get at undocumented features, or that require the device to be rooted, but those techniques are too brittle or user-unfriendly for this project.
There is also the option to copy the APK from app internal storage to external storage before launching the installer. We might have to go there, but the reason the APK is in internal storage in the first place is for purposes of privacy and security. (I realize this is far from bulletproof, but it is better than nothing and it's what our clients want.)
I'm willing to do a conditional based on the present version of Android, but at this point I still don't know what to do in 5.0 - 6.0.
Thanks for any suggestions.
to get the Uri for apk file from internal storage directory you can use this method it works me :
public static Uri getApkUri(Activity activity, String path) {
// Before N, a MODE_WORLD_READABLE file could be passed via the ACTION_INSTALL_PACKAGE
// Intent. Since N, MODE_WORLD_READABLE files are forbidden, and a FileProvider is
// recommended.
boolean useFileProvider = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
int fileMode = useFileProvider ? Context.MODE_PRIVATE : Context.MODE_WORLD_READABLE;
String tempFilename = "tmp.apk";
byte[] buffer = new byte[16384];
try (InputStream is = new FileInputStream(path);
FileOutputStream fout = activity.openFileOutput(tempFilename, fileMode)) {
int n;
while ((n = is.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
Logger.e(TAG, ":getApkUri + " + "Failed to write temporary APK file", e);
}
if (useFileProvider) {
File toInstall = new File(activity.getFilesDir(), tempFilename);
return FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID, toInstall);
} else {
return Uri.fromFile(activity.getFileStreamPath(tempFilename));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With