自定义权限 permission
<permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="normal"
android:label="@string/permlab_install_shortcut"
android:description="@string/permdesc_install_shortcut" />
声明的含义如下;
android:label:权限名字,显示给用户的,值可是一个 string 数据,例如这里的“自定义权限”。
android:description:比 label 更长的对权限的描述。值是通过 resource 文件中获取的,不能直接写 string 值,例如这里的”@string/test”。
android:name:权限名字,如果其他 app 引用该权限需要填写这个名字。
android:protectionLevel:权限级别,分为 4 个级别:
○normal:低风险权限,在安装的时候,系统会自动授予权限给 application。
○dangerous:高风险权限,系统不会自动授予权限给 app,在用到的时候,会给用户提示。
○signature:签名权限,在其他 app 引用声明的权限的时候,需要保证两个 app 的签名一致。这样系统就会自动授予权限给第三方 app,而不提示给用户。
○signatureOrSystem:这个权限是引用该权限的 app 需要有和系统同样的签名才能授予的权限,一般不推荐使用。
声明和强制实施权限
要强制执行自己的权限,首先必须使用一个或多个<permission>标签,在AndroidManifest.xml文件中来声明它们。
例如,应用程序想要控制谁能够启动它的一个Activity,就能够用下面的方法来为这个操作声明一个权限:
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.me.app.myapp">
<permissionandroid:name="com.me.app.myapp.permission.DEADLY_ACTIVITY"
android:label="@string/permlab_deadlyActivity"
android:description="@string/permdesc_deadlyActivity"
android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="dangerous"/>
...
</manifest>
<protectionLevel>属性是必须的,它告诉系统怎样把应用程序需要的权限通知给用户,或者是允许谁拥有这个权限。
<permissionGroup>属性是可选的,并且只用于帮助系统把相关权限显示给用户。通常用标准的系统组来设置这个属性,当然也可以使用自己定义的组(但这很少见)。我们推荐使用既存的分组,这样会简化给用户的显示的权限UI。
要注意的时,权限所支持的label和description属性。它们是能够显示给用户的字符串资源,android:label属性用于权限列表的显示,android:description属性用于单一权限的详细介绍。label属性值应该是简短的,用几个关键的单词来描述被权限保护的功能。description属性应该是权限的详细描述,惯例是使用两句话,第一句话来描述权限的功能,第二句话用来警告用户,如果应用程序获得了这个权限会带来的不利影响。
下面是一个申请CALL_PHONE权限的label和description属性设置的例子:
<stringname="permlab_callPhone">directly call phone numbers</string>
<stringname="permdesc_callPhone">Allows the application to call
phone numbers without your intervention. Malicious applications may
cause unexpected calls on your phone bill. Note that this does not
allow the application to call emergency numbers.</string>
用系统的Settings应用程序和shell命令:adb shell pm list permissions,能够查看系统中当前定义的权限。Settings应用的使用方法是:Settings->Applications,选择一个应用程序,向下滚动,可以看到这个应用程序所使用的权限。对于开发者,带有“-s”选项的adb命令可以用与用户查看格式相类似的格式来显示权限:
$ adb shell pm list permissions -s
AllPermissions:
Network communication: view Wi-Fi state, create Bluetooth connections, full
Internet access, view network state
Your location: access extra location provider commands, fine (GPS) location,
mock location sources for testing, coarse (network-based) location
Services that cost you money: send SMS messages, directly call phone numbers
...
在AndroidManifest.xml中的强制权限
限制访问系统或应用程序整个组件的高级别权限,能够通过应用程序的AndroidManifest.xml文件来设定。所有这些都要求在被期望的组件上包含android:permission属性,以及用于控制访问的命名权限。
Activity的权限(应用于<activity>标签)限制了谁能够启动被关联的Activity。在Context.startActivity()方法和Activity.startActivityForResult()方法执行期间要检查这个权限,如果调用者没有要求的权限,那么就会从调用中抛出一个SecurityException异常。
Service的权限(应用于<service>标签)限制了谁能够启动或绑定被关联的服务。在Context.startService()方法、Context.stopService()方法和Context.bindService()方法执行期间会检查这个权限,如果调用者没有要求的权限,那么就会从调用中抛出一个SecurityException异常。
BroadcastReceiver的权限(应用于<receiver>标签)限制了谁能够发送广播通知给关联的接收器。在Context.sendBroadcast()方法返回后会检查这个权限,也就是在系统试图把提交的广播通知发送给设定的接收器的时候。在因没有权限而失败的时候,它不会向调用者抛出一个异常,它只是不发送Intent对象。同样,给Context.registerReceiver()方法提供的权限,是用来控制谁能够向程序中注册的接收器发送广播。另一种方式是,在调用Context.sendBroadcast()方法时,提供一个权限,来限制那个BroadcastReceiver对象能够接收广播通知。
ContentProvider的权限(应用于<provider>标签)限制了谁能够访问ContentProvider对象中的数据。(内容提供有一套额外的叫做URI权限的重要且易用的安全权限,稍后会介绍。)跟其他组件不同,它有两个独立的权限属性:android:readPermission用于限制谁能够从提供器中读取数据;android:writePermission用于限制谁能够向提供器中写入数据。要注意的是,如果提供器受到读写权限的保护,只拥有写权限并不意味着能够从提供器中读取数据。在首次获取提供器和执行提供器相关的操作时,会进行权限的检查(如果没有权限,就会抛出一个SecurityException异常)。使用ContentResolver.query()方法查询数据时,要求具有读权限,使用ContentResolver.insert()方法、ContentResolver.update()方法、ContentResolver.delete()方法编辑数据时,要求具有写权限。在所有的场景中,如果没有要求的权限,这个调用就会导致一个SecurityException异常被抛出。
发送广播时的强制权限
除了强制谁能够把Intent对象发送给一个BroadcastReceiver对象的权限之外,在发送一个广播通知时,还可以指定需求权限。通过调用带有权限字符串的Context.sendBroadcast()方法,可以要求接收器必须要拥有这个权限,才能够接受这个广播通知。
要注意的是,接收器和广播器都能够要求权限,发生这种情况时,双方的权限都必须检查通过后,才可以把Intent对象发送给匹配的目标。
其他强制性权限
在调用Service过程中,可以设置更细粒度的权限。这种设置是通过调用Context.checkCallingPermission()方法来完成的。调用时给这个方法传入所期望的权限字符串,它会返回一个整数,它指明了所期望的权限是否被当前调用的进程所接受。要注意的是,这种方法只能在执行来自另一个进程调用的时候使用。通常通过IDL接口来发布服务,或者是用其他的方法提供给另一个进程。
有很多有用的检查权限的方法。如果有另一个进程的PID,那么就可以使用Context.checkPermission(String, int, int)方法,针对这个PID来检查权限。如果有另一个应用程序的包名,就可以直接使用包管理器的PackageManager.checkPermission(String, String)方法来找出这个包是否已经被授予了指定的权限。
URI权限
到目前为止我们所介绍的标准的权限系统不能满足内容提供器的使用需要。内容提供器可能要保护它自己的读写权限,但是为了某些操作,它的客户端也需要把指定的URI交给另一个应用程序来处理。一个典型的示例是Mail应用程序中的附件。邮件的访问应该是受到权限的保护,因为这个用户敏感的数据。但是,如果要把一个图片附件的URI提供给一个Image浏览器,那么这个Image浏览器就会因没有权限而不能打开这个图片附件。
这个问题的解决方案是给每个URI都分配一个权限,当启动一个Activity或给一个Activity返回结果时,调用者能够设置Intent.FLAG_GRANT_READ_URI_PERMISSION和(或)Intent.FLAG_GRANT_WRITE_URI_PERMISSION权限。这样就给接受Intent对象的Activity授予了访问Intent对象中指定的数据URI的权限,而不管它是否有权访问与这个Intent对象对应的内容提供器中的数据权限。
这种机制允许使用一种共同的能力样式模型,这种模型利用用户交互(打开一个附件、选择一个通讯录等)来驱动设定更细粒度的权限。这种机制可以有效的减少应用程序所需要的权限,只需要那些与它们直接相关行为权限。
这种把权限细化到URI的做法,需要持有这些URI的内容提供器的配合。强烈推荐内容提供器实现这种机制,并且通过android:grantUriPermissions属性或<grant-uri-permissiongs>标签来声明它们所提供的权限。
更多的信息能够在Context.grantUriPermission()、Context.revokeUriPermission()和Context.checkUriPermission()方法中找到。