Android install apk with Intent.VIEW_ACTION not working with File provider

60,349

Solution 1

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

Solution 2

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

Solution 3

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.

Solution 4

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

Share:
60,349

Related videos on Youtube

Johny19
Author by

Johny19

Updated on July 08, 2022

Comments

  • Johny19
    Johny19 almost 2 years

    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) ?

    • CommonsWare
      CommonsWare over 7 years
      Use ACTION_INSTALL_PACKAGE. That worked for me as of the June edition of the N Developer Preview.
    • Johny19
      Johny19 over 7 years
      No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE ...
    • CommonsWare
      CommonsWare over 7 years
      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().
    • Raj
      Raj over 7 years
      where do you get "file" from? the last argument in getUriForFile
    • Kenneth Argo
      Kenneth Argo over 3 years
      Intent.ACTION_INSTALL_PACKAGE is deprecated, use Intent.ACTION_VIEW
    • IgorGanapolsky
      IgorGanapolsky about 2 years
      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.
  • Sunita
    Sunita over 7 years
    Did anything worked out for you? I am not able to share my file uri with email application.
  • dues71
    dues71 over 7 years
    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.
  • Raj
    Raj over 7 years
    can you post where you get toInstall?
  • just_user
    just_user over 7 years
    @emaillenin I have added it. It just a File pointing to the .apk you want to install.
  • Raj
    Raj over 7 years
    My issue was that I was calling setFlags twice which basically resets the first flag. I had to use addFlag instead.
  • andro
    andro over 7 years
    but its not opening app after installing
  • just_user
    just_user over 7 years
    @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.
  • andro
    andro over 7 years
    @just_user after installing done its not asking open . it just closing it
  • andro
    andro over 7 years
    @just_user can you look into my code stackoverflow.com/questions/41762043/…
  • Gautam
    Gautam about 7 years
    @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 ?
  • Kuri
    Kuri about 7 years
    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.
  • just_user
    just_user about 7 years
    Then you should probably look into FileProvider developer.android.com/reference/android/support/v4/content/…‌​. 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.
  • Jasongiss
    Jasongiss over 6 years
    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.
  • Chris Nevill
    Chris Nevill over 6 years
    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/C‌​heckIn20180122.APK flg=0x1 }
  • Chris Nevill
    Chris Nevill over 6 years
    OK this is bizarre - in my case I followed this post : stackoverflow.com/questions/40098073/… 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!
  • just_user
    just_user over 6 years
    @ChrisNevill that is bizarre!
  • Peter
    Peter over 6 years
    @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
    Peter over 6 years
    @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/li‌​b/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(ActivityThr‌​ead.java:4932) at android.app.ActivityThread.-wrap1(ActivityThread.java) Issue that I am getting
  • just_user
    just_user over 6 years
    @Peter that looks like a different issue. But does that activity with that package name exist and is it in your Manifest?
  • Peter
    Peter over 6 years
    @just_user yes Mainly I am creating the apk by using splits
  • jpussacq
    jpussacq about 6 years
    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(U‌​ri.parse(String.form‌​at("package:%s", getPackageName()))), 1234); }
  • allofmex
    allofmex almost 6 years
    For Oreo I need to use setDataAndType too instead of setData, else it throws the ActivityNotFoundException @CrisNevill mentioned
  • Akash Amin
    Akash Amin about 5 years
    Make a note <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> is required for Android N(Nougot)
  • just_user
    just_user about 5 years
    @AkashAmin Right you are, from Nougat 7.1 more specifically. I have updated the post with it.
  • Pierre
    Pierre about 5 years
    Just always use DataAndType then there is no problem.
  • TheCoderGuy
    TheCoderGuy about 5 years
    Where it is the activity defines ?
  • just_user
    just_user about 5 years
    @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.
  • Omid Ziyaee
    Omid Ziyaee over 4 years
    i'm still getting ActivityNotFoundException on android api 27
  • ParSa
    ParSa almost 4 years
    you saved my life , tnx <3
  • Kartikey Kumar Srivastava
    Kartikey Kumar Srivastava over 3 years
    Thanks for the help. It totally works for all devices.
  • Kenneth Argo
    Kenneth Argo over 3 years
    I believe your comment is reversed. For SDK >= 24 the URI needs to begin with 'content:///'. below SDK 24 it should begin with 'file:///'
  • Shailesh
    Shailesh over 3 years
    @just_user I think you mean to say == PackageManager.PERMISSION_GRANTED instead in canReadWriteExternal function
  • gturedi
    gturedi about 2 years
    this methods not work on samsung A12 model with android 11. package installer gives error "there was a problem parsing the package"
  • just_user
    just_user about 2 years
    Have you tried to google that? Because that issue is probably not related to the code above. @gturedi
  • gturedi
    gturedi about 2 years
    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
    gturedi about 2 years
    also this way i get rid of requesting write file permission
  • CodingBruceLee
    CodingBruceLee almost 2 years
    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.