93

My app has an auto-update feature that download an APK and when the download is finished that a Intent.VIEW_ACTION to open the app and let the user install the downloaded apk

Uri uri = Uri.parse("file://" + destination);
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
install.setDataAndType(uri,
    manager.getMimeTypeForDownloadedFile(downloadId));
activity.startActivity(install);

This works great for all the device < 24

Now with Android 24 apparently we are not allowed any more to start intents with file:/// and after some googling it was advised to use A File Provider

new code:

Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri apkUri = FileProvider.getUriForFile(AutoUpdate.this,
    BuildConfig.APPLICATION_ID + ".provider", file);
install.setDataAndType(apkUri,
    manager.getMimeTypeForDownloadedFile(downloadId));
activity.startActivity(install);

Now activity.startActivity(install); throws an error

No Activity found to handle Intent { act=android.intent.action.VIEW dat=content://com.xxxx.xx.provider/MyFolder/Download/MyApkFile.apk typ=application/vnd.android.package-archive flg=0x4000000 }

Is there any way I can open the APK viewer in Android 7 (24) ?

alexrnov
  • 2,346
  • 3
  • 18
  • 34
Johny19
  • 5,364
  • 14
  • 61
  • 99
  • 3
    Use `ACTION_INSTALL_PACKAGE`. That [worked for me](https://code.google.com/p/android/issues/detail?id=205827) as of the June edition of the N Developer Preview. – CommonsWare Aug 25 '16 at 14:17
  • 2
    No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE ... – Johny19 Aug 25 '16 at 14:25
  • 1
    I have no problems with `ACTION_INSTALL_PACKAGE`, using a `FileProvider` to serve the APK, when tested on a Nexus 9 running Android 7.0 (build NRD90M). Compared with your `Intent`, besides the action string difference, I do not use `FLAG_ACTIVITY_CLEAR_TOP`, and I use `setData()` rather than `setDataAndType()`. – CommonsWare Aug 25 '16 at 14:40
  • where do you get "file" from? the last argument in `getUriForFile` – Raj Dec 11 '16 at 04:13
  • 2
    Intent.ACTION_INSTALL_PACKAGE is deprecated, use Intent.ACTION_VIEW – Kenneth Argo Dec 09 '20 at 22:48
  • It this behavior still allowed in apps with `API 31` and above? Wouldn't this behavior be flagged by Play Protect as a Dynamic Downloader. – IgorGanapolsky May 09 '22 at 17:35

5 Answers5

192

After a lot of trying I have been able to solve this by creating different Intents for anything lower than Nougat as using the FileProvider to create an install intent with Android Versions before Nougat causes the error:

ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }

While using a normal Uri on Android Nougat creates the following error:

FileUriExposedException: file:///XXX.apk exposed beyond app through Intent.getData()

My solution which is working for me with Android N on the emulator and a phone running Android M.

File toInstall = new File(appDirectory, appName + ".apk");
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri apkUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileprovider", toInstall);
    intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
    intent.setData(apkUri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
    Uri apkUri = Uri.fromFile(toInstall);
    intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
activity.startActivity(intent);

UPDATE FOR Android Nougat 7.1:

You also need to add the permission REQUEST_INSTALL_PACKAGES in your manifest. Its available from Api Level 23 (Android 6.0 Marshmallow) and required from Level 25 (Android 7.1 Nougat).

UPDATE:

Remember to request the permissions for read and write to external storage if the file you try to install is on the external storage. And also to set up a correct FileProvider for Android Nougat and above.

First check if you have write permission by calling canReadWriteExternal() below, if not call requestPermission() before:

private static final int REQUEST_WRITE_PERMISSION = 786;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_WRITE_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED)
        Toast.makeText(this, "Permission granted", Toast.LENGTH_LONG).show();
}

private void requestPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION);
}

private boolean canReadWriteExternal() {
    return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
            ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED;
}

Here is an example of a file provider for the Download folder on the external storage. AndroidManifest.xml:

<application ... >
    ...

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>
</application>

resources/xml/filepaths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_download" path="Download"/>
</paths>

If you get an error while installing the .apk saying something like "There is a problem parsing the package." it could be that you haven't asked for the read/write permission or the file you try to install doesn't exist or is corrupt.

UPDATE FOR Android Oreo 8.0:

You have to check if current application is allowed to install the APK on Android Oreo 8.0 or above.

You can check if your app is allowed to install APK by using canRequestPackageInstalls method of PackageManager class. If it returns false, then you can launch intent with ACTION_MANAGE_UNKNOWN_APP_SOURCES action to launch settings dialog where user can allow the app to install APKs.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O 
        && !getPackageManager().canRequestPackageInstalls()) {
    Intent unknownAppSourceIntent = new Intent()
            .setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
            .setData(Uri.parse(String.format("package:%s", getPackageName())));

    unknownAppSourceDialog.launch(unknownAppSourceIntent);
} else {
    // App already have the permission to install so launch the APK installation.
    startActivity(intent);
}

Make sure you add the following code to your activity to receive the result of intent.

ActivityResultLauncher<Intent> unknownAppSourceDialog = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            // User has allowed app to install APKs
            // so we can now launch APK installation.
            startActivity(intent);
        }
    });
Ravi Patel
  • 2,136
  • 3
  • 32
  • 48
just_user
  • 11,769
  • 19
  • 90
  • 135
  • can you post where you get `toInstall`? – Raj Dec 11 '16 at 03:28
  • @emaillenin I have added it. It just a File pointing to the .apk you want to install. – just_user Dec 12 '16 at 08:42
  • 3
    My issue was that I was calling `setFlags` twice which basically resets the first flag. I had to use `addFlag` instead. – Raj Dec 13 '16 at 09:41
  • but its not opening app after installing – andro Jan 20 '17 at 10:50
  • @andro if the user down't press open after install the app wont open. Then you need to create a new intent to open that app. – just_user Jan 20 '17 at 10:57
  • @just_user after installing done its not asking open . it just closing it – andro Jan 20 '17 at 11:02
  • @just_user can you look into my code http://stackoverflow.com/questions/41762043/android-n-install-apk-viva-code?noredirect=1#comment70715648_41762043 – andro Jan 20 '17 at 11:07
  • @just_user i am trying to do the same via shell using pm install -r its throwing security exception in N whereas it works in M. Have you came across it ? – Gautam Mar 01 '17 at 17:21
  • 1
    I did that, but I'm getting an error saying "There is a problem parsing the package". If I use a public directory, the error is gone, but unfortunately I need to use a private one. – Kuri May 12 '17 at 12:29
  • Then you should probably look into FileProvider https://developer.android.com/reference/android/support/v4/content/FileProvider.html. But you have to share the file publicly for the package manger to be able to read it. So if you have it downloaded privately, copy it to a public location and delete after install is done. – just_user May 12 '17 at 12:53
  • 40
    Great answer - thank you! It's worth mentioning that as of **Oreo** you must also add the **REQUEST_INSTALL_PACKAGES** permission to your manifest. Otherwise it just silently fails. – Jasongiss Nov 11 '17 at 21:44
  • Under Nougat this isn't working for me. I get an exception android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://com.myApp.provider/external_files/myMyAppAPK/CheckIn20180122.APK flg=0x1 } – Chris Nevill Jan 22 '18 at 14:55
  • OK this is bizarre - in my case I followed this post : https://stackoverflow.com/questions/40098073/no-activity-found-to-handle-intent-act-android-intent-action-view-trying-to-in And that fixed it. Basically because I had renamed the APK it didn't work. Renamed it back to the APK's original name and it worked! – Chris Nevill Jan 22 '18 at 15:20
  • @ChrisNevill that is bizarre! – just_user Jan 23 '18 at 17:24
  • @just_user :- I have just followed your instruction but I am getting issue classNotFoundException . can you please let me know why i am getting this one. – Peter Feb 16 '18 at 07:03
  • @just_user `Process: xxxx, PID: 4224 java.lang.RuntimeException: Unable to instantiate application xxxx.MyApplication: java.lang.ClassNotFoundException: Didn't find class "xxxx.MyApplication" on path: DexPathList[[],nativeLibraryDirectories=[/data/app/xxxx-1/lib/arm, /data/app/xxxx-1/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]at android.app.LoadedApk.makeApplication(LoadedApk.java:587) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4932) at android.app.ActivityThread.-wrap1(ActivityThread.java)` **Issue that I am getting** – Peter Feb 16 '18 at 07:10
  • @Peter that looks like a different issue. But does that activity with that package name exist and is it in your Manifest? – just_user Feb 16 '18 at 08:34
  • @just_user yes **Mainly I am creating the apk by using splits** – Peter Feb 16 '18 at 09:16
  • 7
    In Android Oreo you need: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && (!getPackageManager().canRequestPackageInstalls()) ) { startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", getPackageName()))), 1234); } – jpussacq Mar 06 '18 at 13:19
  • For Oreo I need to use setDataAndType too instead of setData, else it throws the ActivityNotFoundException @CrisNevill mentioned – allofmex Jun 07 '18 at 09:23
  • 4
    Make a note `` is required for Android N(Nougot) – Akash Amin Feb 28 '19 at 07:40
  • @AkashAmin Right you are, from Nougat 7.1 more specifically. I have updated the post with it. – just_user Feb 28 '19 at 07:58
  • Where it is the `activity` defines ? – TheCoderGuy Apr 27 '19 at 17:35
  • @Spritzig if you are calling the code above from an activity already you don't need **activity.** in front of startActivity(). Otherwise just pass the activity you are calling this from! It's not important where it's declared and its very basic android knowledge. – just_user Apr 29 '19 at 07:21
  • i'm still getting `ActivityNotFoundException` on android api 27 – Omid Ziyaee Aug 23 '19 at 14:09
  • @just_user I think you mean to say `== PackageManager.PERMISSION_GRANTED` instead in `canReadWriteExternal` function – Shailesh Dec 18 '20 at 13:37
  • this methods not work on samsung A12 model with android 11. package installer gives error "there was a problem parsing the package" – gturedi Mar 28 '22 at 19:19
  • Have you tried to google that? Because that issue is probably not related to the code above. @gturedi – just_user Mar 29 '22 at 15:25
  • i fixed issue saving apk to app internal storage. samsung devices giving error if you try to install apk from public external folder like "Download". @just_user – gturedi Mar 30 '22 at 16:07
  • also this way i get rid of requesting write file permission – gturedi Mar 30 '22 at 16:07
  • To be clear, some say `REQUEST_INSTALL_PACKAGES` requires since Android 7 Nougat(API 24). It's not true. the permission requires Android 8 Oreo(API 26) or above. – CodingBruceLee May 19 '22 at 06:53
18

I had this problem when calling start activity.after pausing my current activity, it suddenly came back and called onResume. like nothing happened. my problem was with this permission in manifest:

 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

almost no one mentioned that. so remember this. in sdk >= 24 you need to use provider because it needs an intent starting with file:/// below sdk 24 you should give uri starting with content:/// so that's why we need file provider for sdk 24 and above. I don't think I need to write any codes for this as @just_user has written correct answer. https://stacklearn.ir

Hossein Karami
  • 776
  • 10
  • 11
6

This could be the problem, you have

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 

in your example it should be

install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

as install is the name of the intent.

  • Did anything worked out for you? I am not able to share my file uri with email application. – Sunita Sep 30 '16 at 14:56
  • 2
    I've been switching over from using file:/// to content:// and was having trouble opening the files from the file provider for a while now. This fixed my issue. – dues71 Nov 07 '16 at 15:49
2

You should note that for API < 24 you need to use:

        setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")

instead of setting data and type separately:

data = Uri.fromFile(apkFile)
    type = "application/vnd.android.package-archive"

otherwise, you will get ActivityNotFoundException

Rez
  • 4,501
  • 1
  • 30
  • 27
0

For all of those who face the issue "There was a problem parsing this package.".

Set manualy the premissions.

ContextCompat.checkSelfPermission(getApplicationContext(), READ_EXTERNAL_STORAGE);

See this exemple : Download And Install App Programmatically

Yaegaki2
  • 9
  • 4