Android API Guides –System Permissions

系统权限

声明:

本文由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平台的安全模式。

时间: 2024-10-06 00:40:03

Android API Guides –System Permissions的相关文章

android API Guides学习--Introduction(1)

android介绍: android提供了丰富的应用程序开发框架,它允许你在java语言环境中为移动设备创建独特的应用程序与环境. 1android应用程序提供多个入口点 android 应用程序是由不同的组件组合而成的,每个组件都被单独调用.activity组件提供一块屏幕作为使用者的界面.service独立的在幕后执行. 你可以使用intent类来实现组件的切换.也可以在一个app中多次调用同一个组件.例如在一个地图应用程序中显示地址的activity组件.该模型为一个单一的应用程序提供了多

Android API Guides – Introduction to Android

Android介绍 声明: 本文由Gordon翻译 发布于www.dlvoice.com 欢迎转载,但请保留此声明 原文地址:http://developer.android.com/guide/index.html Android提供了丰富的应用框架以便用户能够创建使用Java的环境来在移动设备上开发有创新性的应用和游戏.Android API Guides文档将会详细描述如何使用Android不同API来开发应用. 假如你是Android开发的新手,最好首先了解一下下面列出的Android

Android API Guides –Device Compatibility

设备的兼容性 声明: 本文由Gordon翻译 发布于www.dlvoice.com 欢迎转载,但请保留此声明 原文地址:http://developer.android.com/guide/practices/compatibility.html Android可以运行在不同类型的设备上,从手机到平板以及电视都可以运行.这些不同的设备将为开发者提供大量的潜在用户.那么为了能够让你的应用在所有的设备上运行良好,你就需要接受一些特性的多样性以及提供一个不同屏幕配置的友好的用户接口. 为了达到这样的目

Android API Guides – Application Fundamentals

应用基础 声明: 本文由Gordon翻译 发布于www.dlvoice.com 欢迎转载,但请保留此声明 原文地址:http://developer.android.com/guide/components/fundamentals.html Android应用是用Java语言来开发的.Android的SDK工具将会把你的代码和数据以及资源文件编译到APK中,所谓的APK就是一个Android包,它是以.apk结尾的压缩包文件.APK文件包含了Android应用的所有部分,一个真正的Androi

翻译Android API Guides: App Manifest

原文在这里:http://developer.android.com/guide/topics/manifest/manifest-intro.html *Manifest译作"清单",这里沿用英文便于理解,其它术语同理. **文中链接都会跳转到android开发者网站. App Manifest 每一个应用都必须在它的根目录有一份AndroidManifest.xml文件(必须使用这个名字).Android系统必须在运行应用的任何代码之前了解一些重要信息,这些信息就来自于这份mani

【Android API Guides 简译(三)】Data Storage--Storage Options

Android提供了几种永久储存手机数据的选项,而我们选择存储的方式依据于我们存储的不同的特定需求,比如你的数据是否需要只对自己公开,数据是否可以被其他应用得到或者你想要储存多大的数据. 数据存储的方式如下: Shared Preferences 通过xml类型的键值对,存储私密的原始数据. Internal Storage 内部存储 通过手机内存存储私密数据 External Storage 外部存储 在设备外部共享里存储公开的数据 SQLite Databases Android 原生内部数

【Android API Guides 简译(一)】App Resourses--Overview

将数据与程序分开的原因,表面是为了独立的管理数据,深层原因是使App兼容不同的环境即使你的数据支持不同语言或者不同屏幕大小的特殊设备.这是非常且越来越重要的! 对于各种各样的资源,我们统一分成两种: 默认资源和针对不同环境的备选资源 举个例子,默认资源存放在res/layout/ directory下,针对于横摆方向的设备的备选资源存放在res/layout-land/ directory(横摆方向的设备的具体方式见图).当只有默认资源时,见图1.当设置了备选资源时,见图二,Android系统会

android API Guides学习--Introduction(2)

应用程序基本原理: android应用程序是用java语言编写的,android SDK工具在APK(Android package)里编译代码(数据文件和资源文件).apk是一个后缀为.apk的档案文件,1个apk文件包含了android应用程序的所有内容,apk是用来安装应用程序的基于android的设备.一旦安装了apk文件,每个app运行在自己安全的沙盒中. android操作系统是一个多用户的Linux操作系统,每一个app有一个不同的用户. 系统默认给各自的app分配一个唯一的Lin

android API Guides学习--Intent and Intent Filters

Intent是一个消息传递类,同时可以启动一些组件 作用: 1启动一个activity组件. 通过Context类的 startActivity()/ startActivity ()方法启动activity组件. 2启动一个service组件 通过Context类的startService()方法启动此组件,只能执行一次性操作(例如下载文件). 通过Context类的 bindService()方法 3发送一条广播 通过Context类的sendBroadcast(), sendOrdered