三十八、请求和要求权限

在 20 世纪 90 年代末,一波病毒通过互联网传播,通过电子邮件传递,使用从微软 Outlook 中收集的联系信息。病毒会将自身的副本通过电子邮件发送给每个有电子邮件地址的 Outlook 联系人。这是可能的,因为当时 Outlook 没有采取任何措施来保护使用 Outlook API 的程序的数据,因为该 API 是为普通开发人员而不是病毒作者设计的。

如今,许多保存联系人数据的应用通过要求用户明确授予其他程序访问联系人信息的权限来保护这些数据。这些权限可以根据具体情况授予,也可以在安装时一次性授予。

Android 也不例外,因为它要求应用读取或写入联系人数据的权限。Android 的许可系统不仅对联系人数据有用,而且对 Android 框架提供的内容供应器和服务也有用。

作为一名 Android 开发人员,您经常需要确保您的应用有适当的权限来处理其他应用的数据。如果您允许其他 Android 组件使用您的数据或服务,您也可以选择要求其他应用使用您的数据或服务的权限。本章涵盖了如何实现这两个目标。

妈妈,我可以吗?

请求使用其他应用的数据或服务需要将uses-permission元素添加到您的AndroidManifest.xml文件中。您的清单可能有零个或多个uses-permission元素,都是根manifest元素的直接子元素。

uses-permission元素接受一个属性android:name,它是您的应用需要的权限的名称:

<uses-permission   android:name="android.permission.ACCESS_LOCATION" />

所有股票系统权限都以android.permission开头,并在 Android SDK 文档中列出Manifest.permission。第三方应用可能有自己的权限,希望他们已经为您记录了这些权限。以下是一些更有用的权限:

  • INTERNET,如果您的应用希望通过任何方式访问互联网,从原始 Java 套接字通过WebView小部件
  • WRITE_EXTERNAL_STORAGE,用于将数据写入 SD 卡(或设备指定的任何外部存储器)
  • NFC,用于在较新的设备上通过近场通信(NFC)无线电执行 I/O
  • ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION,用于确定设备的位置
  • CALL_PHONE,允许应用直接拨打电话,无需用户干预

权限在安装应用时得到确认。系统将提示用户确认您的应用是否可以执行权限所要求的操作。因此,要求尽可能少的权限并证明你所要求的权限是合理的是很重要的,这样用户就不会因为你要求太多不必要的权限而选择跳过安装你的应用。通过 USB 加载应用时,例如在开发过程中,此提示不会出现。

如果你没有想要的许可,并试图做一些需要它的事情,你应该得到一个SecurityException通知你缺少许可。请注意,只有当您忘记请求权限时,您才会在权限检查中失败——您的应用不可能运行,并且而不是已经被授予您所请求的权限。

立定!谁

硬币的另一面是保护您自己的应用。如果您的应用主要是活动,安全可能只是一个“出站”的东西,在那里您请求使用其他应用的资源的权利。另一方面,如果您将内容供应器或服务放在应用中,您将希望实现“入站”安全性来控制哪些应用可以对数据做什么。

请注意,这里的问题不是其他应用是否会弄乱您的数据,而是用户信息的隐私或可能会产生费用的服务的使用。这就是内置 Android 应用的股票权限所关注的地方:你是否可以阅读或修改联系人,发送短信,等等。如果你的应用不存储被认为是隐私的信息,安全性就不是问题。另一方面,如果您的应用存储私人数据,如医疗信息,安全性就更加重要了。

使用权限保护您自己的应用的第一步是再次在AndroidManifest.xml文件中声明所述权限。在这种情况下,您添加了permission元素,而不是uses-permission。同样,您可以拥有零个或更多的permission元素,全部作为根manifest元素的直接子元素。

声明权限比使用权限稍微复杂一些。您需要提供三条信息:

  • 权限的符号名:为了防止您的权限与其他应用的权限冲突,您应该使用应用的 Java 名称空间作为前缀。
  • 权限标签:选择用户能够理解的简短内容。
  • 对权限的描述:选择用户能理解的稍微长一点的内容。

下面是一个例子:

<permission   android:name="vnd.tlagency.sekrits.SEE_SEKRITS"   android:label="@string/see_sekrits_label"   android:description="@string/see_sekrits_description" />

这不会强制权限。相反,它表明这是一个可能的权限;当安全违规发生时,您的应用仍然必须标记它们。

您的应用有两种方法来实施权限,规定在什么地方和什么情况下需要权限。更简单的方法是在清单中指出哪里需要权限。更困难的选择是在代码中强制权限。接下来将讨论这两种选择。

通过清单实施权限

活动、服务和接收者都可以声明一个名为android:permission的属性,其值是访问这些项目所需的权限的名称:

<activity   android:name=".SekritApp"   android:label="Top Sekrit"   **android:permission="vnd.tlagency.sekrits.SEE_SEKRITS">**   <intent-filter>     <action android:name="android.intent.action.MAIN" />     <category       android:name="android.intent.category.LAUNCHER" />   </intent-filter> </activity>

只有已经请求您指定许可的应用才能访问安全组件。在这种情况下,“访问”的含义如下:

  • 未经许可,不能开始活动。
  • 未经许可,不能启动、停止服务或将服务绑定到活动。
  • 意图接收者忽略通过sendBroadcast()发送的消息,除非发送者允许。

在其他地方实施权限

在您的代码中,您有两种额外的方法来实施权限。

首先,您的服务可以通过checkCallingPermission()在每次调用的基础上检查权限。根据调用者是否拥有您指定的权限,这将返回PERMISSION_GRANTEDPERMISSION_DENIED。例如,如果您的服务实现了单独的读写方法,您可以通过检查这些方法是否具有您需要的 Java 权限,来要求代码中有单独的读写权限。

第二,调用sendBroadcast()时可以包含一个权限。这意味着合格的广播接收机必须持有该许可;没有许可的人没有资格得到它。我们将在本书的其他地方更详细地研究sendBroadcast()

我可以看看你的文件吗?

编译时不会自动发现权限;所有权限失败都发生在运行时。因此,记录公共 API 所需的权限非常重要,包括内容提供者、服务和从其他活动启动的活动。否则,试图与您的应用交互的程序员将不得不通过反复试验来找出权限规则。

此外,您应该预料到您的应用的用户将被提示确认您的应用说它需要的任何权限。因此,您需要为您的用户记录他们应该期望什么,以免他们被设备提出的问题弄糊涂,选择不安装或使用您的应用。您可能希望为此使用字符串资源,这样您就可以像国际化应用中所有其他消息和提示一样国际化您的权限细节。你还应该注意,你的用户会在手机或平板电脑屏幕上阅读你的许可信息,所以你的理由要简明扼要。

旧应用中的新权限

有时,Android 引入了新的权限来管理以前不需要权限的行为。WRITE_EXTERNAL_STORAGE就是一个例子。最初,应用可以在没有任何许可的情况下写入外部存储。Android 1.6 引入了WRITE_EXTERNAL_STORAGE,这是你写外存之前必须的。然而,在 Android 1.6 之前编写的应用不可能请求这种权限,因为当时并不存在这种权限。打破这些应用似乎是进步的沉重代价。

Android 所做的是在支持早期 SDK 版本的应用的某些权限中祖父。特别是,如果您的清单中有<uses-sdkandroid:minSdkVersion="3">,表示您支持 Android 1.5,您的应用将自动请求WRITE_EXTERNAL_STORAGEREAD_PHONE_STATE,即使您没有明确请求这些权限。在 Android 1.5 设备上安装您的应用的人会看到这些请求。

随着手机采用新功能以及用户对底层功能态度的成熟,引入的新权限数量也在增加,这就需要更细粒度的方法来管理访问。Android 4.0 中的新权限包括以下内容:

  • ADD_VOICEMAIL:允许应用将语音邮件注入系统
  • BIND_TEXT_SERVICE:您编写的任何从TextService派生的服务都需要
  • BIND_VPN_SERVICE:您编写的任何从VpnService派生的服务都需要
  • READ_PROFILE:提供读取用户个人资料的权限
  • WRITE_PROFILE:允许应用更新或更改用户的个人资料

最终,当你放弃对旧版本权限的支持(例如,切换到<uses-sdkandroid:minSdkVersion="4">)时,Android 将不再自动请求那些权限。因此,如果你的代码真的需要那些权限,你将需要自己申请。

*### 权限:预先或根本没有

Android 中的权限系统并不是特别灵活。值得注意的是,你必须事先申请你可能需要的所有权限,用户必须全部同意或者放弃安装你的应用。

这意味着您不能执行以下操作:

  • 创建可选的权限,用户可以对这些权限说“不,谢谢”,应用可以对这些权限做出动态反应
  • 安装后请求新的权限,这意味着即使只是一些很少使用的功能需要权限,你也必须请求

因此,在确定应用的功能列表时,牢记权限非常重要。你要求的每一个额外的许可都是一个过滤器,会让你失去一部分潜在的观众。某些组合——比如INTERNETREAD_CONTACTS——会有更强的效果,因为用户可能会担心这种组合会产生什么效果。您需要自己决定通过提供该功能来吸引更多用户是否值得为该功能的运行要求权限。*