Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically install an apk in Android 7 / api24

I am trying to get my app to automatically install an apk. This works fine for api<24. But for 24, it is failing. Android has implemented extra security:

For apps targeting Android 7.0, the Android framework enforces the StrictMode API policy that prohibits exposing file:// URIs outside your app. If an intent containing a file URI leaves your app, the app fails with a FileUriExposedException exception.

So I tried this:

    Uri myuri;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){
        myuri = Uri.parse("file://"+outapk);
    } else {
        File o = new File(outapk);
        myuri = FileProvider.getUriForFile(con, con.getApplicationContext().getPackageName() + ".provider", o);
    }
    Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(myuri,"application/vnd.android.package-archive");
    con.startActivity(promptInstall);

but get a fatal exception:

com.android.packageinstaller "Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{b42ee8a 6826:com.android.packageinstaller/u0a15} (pid=6826, uid=10015) that is not exported from uid 10066". 

I have export=true in my manifest.

The problem seems to be that packageinstaller cannot use a content:// uri.

Are there any ways to allow an app to progammatically install an apk with api24?

like image 720
elsie Avatar asked Jan 04 '17 23:01

elsie


People also ask

Can I install an APK on Android Studio emulator?

“To install an APK file on the emulated device, drag an APK file onto the emulator screen. An APK Installer dialog appears. When the installation completes, you can view the app in your apps list. To add a file to the emulated device, drag the file onto the emulator screen.


2 Answers

Are there any ways to allow an app to progammatically install an apk with api24?

Add addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) to your promptInstall setup, to grant read access to the content.

I have export=true in my manifest.

Not on your FileProvider, as that would cause your app to crash.

The problem seems to be that packageinstaller cannot use a content:// uri.

No, the problem is that you did not grant permission to the package installer to read from that Uri. Had the package installer been unable to use a content scheme, you would have gotten an ActivityNotFoundException.

Note, though, that it is only with Android 7.0 that the package installer starts supporting content. Earlier versions of Android have to use file.

like image 141
CommonsWare Avatar answered Sep 25 '22 10:09

CommonsWare


For Oreo, Add permission in AndroidManifast (Otherwise it just silently fails)

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

now add to you'r Manifest

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

in xml directory add...

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

then use these codes where you want.

File directory = Environment.getExternalStoragePublicDirectory("myapp_folder"); 

 File file = new File(directory, "myapp.apk"); // assume refers to "sdcard/myapp_folder/myapp.apk"


    Uri fileUri = Uri.fromFile(file); //for Build.VERSION.SDK_INT <= 24

    if (Build.VERSION.SDK_INT >= 24) {

        fileUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
    }
    Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
    intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
    intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
    intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //dont forget add this line
    context.startActivity(intent);
}
like image 42
smartmob Avatar answered Sep 24 '22 10:09

smartmob