Android是一个多进程系统,每一个应用程序(以及系统的部分)运行在它本身的进程里。大多数程序和系统之间的安全性经过基础的Linux机制在进程级别进行支持,如分配给应用程序的用户和群组IDs。更多细化的安全特性经过“许可”机制来提供,它实施对一个进程可以执行的特定操做方面的限制,和对于每一个URI的特定数据段的特许访问的受权。html
Android安全架构中的一个设计要点是在默认状况下应用程序没有权限执行对其它应用程序、操做系统或用户有害的操做。这些操做包括读/写用户的隐私数据(例如联系方式或e-mail),读/写其它应用程序的文件,执行网络访问,保持设备激活,等等。android
应用程序的进程是一个安全的沙箱。它不能干扰其它应用程序,除非明确声明它须要额外的基本的沙箱不能提供的功能的许可权。这些许可权请求可以被不一样方式的操做所处理,常见的是基于证书和用户提示的自动容许或禁止。应用程序的权限请求被声明为静态的,这样后面在安装时可以知道它们并且不会被改变。shell
全部的Android应用程序(.apk文件)必须用证书进行签名认证,而这个证书的私钥是由开发者保有的。该证书能够用以识别应用程序的做者。该证书也不须要被认证机构签名。Android应用程序彻底容许并且通常也都是使用自签名(self-signed)证书。证书是用于在应用程序之间创建信任关系,而不是用于控制程序是否能够安装。签名影响安全性的最重要的方式是经过决定谁能够进入基于签名的许可,以及谁能够共享用户IDs。浏览器
每个Android应用程序(.apk文件)都会在安装时就分配一个独有的Linux用户ID,这就为它创建了一个沙盒,使其不能与其余应用程序进行接触(也不会让其它应用程序接触它)。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值。安全
因为安全性限制措施是发生进程级,因此两个package中的代码不会运行在同一个进程当中,他们要做为不一样的Linux用户出现。咱们能够经过使用AndroidManifest.xml文件中的manifest标签中的sharedUserId属性,来使不一样的package共用同一个用户ID。经过这种方式,这两个package就会被认为是同一个应用程序,拥有同一个用户ID(实际不必定),而且拥有一样的文件存取权限。注意:为了保持安全,只有当两个应用程序被同一个签名签署的时候(而且请求了同一个sharedUserId)才会被分配一样的用户ID.网络
全部存储在应用程序中的数据都会赋予一个属性-该应用程序的用户ID,这使得其余package没法访问这些数据。当经过这些方法getSharedPreferences(String, int), openFileOutput(String, int), 或者openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)来建立一个新文件时,你能够经过使用MODE_WORLD_READABLE与/或MODE_WORLD_WRITEABLE标志位来设置是否容许其余package来访问读写这个文件。当设置这些标志位时,该文件仍然属于该应用程序,可是它的全局读写权限已经被设置,使得它对于其余任何应用程序都是可见的。架构
一个基本的Android程序一般是没有任何许可与之关联的,这就是说它不能作任何扰乱用户或破坏数据的勾当。那么为了使用设备被保护的特性,咱们就必须在AndroidManifest.xml添加一个或多个<uses-permission>标签,用以声明你的应用程序须要的许可。app
例如,一个想要监控接收短消息的应用程序须要指定:ide
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.myapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
</manifest>
在应用程序安装时,该应用程序请求的权限许但是经过package installer来授予的。package installer是经过检查该应用程序的签名和/或用户的交换结果来肯定是否给予该程序request的权限。在用户使用过程当中不会去检查权限,也就是说要么在安装的时候就批准该权限,使其按照设计可使用该权限;要么就不批准,这样用户也就根本没法使用该feature,也不会有任何提示告知用户尝试失败。ui
不少时候, 一个许可失败会致使一个SecurityException被抛回该应用程序. 可是Android并不保证这种状况会到处发生。例如,当数据被deliver到每个receiver的时候,sendBroadcast(Intent) 方法会去检查permissions,在这个方法调用返回以后,你也不会收到任何exception。几乎绝大多数状况,一个permission failure都会打印到log当中。(注意查看Logcat)
Android系统定义的权限能够在Manifest.permission类中找到。任何一个程序均可以定义并强制执行本身独有的permissions,所以Manifest.permission中定义的permissions并非一个完整的列表(即有肯能有自定义的permissions)。
一个特定的许可可能会在你的程序操做过程当中的不少地方都被实施:
为了实施你本身的permissions,你必须首先在AndroidManifest.xml文件中声明该permissions.一般咱们经过使用一到多个<permission> tag来进行声明。
例如,一个应用程序想要控制谁能启动它的活动,能够为该操做声明许可以下:
<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>属性是必需的,经过声明该属性,咱们就能够告知系统如何去通知用户哪些应用程序须要这个许可,或者谁能够拥有该许可。具体请参看连接的文档。
<permissionGroup>属性是可选的,只是用于帮助系统显示许可permission给用户(实际是告知系统该许但是属于哪一个许可组permission group的)。你一般会选择使用标准的system group来设定该属性,或者更为少见的用你本身定义的group。推荐使用一个已经存在的group,由于这样UI给用户显示许可的时候会更简单。
须要注意的是标签(label)和描述(description)都是须要为许可提供的。这些都是字符串资源,当用户去看许可列表(android:label)或者某个许可的详细信息(android:description)时,这些字符串资源就能够显示给用户。label应当尽可能简短,之须要告知用户该许但是在保护什么功能就行。而description能够用于具体描述获取该许可的程序能够作哪些事情,实际上让用户能够知道若是他们赞成程序获取该权限的话,该程序能够作什么。咱们一般用两句话来描述许可,第一句描述该许可,第二句警告用户若是批准该权限会可能有什么很差的事情发生。下面是一个描述CALL_PHONE 许可的label和description的例子:
<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 来查看目前系统已有的permissions. 特别的,"-s"选项会以一种用户会看到的基本相同的格式来显示这些permissions。
用于限制进入系统或应用程序的Components的高级别许可能够再AndroidManifest.xml中实现.全部这些均可以经过在相应的component中包含 android:permission 属性,命名该permission以使其被用以控制进入的权限。
Activity许可(应用于<activity>标签)限制了谁才能够启动相应的活动。permission会在Context.startActivity()和Activity.startActivityForResult()的时候进行检查。若是caller没有所需的权限,则会抛出一个SecurityException。
Service许可(应用于<service>标签)用于限制谁才能够start或bind该service。在Context.startService() , Context.stopService()和Context.bindService()调用的时候会进行权限检查。若是caller没有所需的权限,则会抛出一个SecurityException。
BroadcastReceiver许可(应用于<receiver>标签)用于限制谁才能够向该receiver发送广播。权限检查会在Context.sendBroadcast() 返回时进行,由系统去发送已经提交的广播给相应的Receiver。最终,一个permission failure不会再返回给Caller一个exception;它只是不会去deliver该Intent而已。一样地,Context.registerReceiver()也能够有本身permission用于限制谁才能够向一个在程序中注册的receiver发送广播。另外一种方式是,一个permission也能够提供给Context.sendBroadcast() 用以限制哪个BroadcastReceiver才能够接收该广播。
ContentProvider许可(应用于<provider>标签)用于限制谁才能够访问ContentProvider提供的数据。(Content providers有一套而外的安全机制叫作URI permissions,这些在稍后讨论)不一样于其它的Components,这里有两种不一样的permission属性能够设置: android:readPermission 用于限制谁能够读取provider中的数据,而 android:writePermission 用于限制谁才能够向provider中写入数据。须要注意的是若是provider同时被读写许可,若是这时只有写许可并不意味着你就能够读取provider中的数据了。当你第一次获取provider的时候就要进行权限检查(若是你没有任何permission,则会抛出SecurityException), 而且这时你对provider执行了某些操做。当使用ContentResolver.query()时须要读权限,而当使用ContentResolver.insert(), ContentResolver.update(), ContentResolver.delete()时须要写权限。在全部这些状况下,没有所需的permission将会致使SecurityException被抛出。
除了以前说过的Permission(用于限制谁才能够发送广播给相应的BroadcastReceiver),你还能够在发送广播的时候指定一个permission。在调用Context.sendBroadcast()的时候使用一个permission string,你就能够要求receiver的宿主程序必须有相应的permission。
值得注意的是Receiver和broadcaster均可以要求permission。当这种状况发生时,这两种permission检查都须要经过后才会将相应的intent发送给相关的目的地。
在调用service的过程当中能够设置任意细化的许可。这是经过Context.checkCallingPermission()方法来完成的。呼叫的时候使用一个想获得的permission string,而且当该权限获批的时候能够返回给呼叫方一个Integer(没有获批也会返回一个Integer)。须要注意的是这种状况只能发生在来自另外一个进程的呼叫,一般是一个service发布的IDL接口或者是其余方式提供给其余的进程。
Android提供了不少其余的方式用于检查permissions。若是你有另外一个进程的pid,你就能够经过Context.checkPermission(String, int, int)去针对那个pid去检查permission。若是你有另外一个应用程序的package name,你能够直接用PackageManager的方法PackageManager.checkPermission(String, String)来肯定该package是否已经拥有了相应的权限。
到目前为止咱们讨论的标准的permission系统对于内容提供器(content provider)来讲是不够的。一个内容提供器可能想保护它的读写权限,而同时与它对应的直属客户端也须要将特定的URI传递给其它应用程序,以便对该URI进行操做。一个典型的例子是邮件应用程序的附件。访问邮件须要使用permission来保护,由于这些是敏感的用户数据。然而,若是有一个指向图片附件的URI须要传递给图片浏览器,那个图片浏览器是不会有访问附件的权利的,由于它不可能拥有全部的邮件的访问权限。
针对这个问题的解决方案就是per-URI permission: 当启动一个activity或者给一个activity返回结果的时候,呼叫方能够设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这赋予接收活动(activity)访问该意图(Intent)指定的URI的权限,而不论它是否有权限进入该意图对应的内容提供器。
这种机制容许一个一般的能力-风格(capability-style)模型,以用户交互(如打开一个附件, 从列表中选择一个联系人)来驱动细化的特别受权。这是一个很关键的能力,能够减小应用程序所须要的权限,只留下和程序行为直接相关的权限。
这些URI permission的获取须要内容提供器(包含那些URI)的配合。强烈推荐在内容提供器中实现这种能力,并经过android:grantUriPermissions或者<grant-uri-permissions>标签来声明支持。
更多的信息能够参考Context.grantUriPermission(), Context.revokeUriPermission()和Context.checkUriPermission()方法。
官方文档原文http://androidappdocs.appspot.com/guide/topics/security/security.html