Программно установить apk в Android 7/api24

Я пытаюсь получить мое приложение для автоматической установки apk. Это отлично работает для api <24. Но для 24 он терпит неудачу. Android реализовал дополнительную безопасность:

Для приложений, ориентированных на Android 7.0, платформа Android применяет политику StrictMode API, которая запрещает выставлять файлы://URI за пределами вашего приложения. Если намерение, содержащее URI файла, покидает ваше приложение, приложение выходит из строя с исключением FileUriExposedException.

Поэтому я попробовал это:

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

но получить фатальное исключение:

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". 

У меня есть export = true в моем манифесте.

Проблема заключается в том, что packageinstaller не может использовать контент://uri.

Есть ли способ разрешить приложению прогамматически устанавливать apk с api24?

Ответы

Ответ 1

Есть ли способ разрешить приложению прогамматически устанавливать apk с api24?

Добавьте addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) в promptInstall, чтобы предоставить доступ для чтения к контенту.

У меня есть export = true в моем манифесте.

Не на вашем FileProvider, так как это приведет к сбою вашего приложения.

Проблема заключается в том, что packageinstaller не может использовать контент://uri.

Нет, проблема в том, что вы не предоставили разрешение на установку установщика пакетов для чтения из этого Uri. Если установщик пакетов не смог использовать схему content, вы получили бы ActivityNotFoundException.

Обратите внимание, однако, что только с Android 7.0 установщик пакетов начинает поддерживать content. Раньше версии Android должны использовать file.

Ответ 2

Для Oreo, добавить разрешение в AndroidManifast (в противном случае это просто молча не удается)

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

теперь добавь к тебе манифест

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

в каталоге xml добавить...

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

затем используйте эти коды, где вы хотите.

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

Ответ 3

Для Oreo добавьте разрешение в AndroidManifast

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

Ответ 4

Добавить файл в res/xmlprovider_paths.xml

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

Добавьте этот код в AndroidManifest.xml

     <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.provider" <-- change this with your package name
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

запустите этот код для установки приложения или откройте

    public void installApk(String file) {
        Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider",new File(file));
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        context.startActivity(intent);
    }

Ответ 5

просто выполните следующие шаги:

1- добавить следующее разрешение для манифеста:

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

2- Добавить провайдера в манифест (как дочерний, как тег приложения):

 <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="tatcomputer.ir.libraryapp.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths"/>
    </provider>

3- Добавить paths.xml в каталог xml:

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

4- покажите страницу установки apk, используя следующий код (обратите внимание, что в моем случае apk находится в корне моего телефона с именем tmp.apk:

       String root = Environment.getExternalStorageDirectory().toString();

        Uri fileUri24 = FileProvider.getUriForFile(App.applicationContext, BuildConfig.APPLICATION_ID + ".provider", new File(root + "/tmp.apk"));
        Uri fileUri = Uri.fromFile(new File(root + "/tmp.apk"));

        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= 24)
            intent.setDataAndType(fileUri24, "application/vnd.android.package-archive");
        else intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        App.applicationContext.startActivity(intent);

Ответ 6

Вот решение, которое я нашел

val newFile = File(dirPath, "$fileNameWithoutExtn.apk")
                        var fileUri = Uri.fromFile(newFile)
                        //use the fileProvider to get the downloaded from sdcard
                        if (Build.VERSION.SDK_INT >= 24) {
                            fileUri = FileProvider.getUriForFile([email protected], applicationContext.packageName + ".provider", newFile)
                         val intent=Intent(Intent.ACTION_VIEW)
                            intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
                            intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
                            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                            startActivity(intent)
                        }else{
                            newFile.setReadable(true, false)
                            val intent = Intent(Intent.ACTION_VIEW)
                            intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
                            intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
                            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                            startActivity(intent)
                        }

и напишите в манифесте

<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/paths"/>

а также установить разрешение

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

и в папке xml пути будут

<paths>
    <external-path
        name="external_files"
        path="." />
</paths>

Ответ 7

Intall Apk автоматически

Intent intent1 = new Intent(Intent.ACTION_INSTALL_PACKAGE);
            intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent1.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" +filename)), "application/vnd.android.package-archive");
            intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);