Android API Guides –System Permissions

时间:2023-02-04 04:16:32

系统权限

声明:

本文由Gordon翻译

公布于www.dlvoice.com

欢迎转载,但请保留此声明

原文地址:http://developer.android.com/guide/topics/security/permissions.html

Android是一个特权分离的操作系统。执行在其上的应用都有一个特定的系统身份(Linux的用户ID和组ID)。系统的部分也会被分为特定的身份,Linux就是通过这个身份来差别各个应用的。

更加具体的安全特性是通过“权限”机制来控制一个进程的特定操作能否够运行。

它通过每个URI的权限来来决定他们能否够訪问特定的数据。

本文将会介绍开发人员怎样使用Android提供的安全特性。更基础的文章“Android Security Overview”能够在Android的开源项目中查看。

安全架构

Android安全架构设计的核心理念就是没有一个应用能够破坏另外一个应用。操作系统或者用户。这包含读写用户的私有数据(比方联系人和email)。读写另外一个应用的文件,进行网络訪问。保持设备一直醒着或者别的操作。

由于每个应用都是工作在进程封装上。所以它必须明白地分享资源和数据。他们能够通过声明他们须要的权限来实现资源和数据的共享。

应用静态声明他们的权限。然后系统在安装应用的时候请求用户允许应用获得这些权限。

应用的封装并非由编译应用的技术来决定的,Dalvik虚拟机(VM)并非一个特殊安全的界限,每个应用都能够执行本地的代码(參考Android NDK)。每一种类型的应用——Java,本地以及混合的——他们都是用相同的方式来进行封装的,而且他们的安全等级也是一样的。

应用的签名

全部的APK文件都必须进行签名,并且签名使用的是包括开发人员私有密钥的证书。

这个证书指明了应用的作者。这个证书并不须要一个证书认证来进行签名。

通常来说。Android的应用使用一个自签名的证书就足够了。

证书的目的就是为了区分应用的作者。这就使得系统能够推断应用能否够訪问签名级别的权限,以及是否同意别的应用和这个应用使用相同的Linux身份(ID)。

用户ID和文件存取

在安装的时候,Android给每一个包一个固定的Linux用户ID。同一设备上这个ID将会伴随这个包一生。当然不同设备上,同一个应用包可能会有不同的用户ID。

无论如何在特定的设备上每一个包都有一个特定的UID。

由于安全相关的操作都是在进程级进行执行的,不论什么两个应用包的代码不能在同一进程执行,由于他们须要在不同的Linux用户上进行执行。若你想两个应用使用相同的用户ID执行,仅仅要设置AndroidManifest.xml文件的manifest标签中的sharedUserId属性相同就可以。

这样做了之后,在安全层面来看。这两个应用将会被觉得是同一个应用,具有相同的用户ID和文件权限。

注意,仅仅有两个使用相同签名的应用(当然SharedUserID也得相同)才会给予相同的用户ID。

应用存储的不论什么数据都应当指定为这个应用的用户ID,而且不能被别的应用包訪问。当使用getSharedPreferences(String, int), openFileOutput(String, int)或者openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)创建文件时,你能够使用MODE_WORLD_READABLE或者MODE_WORLD_WRITEABLE标志来同意别的应用对这个文件的读写。当这些标志被设置之后,这个文件仍然是属于你的应用,可是他的全局读写权限将会被设置。从而让别的应用能够使用它。

权限的使用

一个主要的Android应用默认来说是没有相关权限的。这也就意味着它不能做不论什么破坏用户体验及设备数据的操作。

为了保护设备的特性。你必须在AndroidManifest.xml文件里声明一个或者多个<uses-permission>标签。

比如。一个须要监听SMS信息的应用应当设定例如以下:

1
2
3
4
5
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.myapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
...
</manifest>

在应用安装的时候,应用所请求的权限(用户的签名和声明时确定的)会在包安装的时候让用户赋予给应用。应用在执行的时候,就不会再让用户进行检查了,应用要不在安装的时候被赋予特定的权限。从而能够使用对应的特性,要不就没有使用相关特性的权限。

通常情况下。若是权限失败将会导致一个SecurityException发送给应用。可是并非在不论什么地方都会产生这个exception。举例来说,sendBroadcast(Intent)方法由于要把数据发送给每个receiver,在这种方法返回的时候它会检查他们的权限,可是若是有权限的失败。你不会收到exception。

当然。差点儿全部的权限失败的情况。都会被打印到系统log中。

然而,正经常使用户使用的情况下(比方应用从Google应用商店里安装),若是用户不同意应用申请的权限。这个应用就不会被成功安装。所以。一般来说你没有必要操心执行时缺少权限。由于其实你的应用在安装的时候就已经获得了它想要的对应权限。

Android系统提供的权限能够參考Manifest.permission文件。不论什么应用也都有可能定义并支持它自己的权限。所以这个列表也不能包括全部的可能权限。

  • 一个特定的权限可能在你的应用操作的一系列地方被实施:
  • 当调用到系统的时候,为了防止运行特定的函数。
  • 当開始一个activity的时候,为了防止一个应用调用另外一个应用的activity。
  • 在发送和接收broadcast,在控制谁能接收你的broadcast或者谁能发送broadcast给你。
  • 绑定或者启动service。

注意:

随着时间的推移,平台可能会添加新的权限要求。所以为了使用特定的API,你的应用须要请求一些之前不用请求的权限。由于已经存在的应用可能觉得訪问这些API是直接可用的。Android能够在应用的manifest文件里直接申请新的权限请求从而避免在新的平台版本号上破坏原有的应用。

Android作出应用可能须要权限的描写叙述是基于targetSdkVersion属性的。假如这个的值小于权限假如的版本号。那么Android就添加相应的权限。

举例来说,WRITE_EXTERNAL_STORAGE权限是在API级别4中增加的。它是为了防止訪问共享的存储空间。

假如你的targetSdkVersion小于3,那么在新的Android版本号中会把这个的权限增加到你的应用中。

须要注意的是,假如这样的情况在你的应用中发生,即使这些权限在你的应用中可能没有真正地请求。Google应用商店也会在显示你的应用的时候请求这些权限。

为了避免这样的情况发生,你须要及时把你的targetSdkVersion更新到足够高的版本号。

你能够參考Build.VERSION_CODES文档来看每次公布都增加了哪些权限。

声明和实施许可

为了实施你的权限,你须要首先在AndroidManifest.xml文件里使用<permission>标签来声明他们。

比如,一个应用想要控制谁能够启动它的activity,就应当为这个操作声明一个权限,详细的方法例如以下:

1
2
3
4
5
6
7
8
9
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.me.app.myapp" >
<permission android: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>属性是须要的。它是用来告诉系统应用须要这个权限的时候是怎样来通知用户的,或者谁是被同意得到这个权限的。详细的描写叙述能够參考链接的文件。

(译者注:后期翻译ok会增加)。

<permissionGroup>属性是可选的,仅仅是用来让系统向用户显示权限的。通常你能够把它设为一个标准的系统组(在android.Manifest.permission_group中有列出)或者有时候你也能够设为你自定义的内容。推荐是使用一个已经存在的组。这样会简化向用户显示的权限UI。

注意应当为权限提供标签和描写叙述。

当用户看到一个权限的列表(android:label)或者单独权限的具体信息(android:description)时,应当有一个字符型的资源用来进行显示。这个标签应当简短,用关键词来描写叙述权限保护的功能就可以。

描写叙述是一些句子用来指明权限获得者能做些什么。

一般来说,描写叙述有两个句子,第一个具体描写叙述权限,第二个用来说明应用被授予这个权限后会发生些什么不好的事情。

以下就是CALL_PHONE权限的标签和描写叙述:

<string name=”permlab_callPhone”>directly call phone numbers</string>

<string name=”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>

你能够通过设置应用和shell命令“adb shell pm list permissions”来查看当前系统定义的权限。若是使用设置应用,能够在设置->应用以下进行查看。

选取一个用户。向下滚动,能够看到应用使用的权限。

对开发人员来说,adb –s的选项将会以用户看到的形式来显示相应的权限:

1
2
3
4
5
6
7
8
9
10
11
12
$ adb shell pm list permissions -s
All Permissions:
 
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>标签)限制了谁能够启动和绑定相应的service。

这个权限是通过Context.startService()。Context.stopService()以及Context.bindService()来检查的。假如调用者没有这个权限,相同会产生一个SecurityException。

BroadcastReceiver权限(<receiver>标签)限制了谁能向相关的receiver发送广播。这个权限是在Context.sendBroadcast()之后检查权限的,由于这个时候系统会把广播发送给特定的接受者。这样一来,权限的失败将不会产生一个exception返回给调用者,他仅仅会不发送intent而已。相同的。权限能够通过Context.registerReceiver()来控制谁能够广播给一个通过程序注冊的receiver。还有一方面,当调用context.sendBoradcast()的时候也会提供权限来限制哪一个BoradcastReceiver对象能够接收这个广播。

(详情见以下)

ContentProvider权限(<provider>标签)限制了谁能够訪问ContentProvider的数据。(Content provider另一个重要的额外可用的安全机制称之为URI权限,这个会在稍后进行介绍)。

和别的组件不同,这里有两个独立的权限属性能够设置:android:readPermission限制了谁能够读这个provider,android:writePermission限制了谁能够写它。

注意,provider是由读写权限分别保护的,获得些权限不意味着你能够读。

这个权限是在你第一次检索provider进行检查的(假如你没有权限,一个securityException将会抛出)。当然你在这个provider上进行操作的时候也会检查对应的权限。

能够使用ContentResolver.query()函数来请求得到读的权限,使用ContentResolver.insert()。ContentResolver.update(),
ContentResolver.delete()来请求写的权限。在全部这些情况中。若是没有得到对应权限都会抛出一个SecurityException。

当发送广播的时候运行权限

除了上文提到的对于注冊过的BroadcastReceiver发送intent的时候运行权限之外,你还能够在发送广播的时候请求权限。在调用Context.sendBroadcast()的时候。增加一个权限字符串,你就能够要求这个接收的应用必须有响应的权限才干接收广播。

注意,发送者和接收者都能够请求权限。

这样的情况下,在发送intent和接收端都须要进行对应的权限检查。

其它的权限运行

更细的权限会在调用service时运行。这个是通过Contex.checkCallingPermission()方法来实现的。在调用这种方法的时候传入一个权限字符串,就会返回一个整形值用来表示当前的进程是否已经获得了相应的权限。

注意,这个仅仅会在别的进程运行一个调用才会被使用。通常来说是通过一个service的IDL接口或者提供给别的进程的其它方法来实现。

另一些别的方式来检查权限。假如你有另外进程的pid,你能够使用Context.checkPermission(String, int, int)来检查相应的权限。

假如你有别的应用的包名字。你能够使用PackageManager.checkPermission(String, String)来检查相应的包是否获得相应的权限。

URI权限

到眼下为止所描写叙述的标准的权限系统对content providers的使用来说都不是非常好。

content provider可能想要保护它自己的读写权限,尤其是当它的直接使用者对别的应用操作获取特定的URI的时候。一个典型的样例就是邮件应用中的附件。邮件的訪问须要权限进行设置。由于毕竟用户的数据是敏感的。然而,假如图片浏览器获得了一个图片附件的URI。这个图片浏览器将不会有打开图片的权限,由于它没有道理去获得訪问邮件的权限。

这个问题的解决方法就是每个URI的权限:当启动一个activity或者返回一个结果给activity的时候,调用者能够设置Intent.FLAG_GRANT_READ_URI_PERMISSION或者Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这个同意接收activity获得訪问intent中特殊数据URI的权限。而不用管他是否有权限訪问这个Intent提供者的数据权限。

这个机制同意了一个用户交互时(打开一个附件,选择一个联系人等)对专用的更细的权限訪问的通用模型。

这将会是降低应用须要权限的一个关键所在。应用仅仅须要那些他们特性直接相关的权限就可以。

然而对细粒度URI权限的获取,须要这些URI的content provider也要做一些对应的操作。我们推荐这些content provider实现这个功能,他们仅仅须要通过android:grantUriPermissions属性或者<grant-uri-permissions>标签来声明就可以。

很多其它的资讯请參考Context.grantUriPermission(),Context.revokeUriPermission()和Context.checkUriPermission()方法。

推荐您继续阅读下面内容:

Permission that Imply Feature Requirements

关于怎样通过请求权限来限制你的应用仅仅在包括相应硬件或者软件特性的设备上执行。

<uses-permission>

Manifest标签下的API參考,他声明了应用须要的系统权限。

Manifest.permission

全部系统权限的API參考。

下面内容你可能也感兴趣:

Device Compatibility

关于Android在不同类型设备上执行的资讯,他会介绍怎样针对不同设备进行优化你的应用以以及怎样在不同设备上执行你的应用。

Android Security Overview

更具体的关于Android平台的安全模式。