软件包可见性

软件包可见性简介

如果应用以 Android 11(API 级别 30)或更高版本为目标平台,并查询与设备上已安装的其他应用相关的信息,则系统在默认情况下会过滤此信息。此过滤行为意味着您的应用无法检测设备上安装的所有应用,这有助于最大限度地减少您的应用可以访问但在执行其用例时不需要的潜在敏感信息。

此外,过滤后的软件包可见性可帮助 Google Play 等应用商店评估应用为用户提供的隐私权和安全性。例如,Google Play 会将已安装应用的列表视为个人和敏感用户数据

有限的应用可见性会影响提供其他应用相关信息的方法的返回结果,例如 queryIntentActivities()getPackageInfo() 和 getInstalledApplications()。有限的可见性还会影响与其他应用的显式交互,例如启动另一个应用的服务。

部分软件包是自动可见的。您的应用始终可以在查询其他已安装的应用时检测这些软件包。如需查看其他软件包,请使用 <queries> 元素声明您的应用需要提高软件包可见性用例页面提供了有关如何选择性地扩展软件包可见性的示例。其中介绍的工作流可让您在保护用户隐私的同时执行常见的应用交互场景。

在极少数情况下,如果遇到 <queries> 元素无法提供适当的软件包可见性,您还可以使用 QUERY_ALL_PACKAGES 权限。如果您在 Google Play 上发布应用,那么应用使用此权限需要获得批准

自动可见的软件包

系统会自动让部分应用可见,以便您的应用可与其交互,而无需声明 <queries> 元素。此行为有助于支持基本功能和常见用例。

注意:您可以使用隐式显式 intent 来启动另一应用的 activity,无论该应用是否对您的应用可见。此外,如果您的应用以 Android 10(API 级别 29)或更低版本为目标平台,那么全部应用均会自动对您的应用可见。

自动可见的应用类型

尤其要注意,即使您的应用以 Android 11(API 级别 30)或更高版本为目标平台,以下类型的应用也始终对您的应用可见:

  • 您自己的应用。
  • 实现 Android 核心功能的某些系统软件包,例如媒体提供程序。
  • 安装了您应用的应用。
  • 使用 startActivityForResult() 方法在您的应用中启动 activity 的任何应用,正如如何获取 activity 的结果这一指南中所述。
  • 启动或绑定到您应用中的某项服务的任何应用。
  • 访问您应用中的 content provider 的任何应用。
  • 具有 content provider 的任何应用,其中您的应用已被授予 URI 权限来访问该 content provider。
  • 接收您应用的输入的任何应用。这种情况仅适用于您的应用作为输入法应用提供输入。
自动可见的系统软件包

实现 Android 核心功能的某些系统软件包会自动对您的应用可见,即使您的应用以 Android 11 或更高版本为目标平台也是如此。这组特定的软件包取决于运行您应用的设备。

如需查看特定设备的完整软件包列表,请在开发机器上的终端中运行以下命令:

adb shell dumpsys package queries

在命令输出中,找到 forceQueryable 部分。本部分包含设备上自动对您的应用可见的软件包列表。

Queries:
  system apps queryable: false
  forceQueryable:
    [com.android.car]
    [com.android.providers.telephony,com.android.mms.service,com.android.phone]
    com.android.bluetooth
    com.android.nfc
    com.android.se
    com.android.shell
    [com.android.providers.contacts,com.android.providers.userdictionary,com.android.providers.blockednumber]
    com.android.externalstorage
    com.android.sharedstoragebackup
    com.android.car.media.localmediaplayer
    com.android.car.media
    com.android.proxyhandler
    com.android.car.bugreport
    com.android.vpndialogs
    com.android.managedprovisioning
    com.android.providers.calendar
    com.android.documentsui
    com.android.statementservice
    com.android.car.activityresolver
  queries via package name:
  queries via intent:
    [com.android.car]:
      com.android.music
    com.android.car.messenger:
      com.android.music
    com.android.car.media.localmediaplayer:
      com.android.music
自动可见的软件包添加过程

不同版本android代码有一定差异但逻辑类似,以android13为例:

// frameworks/base/services/core/java/com/android/server/pm/AppsFilterImpl.java
    /**
     * A set of App IDs that are always queryable by any package, regardless of their manifest
     * content.
     */
    @NonNull
    @Watched
    protected WatchedArraySet<Integer> mForceQueryable;

    /**
     * The set of package names provided by the device that should be force queryable regardless of
     * their manifest contents.
     */
    @NonNull
    protected String[] mForceQueryableByDevicePackageNames;
    @NonNull
    /** True if all system apps should be made queryable by default. */
    protected boolean mSystemAppsQueryable;


@Nullable
private ArraySet<String> addPackageInternal(PackageStateInternal newPkgSetting,
        ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
    if (Objects.equals("android", newPkgSetting.getPackageName())) {
        // let's set aside the framework signatures
        //保存"android"包的签名,即系统签名
        mSystemSigningDetails = newPkgSetting.getSigningDetails();
        // and since we add overlays before we add the framework, let's revisit already added
        // packages for signature matches
        for (PackageStateInternal setting : existingSettings.values()) {
            //如果包在system相关分区并且签名是系统签名就添加到可见包列表mForceQueryable
            if (isSystemSigned(mSystemSigningDetails, setting)) {
                synchronized (mForceQueryableLock) {
                    mForceQueryable.add(setting.getAppId());
                }
            }
        }
    }

    final AndroidPackage newPkg = newPkgSetting.getPkg();
    if (newPkg == null) {
        return null;
    }

    // ...

    final boolean newIsForceQueryable;
    synchronized (mForceQueryableLock) {
        //包是system相关分区前提下,如果满足mSystemAppsQueryable或newPkg.isForceQueryable()
        //或包名在mForceQueryableByDevicePackageNames中,也被认为包默认可见。
        //mSystemAppsQueryable是config_forceSystemPackagesQueryable配置值,默认false
        //newPkg.isForceQueryable()是应用Manifest中application配置的android:forceQueryable
        //mForceQueryableByDevicePackageNames是config_forceQueryablePackages配置值,
        //默认只配置了"com.android.settings"和"com.android.providers.settings"
        newIsForceQueryable = mForceQueryable.contains(newPkgSetting.getAppId())
                        /* shared user that is already force queryable */
                        || newPkgSetting.isForceQueryableOverride() /* adb override */
                        || (newPkgSetting.isSystem() && (mSystemAppsQueryable
                        || newPkg.isForceQueryable()
                        || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
                        newPkg.getPackageName())));
        if (newIsForceQueryable
                || (mSystemSigningDetails != null
                && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
            mForceQueryable.add(newPkgSetting.getAppId());
        }
    }


    // ...

    return changedPackages;
}

    private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
            PackageStateInternal pkgSetting) {
        return pkgSetting.isSystem()
                && pkgSetting.getSigningDetails().signaturesMatchExactly(sysSigningDetails);
    }

声明软件包可见性需求

在创建应用时,请务必考虑您的应用需要与之交互的设备中的其他应用。如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,在默认情况下,系统会自动让部分应用对您的应用可见,但会过滤掉其他应用。本指南将介绍如何让上述其他应用对您的应用可见。

如果您的应用以 Android 11 或更高版本为目标平台,并且需要与并非自动可见的应用交互,请在您应用的清单文件中添加 <queries> 元素。在 <queries> 元素中,按软件包名称按 intent 签名按提供程序授权指定其他应用,如以下部分所述。

特定软件包名称

如果您知道要查询或与之交互的特定应用(例如,与您的应用集成的应用或您使用其服务的应用),请将其软件包名称添加到 <queries> 元素内的一组 <package> 元素中:

<manifest package="com.example.game">
    <queries>
        <package android:name="com.example.store" />
        <package android:name="com.example.services" />
    </queries>
    ...
</manifest>

注意: 如果您在应用的清单中声明了 <package> 元素,则与该软件包名称关联的应用会出现在对 PackageManager 执行的任何与该应用的组件匹配的查询的结果中。

与 intent 过滤器签名匹配的软件包

您的应用可能需要查询一组具有特定用途的应用或与之交互,但您可能不知道要添加的具体软件包名称。在这种情况下,您可以在 <queries> 元素中列出 intent 过滤器签名。然后,您的应用就可以发现具有匹配的 <intent-filter> 元素的应用。

以下代码示例展示了一个 <intent> 元素,该元素允许应用查看支持 JPEG 图片共享的其他已安装应用:

<manifest package="com.example.game">
    <queries>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="image/jpeg" />
        </intent>
    </queries>
    ...
</manifest>

<intent> 元素有一些限制:

  • 您必须只添加一个 <action> 元素。
  • 您不能在 <data> 元素中使用 pathpathPrefixpathPattern 或 port 属性。系统的行为就像您将每个属性的值都设为通用通配符 (*) 一样。
  • 您不能使用 <data> 元素的 mimeGroup 属性。
  • 在单个 <intent> 元素的 <data> 元素中,您可以使用以下每个属性最多一次:

    • mimeType
    • scheme
    • host

    您可以在多个 <data> 元素之间分配这些属性,也可以在单个 <data> 元素中使用这些属性。

<intent> 元素支持通用通配符 (*) 作为一些属性的值:

  • <action> 元素的 name 属性。
  • <data> 元素的 mimeType 属性的子类型 (image/*)。
  • <data> 元素的 mimeType 属性的类型和子类型 (*/*)。
  • <data> 元素的 scheme 属性。
  • <data> 元素的 host 属性。

除非前面列表中另有说明,否则系统不支持混合使用文本和通配符,如 prefix*

使用特定授权的软件包

如果您需要查询 content provider 但不知道具体的软件包名称,您可以在 <provider> 元素中声明该提供程序授权,如以下代码段所示:

<manifest package="com.example.suite.enterprise">
    <queries>
        <provider android:authorities="com.example.settings.files" />
    </queries>
    ...
</manifest>

您可以在单个 <queries> 元素中声明多项提供程序授权。在 <queries> 元素中,您可以声明一个或多个 <provider> 元素。<provider> 元素可以包含一个提供程序授权或以英文分号分隔的提供程序授权列表。

所有应用(不推荐)

在极少数情况下,您的应用可能需要查询设备上的所有已安装应用或与之交互,不管这些应用包含哪些组件。为了允许您的应用看到其他所有已安装应用,系统会提供 QUERY_ALL_PACKAGES 权限。

下面列举了一些适合添加 QUERY_ALL_PACKAGES 权限的用例:

  • 无障碍应用
  • 浏览器
  • 设备管理应用
  • 安全应用
  • 防病毒应用

不过,通常可以通过以下方式实现您应用的用例:与一组自动可见的应用交互,并在您的清单文件中声明您的应用需要访问的其他应用。为了尊重用户隐私,您的应用应请求应用正常工作所需的最小软件包可见性。

这项来自 Google Play 的政策更新为需要 QUERY_ALL_PACKAGES 权限的应用提供了相关准则。

使用广泛的软件包(应用)可见性 (QUERY_ALL_PACKAGES) 权限

Google Play 会限制应用使用高风险或敏感权限,其中包括 QUERY_ALL_PACKAGES 权限,该权限可让应用查询指定设备上已安装应用的目录。Play 将从用户设备上查询到的已安装应用的目录视为私人敏感信息,仅当您的应用为实现面向用户的核心功能或用途而必须全面了解用户设备上已安装的应用时,Play 才会允许应用使用该权限。

如果您的应用不符合下方的使用限制要求,您必须从应用的清单中移除此权限才能符合 Play 政策。下文还围绕符合政策的替代实现方式详细介绍了相关建议。

如果您的应用对 QUERY_ALL_PACKAGES 权限的使用在使用限制范围内,那么您必须使用 Play 管理中心内的权限声明表单声明此权限以及所有其他高风险权限

对于不符合政策要求或未提交权限声明表单的应用,我们可能会将其从 Google Play 下架。

重要提示:如果您更改应用对这些受限权限的使用方式,则必须修订您的声明,提供准确的最新信息。如果未如实声明或未声明这些权限,我们可能会暂停您的应用并/或终止您的开发者账号。

在什么情况下应该请求 QUERY_ALL_PACKAGES 权限?

仅当您的应用在搭载 Android 11 或更高版本的设备上以 Android API 级别 30 或更高版本为目标运行环境时,该应用才能使用 QUERY_ALL_PACKAGES 权限。

要想使用此权限,您的应用对此权限的使用必须在下方列出的允许使用情形范畴内,并且应用的核心用途之一是搜索设备上的所有应用。您必须能够给出充分理由,说明为何您的应用如果以侵扰度较低的方式查询其他应用的安装情况,则不足以实现符合政策规定的面向用户的核心功能。

核心功能即应用的主要用途。如果不具备搜索设备上所有应用这一核心功能,应用就会“损坏”或无法使用。对于核心功能以及构成核心功能的所有核心特性,您必须在应用说明中醒目地载明并宣传。

QUERY_ALL_PACKAGES 权限的允许使用情形

允许的使用情形涉及满足以下条件的应用:为了解相关情况或进行互操作(可能符合该权限的使用条件),必须发现设备上任何及所有已安装的应用。允许的使用情形包括:设备搜索、杀毒应用、文件管理器和浏览器。

获准使用此权限的应用必须遵守用户数据政策(包括关于提供醒目披露声明和征求用户同意的要求),并且不得将其使用情形扩大到未声明或无效的用途。

例外情况

对于下述不符合上文指定的允许使用情形的应用,Google Play 可能会暂时按例外情况来处理。

  • 符合以下条件的现金赌博应用:应用的核心用途是进行现金赌博,并且应用需要使用广泛的软件包可见性权限,才能符合适用的地理围栏法规所规定的技术标准。
  • 如果应用的某个可验证的核心用途是协助处理金融交易,且相关交易涉及受金融监管的工具(例如专用银行服务、专用数字钱包),我们可能会允许该应用仅出于安全方面的目的而广泛地查询已安装的应用。

根据我们的“个人贷款”政策,以任何个人贷款/信贷为目的或出于协助获取个人贷款的原因而使用这项权限的行为均不属于此类例外情况的范畴。

我们鼓励开发者积极采用不依赖 QUERY_ALL_PACKAGES 权限的替代安全保护或欺诈防范解决方案。

参考:

Android 上的软件包可见性过滤  |  Android Developers

  • 40
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值