关于Android自定义权限:Android自定义权限-棉花糖

Android custom permissions - Marshmallow

背景

从历史上看,Android自定义权限是一团糟,并且依赖于安装顺序,众所周知这会暴露漏洞。

在API 21之前,有一个令人不安的变通办法,即在清单中声明另一个应用程序的自定义许可,并授予该许可...。但是,自API 21起,只有一个应用程序可以声明自定义许可,而另一个应用程序可以声明安装同样的权限,将被阻止。

替代方法是重新安装需要许可的应用程序,因此系统会检测到它们,但这不是良好的用户体验。或者在运行时检查调用应用程序的权限,但这并非没有理论上的缺陷。

问题

从Android Marshmallow(6.0-API 23)开始,应用程序需要向用户请求权限,才能使用其自己的自定义权限。声明的自定义权限不会自动授予。

鉴于现在只有一个应用程序可以声明它,所以这似乎很奇怪。

复制

在清单中声明自定义权限和一个BroadcastReceiver。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<permission
    android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"
    android:description="@string/control_description"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/control_label"
    android:protectionLevel="normal or dangerous"/>

<uses-permission
    android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"/>

// etc

<receiver
    android:name="com.example.app.MyBroadcastReceiver"
    android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">
    <intent-filter android:priority="999">
       
    </intent-filter>
</receiver>

在第三方应用程序中,声明它使用清单中的自定义权限(并通过对话框或设置接受它),然后调用:

1
2
3
4
5
6
7
8
9
10
    final Intent intent = new Intent("com.example.app.REQUEST_RECEIVER");

    context.sendOrderedBroadcast(intent,"com.example.app.permission.CONTROL_EXAMPLE_APP", new BroadcastReceiver() {
        @Override
        public void onReceive(final Context context, final Intent intent) {

        // getResultCode();

        }
    }, null, Activity.RESULT_CANCELED, null, null);

结果将返回CANCELED并且日志将显示:

system_process W/BroadcastQueue: Permission Denial: receiving Intent {
act=com.example.app.REQUEST_RECEIVER flg=0x10 (has extras) } to
com.example.app/.MyBroadcastReceiver requires
com.example.app.permission.CONTROL_EXAMPLE_APP due to sender
com.example.thirdparty

如果我使用标准的ActivityCompat.requestPermissions()对话框来允许用户接受许可,则接收器就可以正常工作。

这是预期的行为吗?还是我以某种方式忽略了某些东西?

提出一个对话说似乎很荒谬

The application Example App wants permission to use Example App

它确实可能与用户有关,向他们提供了这种荒谬的要求。

我当然可以将权限描述和名称更改为它们可以接受的名称,例如"与其他已安装的应用程序通信",但是在叹息并采取这种方法之前,我想我会问这个问题。

注意

有序广播的示例就是要复制该问题。我的应用程序确实使用了内容提供程序和绑定服务的其他实现。这不是我需要的替代实现,它是对问题的确认。

感谢您阅读本文。

编辑:为了澄清,对于其他实现,例如,声明对服务的许可(这将是最简单的复制),将自动授予声明的自定义权限。


据我了解,您尝试做下一件事(至少,这是我能够重现您的问题的方式):

  • 您在第一个(称为F)应用程序中声明新的自定义权限

    1
    2
    3
    4
    5
    6
    <permission
        android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"
        android:description="@string/control_description"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/control_label"
        android:protectionLevel="normal or dangerous"/>
  • 您定义F应用程序使用com.example.app.permission.CONTROL_EXAMPLE_APP权限。正如指导原则所说,这是正确的。

    1
    2
    <uses-permission
        android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"/>
  • 您在F应用程序中声明自定义广播接收器。要与该广播进行通信,您的应用程序(无论是哪个,F或其他应用程序)都必须获得您的自定义权限

    1
    2
    3
    4
    5
    6
    7
    <receiver
        android:name="com.example.app.MyBroadcastReceiver"
        android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">
        <intent-filter android:priority="999">
           
        </intent-filter>
    </receiver>
  • 您定义您的第二个(称为S)应用程序使用com.example.app.permission.CONTROL_EXAMPLE_APP权限。因为您要允许S app向F app接收方发送广播消息。

    1
    2
    <uses-permission
        android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"/>
  • 最后,您尝试使用此代码从S应用发送广播消息。

    1
    2
    3
    4
    5
    6
    7
    final Intent intent = new Intent("com.example.app.REQUEST_RECEIVER");
    context.sendOrderedBroadcast(intent,"com.example.app.permission.CONTROL_EXAMPLE_APP", new BroadcastReceiver() {
            @Override
            public void onReceive(final Context context, final Intent intent) {
                // getResultCode();
            }
        }, null, Activity.RESULT_CANCELED, null, null);

    而且,这一点很重要,您已授予S应用程序权限,但未授予F应用程序权限。

    结果,您在F app中声明的广播接收器未收到任何信息。

  • 在授予F应用程序权限后(请注意,现在S和F已授予自定义权限),一切正常。在F app中声明的广播接收器收到来自S app的消息。

  • 我猜这是正确的行为,因为此文档告诉我们:

    Note that, in this example, the DEBIT_ACCT permission is not only
    declared with the element, its use is also requested with
    the element. You must request its use in order for
    other components of the application to launch the protected activity,
    even though the protection is imposed by the application itself.

    声明许可的应用也必须请求相同的许可才能与自己通信。

    因此,Android API 23应该首先获得使用您的权限表单用户的访问权限。而且我们必须获得2个授予的权限,第一个是从F应用程序(因为指导说那样),第二个是从S应用程序(因为我们只需要获得访问权限)。

    但是我没听懂你的下一点:

    It would seem ridiculous to raise a dialog saying

    The application Example App wants permission to use Example App

    我的原生Android API 23向我显示了以下内容:

    The application Example App wants


    我认为声明的自定义权限不会自动授予该应用程序不是完全正确的。对于自定义权限具有保护级别"正常"或"签名"的情况,则在安装时授予该权限。否则,如果保护级别为"危险",则它是运行时权限,并且其作用与其他危险权限一样:您将需要提示用户向应用程序授予权限。


    我认为您的示例中的问题是您明确要求两个应用程序都被授予自定义权限。

    这部分要求com.example.thirdparty应用具有以下权限:

    1
    2
    3
    <receiver
        android:name="com.example.app.MyBroadcastReceiver"
        android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">

    这部分要求com.example.app应用程序也具有以下权限:

    1
    context.sendOrderedBroadcast(intent,"com.example.app.permission.CONTROL_EXAMPLE_APP", ...

    您提到使用服务时没有此问题。我不知道您使用服务的方式如何,但是如果您只是这样声明:

    1
    2
    3
    <service
        android:name="com.example.app.MyService"
        android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">

    然后像这样绑定它:

    1
    context.bindService(serviceIntent, mServiceConnection, ...

    那么只要com.example.thirdparty拥有了授予的权限就足够了,而com.example.app则不需要拥有它。

    换句话说,我认为此行为是设计使然,您看到的"广播"和"服务"行为之间的区别在于,在"广播"情况下,您明确要求com.example.app具有自定义权限,而在"服务"情况下,别。

    希望我不要误会你的问题。如果我这样做了,请告诉我,我将删除此回复。


    尽管用户看到在同一应用程序中声明的应用程序的权限请求可能不明确,但这是自棉花糖以来设计的android运行方式。我认为,从android的角度来看,行为是预期的和正确的。


    首先在清单文件中添加权限

    1
    2
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />