Android 11 适配 软件包可见性

本文详细解读了Android 11中软件包可见性的变化,介绍了如何配置以确保应用在API级别30及以上设备上遵循最小权限原则,包括检查浏览器、文件处理、分享功能、语音服务等关键操作的适配策略。

Android 11 软件包可见性 适配

一、软件包可见性说明

如果应用以 Android 11(API 级别 30)或更高版本为目标平台,在默认情况下,系统会自动让部分应用对您的应用可见,但会隐藏其他应用。
通过让部分应用在默认情况下不可见,系统可以了解应向应用显示哪些其他应用,这样有助于鼓励最小权限原则。

注意:

  1. 如果您的应用以 Android 10(API 级别 29)或更低版本为目标平台,那么全部应用均会自动对您的应用可见。
  2. 即使您的应用以 Android 11(API 级别 30)或更高版本为目标平台,特定场景不会受影响。

1. 不受影响的特定场景

  • 实现 Android 核心功能的某些系统软件包,如媒体提供程序。
  • 使用 startActivityForResult() 方法启动其他应用的某个页面。
  • 通过 Content Provider 访问的应用。
  • 具有 Content Provider 的任何应用,且应用已被授予 URI 权限来访问该 Content Provider。
  • 可以使用隐式或显式 intent 来启动另一应用的 activity,无论这个应用是否对您的应用可见。

2. 受影响的方法列表


    // 检索可以为给定意图执行的所有 activity
    PackageManager.queryIntentActivities(intent, flag)
    
	// 检索可以匹配给定意图的所有服务
    PackageManager.queryIntentServices(intent, flag)
        
    // 检索可以处理给定意图广播的所有接收器
    PackageManager.queryBroadcastReceivers(intent, flag)
        
    // 查询内容提供者
    PackageManager.queryContentProviders(processName, uid, flag)

    // 获取为当前用户安装的所有软件包的列表。
    PackageManager.getInstalledPackages(flag)

	// 获取已安装的应用程序
	PackageManager.getInstalledApplications(flag)
    

二、软件包可见性配置

1. 检查是否有可用浏览器,例如需要打开外部浏览器

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
        </intent>
    </queries>
</manifest>

2. 检查设备能否打开给定文件,如需要打开 pdf、zip

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <data android:mimeType="*/*" />
        </intent>
    </queries>
</manifest>

3. 创建自定义共享表单,如分享图片、文件

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="*/*" />
        </intent>
    </queries>
</manifest>

4. 显示自定义文字选择操作,如选中一段文字,进行复制、粘贴操作

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT" />
            <data android:mimeType="text/plain" />
        </intent>
    </queries>
</manifest>

5. 连接到文字转语音引擎

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <intent>
            <action android:name="android.intent.action.TTS_SERVICE" />
        </intent>
    </queries>
</manifest>

6. 连接到语音识别服务

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <intent>
            <action android:name="android.speech.RecognitionService" />
        </intent>
    </queries>
</manifest>

7. 显示联系人的自定义数据行

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <!-- Allows the app to read the "contacts.xml" file from the other apps. -->
        <intent>
            <action android:name="android.accounts.AccountAuthenticator" />
        </intent>
        <!-- Allows the app to load an icon corresponding to the custom MIME type. -->
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <data
                android:host="com.android.contacts"
                android:mimeType="vnd.android.cursor.item/*"
                android:scheme="content" />
        </intent>
    </queries>
</manifest>

8. 连接到媒体浏览器服务

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <intent>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent>
    </queries>
</manifest>

9. 配置访问 微信,支付宝,QQ,微博

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <!--微信-->
        <package android:name="com.tencent.mm" />

        <!--支付宝-->
        <package android:name="com.eg.android.AlipayGphone" />
        <package android:name="hk.alipay.wallet" />

        <!--QQ-->
        <package android:name="com.tencent.qqlite" />
        <package android:name="com.tencent.mobileqq" />

        <!--新浪微博-->
        <package android:name="com.sina.weibo" />
    </queries>
</manifest>

10. 配置查询所有应用及与之交互(以上均可不配置,但 gp 会严格审核)

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

附 Github 源码:

AndroidManifest.xml

<think>我们正在处理一个关于Android 13系统应用调用安装程序来安装APK的问题。 根据引用[1]:系统内置的应用程序packageinstaller用于安装和卸载应用程序。因此,我们可以通过启动packageinstaller的Activity来安装APK。 在Android中,通常使用Intent来启动其他组件的活动。对于安装APK,我们需要使用ACTION_VIEW意图,并设置正确的MIME类型和文件URI。 但是,需要注意的是,从Android 7.0(Nougat)开始,禁止通过file:// URI在应用间共享文件,而应该使用FileProvider来生成content:// URI。 另外,作为系统应用,我们可能需要处理一些权限问题,因为系统应用通常具有更高的权限。 根据引用[2]和[3],静默安装(不通过用户界面)通常需要系统签名或者特定的权限(如INSTALL_PACKAGES),并且只能在系统应用中使用。但是,用户要求的是调用安装程序(即用户可见的安装界面),所以我们需要启动PackageInstaller的界面。 步骤: 1. 在AndroidManifest.xml中声明必要的权限,例如:如果应用是系统应用,可以放在/system/priv-app目录下,这样会获得一些特权。 2. 使用FileProvider来提供APK文件,因为从Android 7.0开始,必须使用content URI。 3. 创建一个Intent,设置action为Intent.ACTION_VIEW,设置data为APK的content URI,设置MIME类型为"application/vnd.android.package-archive"。 4. 设置Intent的flag为Intent.FLAG_ACTIVITY_NEW_TASK,因为从后台启动Activity。 5. 授予URI读取权限给目标应用(即安装器),通过Intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)。 但是,请注意,在Android 11(API级别30)及更高版本中,对于安装器,我们可能需要使用PackageInstaller API,但是这里我们要求的是调用安装器的界面,所以使用Intent方式仍然是可行的。 另外,引用[4]提到了一个在Android 14中出现的错误,虽然我们处理的是Android 13,但也要注意可能存在的兼容性问题。 下面是一个示例代码: 首先,在AndroidManifest.xml中定义FileProvider: ```xml <application> ... <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application> ``` 在res/xml/file_paths.xml中定义路径: ```xml <?xml version="1.0" encoding="utf-8"?> <paths> <!-- 这里根据你的APK存放位置来设置,例如在内部存储的files目录下 --> <files-path name="apk" path="apk/" /> <!-- 或者使用外部存储 --> <!-- <external-path name="external_apk" path="Download/" /> --> </paths> ``` 然后,在代码中: ```java public void installApk(Context context, File apkFile) { // 检查文件是否存在 if (apkFile == null || !apkFile.exists()) { Log.e(TAG, "APK file does not exist"); return; } Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 对于Android N及以上版本,使用FileProvider if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 获取URI Uri apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", apkFile); intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); // 授予临时读取权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { Uri apkUri = Uri.fromFile(apkFile); intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); } // 验证意图是否可以被处理 PackageManager pm = context.getPackageManager(); if (intent.resolveActivity(pm) != null) { context.startActivity(intent); } else { Log.e(TAG, "No activity found to handle the intent"); } } ``` 注意:上述代码中,FileProvider的authorities必须与AndroidManifest.xml中定义的一致。 但是,如果我们的应用是系统应用,并且安装在系统分区(如/system/priv-app),那么我们可能不需要使用FileProvider,因为系统应用可以访问一些其他应用不能访问的URI?实际上,即使系统应用,在启动其他应用(如安装器)时,也需要遵守内容URI的规则。所以,使用FileProvider是安全的。 另外,在Android 11(API 30)及以上,我们还需要在AndroidManifest.xml中添加<queries>来查询安装器。因为安装器可能不在默认可见的范围内。我们可以添加: ```xml <queries> <intent> <action android:name="android.intent.action.VIEW" /> <data android:mimeType="application/vnd.android.package-archive" /> </intent> </queries> ``` 这样,我们就能确保找到安装器。 总结:在Android 13上,系统应用调用安装程序安装APK,可以通过上述Intent方式实现。 注意:如果我们的系统应用具有更高的权限,并且希望绕过用户确认界面,则需要使用静默安装(如PackageInstaller API),但这不是用户要求的,用户要求的是调用安装程序(即用户可见的安装界面)。 因此,我们使用上述方法启动安装器的界面,由用户手动点击安装。 相关问题: 1. 如何在没有用户交互的情况下静默安装APK? 2. 在Android 13中,系统应用调用安装程序时,如何处理权限问题? 3. 如何适配不同Android版本(特别是Android 11及以上)的安装器调用?</think>在 Android 13 中,系统应用调用 Package Installer 安装 APK 的实现步骤如下: ### 实现步骤 1. **声明权限** 在 `AndroidManifest.xml` 中添加系统权限声明: ```xml <manifest ...> <!-- 声明为系统应用 --> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- 声明 FileProvider --> <application ...> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application> </manifest> ``` 2. **配置 FileProvider 路径** 在 `res/xml/file_paths.xml` 中定义 APK 存储路径: ```xml <?xml version="1.0" encoding="utf-8"?> <paths> <!-- 指向内部存储的 apk 目录 --> <files-path name="apk_dir" path="apk/" /> </paths> ``` 3. **Java/Kotlin 实现安装逻辑** ```java public void installApk(Context context, File apkFile) { Uri apkUri; // Android 7.0+ 需使用 FileProvider if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { apkUri = FileProvider.getUriForFile( context, context.getPackageName() + ".fileprovider", apkFile ); } else { apkUri = Uri.fromFile(apkFile); } Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setData(apkUri); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 授予临时读取权限 intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true); // 允许未知来源 context.startActivity(intent); } ``` ### 关键注意事项 1. **系统应用签名** 必须使用与系统相同的签名密钥签名 APK(如 AOSP 的 `platform` 密钥),否则无法声明 `INSTALL_PACKAGES` 权限[^1][^3]。 2. **APK 存储位置** 系统应用应优先使用内部存储路径(如 `data/data/your.package/files/apk/`),避免 SD 卡路径导致的权限冲突[^2]。 3. **Android 11+ 适配** 针对 Android 11(API 30+): ```xml <!-- 添加安装器可见性声明 --> <queries> <intent> <action android:name="android.intent.action.INSTALL_PACKAGE" /> </intent> </queries> ``` 4. **错误处理** 若遇到 `PackageInstaller` 崩溃(如 Android 14 的 `Bitmap` 序列化问题),需检查系统日志并参照官方补丁修复[^4]。 ### 完整调用示例 ```java // 将 APK 放入内部存储 File apkDir = new File(context.getFilesDir(), "apk"); apkDir.mkdirs(); File apkFile = new File(apkDir, "update.apk"); // 调用安装方法 installApk(context, apkFile); ``` --- ### 相关问题 1. **如何绕过用户确认界面实现静默安装?** 系统应用需使用 `PackageInstaller` API 直接安装,但需 `android.permission.INSTALL_PACKAGES` 和系统签名[^2][^3]。 2. **Android 14 安装失败时如何调试?** 检查 `PackageInstaller` 系统日志,重点关注 `Parcel` 序列化错误(如 `Bitmap` 处理)[^4]。 3. **非系统应用能否调用 Package Installer?** 普通应用可通过 `ACTION_INSTALL_PACKAGE` 调用,但需用户手动确认安装,且受未知来源安装限制[^1]。 4. **如何适配 Android 13 的存储权限变更?** 使用 `FileProvider` 替代 `file://` URI,并确保 APK 文件位于应用私有目录或共享媒体目录。 [^1]: 通过系统安装器 PackageInstaller 安装 APK 的流程说明 [^2]: 系统应用静默安装需使用系统签名和内部存储路径 [^3]: 系统应用安装权限需平台签名和 `INSTALL_PACKAGES` 权限 [^4]: Android 14 安装失败需关注 `Parcel` 序列化逻辑
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值