You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android应用自动更新触发SecurityException权限拒绝问题

应用自动更新权限错误修复方案

问题背景

已成功下载APK文件,手动点击可正常完成应用更新,说明APK未损坏且可正常安装。但实现同一应用内自动更新(无需Google Play Store,类似Google应用内更新逻辑)时,触发权限错误,日志如下:

FATAL EXCEPTION: AsyncTask #1
Process: com.google.android.packageinstaller, PID: 15764
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:325)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.SecurityException: Permission Denial: opening provider androidx.core.content.FileProvider from ProcessRecord{f4c3105 15764:com.google.android.packageinstaller/u0a18} (pid=15764, uid=10018) that is not exported from uid 10099
at android.os.Parcel.readException(Parcel.java:1684)
at android.os.Parcel.readException(Parcel.java:1637)
at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:4199)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:5508)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2239)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1520)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1133)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:986)
at android.content.ContentResolver.openInputStream(ContentResolver.java:706)
at com.android.packageinstaller.PackageInstallerActivity$StagingAsyncTask.doInBackground(PackageInstallerActivity.java:792)
at com.android.packageinstaller.PackageInstallerActivity$StagingAsyncTask.doInBackground(PackageInstallerActivity.java:783)
at android.os.AsyncTask$2.call(AsyncTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
at java.lang.Thread.run(Thread.java:761) 

当前实现代码

自动安装触发代码

private void openDownloadedAttachment(final Context context, final long downloadId, Uri uri) {
    DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
    DownloadManager.Query query = new DownloadManager.Query();
    query.setFilterById(downloadId);
    Cursor cursor = downloadManager.query(query);
    if (cursor.moveToFirst()) {
        @SuppressLint("Range")
        int downloadStatus = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
        @SuppressLint("Range")
        String downloadLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
        @SuppressLint("Range")
        String downloadMimeType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
        if ((downloadStatus == DownloadManager.STATUS_SUCCESSFUL) && downloadLocalUri != null) {
            try {
                downloadLocalUri = "/storage/emulated/0/AATv2.apk";
                openDownloadedAttachment(context, Uri.parse(downloadLocalUri), downloadMimeType);
                Intent promptInstall = new Intent(Intent.ACTION_VIEW);
                promptInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                promptInstall.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                promptInstall.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION );
                File file = new File(downloadLocalUri);
                uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName()+"provider",
                        file);
                promptInstall.setDataAndType(uri,
                        "application/vnd.android.package-archive");
                promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(promptInstall);
            }
            catch (ActivityNotFoundException e) {
                e.printStackTrace();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    cursor.close();
}

AndroidManifest配置

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.INSTALL_PACKAGES"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.DELETE_PACKAGES"
    tools:ignore="ProtectedPermissions" />

<application>
        .....
    <provider
        android:name="androidx.core.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>
</application>

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

问题原因

  1. Intent Flag被覆盖:先调用addFlags()添加权限相关Flag,之后调用setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)会清空之前所有Flag,导致PackageInstaller无法获取APK文件的访问权限。
  2. 无限递归调用:代码中手动指定downloadLocalUri后,再次调用openDownloadedAttachment触发无限递归,干扰正常逻辑。
  3. 无用权限声明:INSTALL_PACKAGES和DELETE_PACKAGES是系统级权限,普通应用无法获取,声明后无实际作用。

修复方案

1. 修正Intent Flag设置

使用addFlags()一次性添加所有需要的Flag,避免权限Flag被覆盖:

promptInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
        | Intent.FLAG_GRANT_READ_URI_PERMISSION
        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);

2. 删除递归调用代码

移除手动指定路径后的openDownloadedAttachment调用,避免无限递归。

3. 移除无用权限

删除INSTALL_PACKAGES和DELETE_PACKAGES权限声明,普通应用无法使用这些权限。

修正后的完整代码

private void openDownloadedAttachment(final Context context, final long downloadId, Uri uri) {
    DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
    DownloadManager.Query query = new DownloadManager.Query();
    query.setFilterById(downloadId);
    Cursor cursor = downloadManager.query(query);
    if (cursor.moveToFirst()) {
        @SuppressLint("Range")
        int downloadStatus = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
        @SuppressLint("Range")
        String downloadLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
        @SuppressLint("Range")
        String downloadMimeType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
        if ((downloadStatus == DownloadManager.STATUS_SUCCESSFUL) && downloadLocalUri != null) {
            try {
                Intent promptInstall = new Intent(Intent.ACTION_VIEW);
                // 一次性添加所有必要Flag
                promptInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_GRANT_READ_URI_PERMISSION
                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
                File file = new File(downloadLocalUri);
                uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider",
                        file);
                promptInstall.setDataAndType(uri, "application/vnd.android.package-archive");
                startActivity(promptInstall);
            } catch (ActivityNotFoundException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    cursor.close();
}

修正后的AndroidManifest权限部分

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application>
        .....
    <provider
        android:name="androidx.core.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>
</application>

内容的提问来源于stack exchange,提问作者Ibanez1408

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:50:28