android权限管理

来源:互联网 发布:java乘法函数 编辑:程序博客网 时间:2024/06/06 04:20

系统权限

Android 是一个权限分隔的操作系统,其中每个应用都有其独特的系统标识(Linux 用户 ID 和组 ID)。系统各部分也分隔为不同的标识。Linux 据此将不同的应用以及应用与系统分隔开来。

安全架构

在默认情况下,任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。比如:读写用户私有数据、执行网络访问等。

Android应用都在进程沙盒中运行,如需访问沙盒外的额外功能,应用应该静态声明所需权限,android系统提示用户同意。

用户 ID 和文件访问

在android系统上,每个应用是一个Linux用户,他们都拥有唯一一个用户ID。应用程序的数据只能被拥有该应用程序对应的用户ID的程序在可以访问,为此,我们可以通过设置AndroidManifest.xml 的 manifest 标记中使用 sharedUserId 属性,以及和该应用程序打相同的签名来共享用户ID,以实现共享数据的功能。在android也可以通过ContentProvider提供数据给其他应用访问。

UserId不同时:

包名不同:未设定process属性时,各自的Activity在各自的进程。 即使process指定了包名,也不会和另一个用户的同名包共享进程。

 包名相同:签名相同:覆盖旧的同包名apk。

           签名不同:新的apk会安装失败。

UserId相同时:

 包名不同:未设定process属性时,各自的Activity在各自的进程。

           process属性指定,则可以共享进程。

  包名相同:签名相同:覆盖旧的同包名apk。

            签名不同:新的apk会安装失败。

使用权限

在android应用中,要利用受保护的设备功能,必须在清单文件中包含一个或者多个<uses-permission>标记。

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

在清单文件中列出的权限,如果是正常权限(不涉及用户隐私数据和设备正常运行)系统自动授予该权限,危险权限(涉及用户隐私数据和正常运行),需要系统通知用户并获得用户同意。

自动权限调整

随着时间的推移,一些特定API的访问,低版本不需要的权限,在高版本API Level中可能需要添加相关权限。Android 将根据为 targetSdkVersion 属性提供的值决定应用是否需要权限。如果该值低于在其中添加权限的版本,则 Android 会自动添加该权限。

例如,API Level 4 中加入了 WRITE_EXTERNAL_STORAGE 权限,用以限制访问共享存储空间。如果您的 targetSdkVersion 为 3 或更低版本,则会向更新 Android 版本设备上的应用添加此权限。

注意:如果某权限自动添加到应用,则即使您的应用可能实际并不需要这些附加权限,Google Play 上的应用列表也会列出它们。

正常权限和危险权限

正常权限涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域,不需要用户授予,系统自动授予。

危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域,需要系统通知用户,用户授予。

 

权限组

正常权限和危险权限都是属于权限组,如果设备运行的是 Android 6.0(API 级别 23),targetSdkVersion 是23 或者更高版本:

如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。

如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。

如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。

表 1. 危险权限和权限组。

权限组

权限

CALENDAR

·         READ_CALENDAR

·         WRITE_CALENDAR

CAMERA

·         CAMERA

CONTACTS

·         READ_CONTACTS

·         WRITE_CONTACTS

·         GET_ACCOUNTS

LOCATION

·         ACCESS_FINE_LOCATION

·         ACCESS_COARSE_LOCATION

MICROPHONE

·         RECORD_AUDIO

PHONE

·         READ_PHONE_STATE

·         CALL_PHONE

·         READ_CALL_LOG

·         WRITE_CALL_LOG

·         ADD_VOICEMAIL

·         USE_SIP

·         PROCESS_OUTGOING_CALLS

SENSORS

·         BODY_SENSORS

SMS

·         SEND_SMS

·         RECEIVE_SMS

·         READ_SMS

·         RECEIVE_WAP_PUSH

·         RECEIVE_MMS

STORAGE

·         READ_EXTERNAL_STORAGE

·         WRITE_EXTERNAL_STORAGE


定义和实施权限

一、定义权限

在AndroidManifest.xml 定义一个或多个<permission>权限。例如:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.myapp" >    <permission android:name="com.example.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>

android:description:对权限的描述,一般是两句话,第一句话描述这个权限所针对的操作,第二句话告诉用户授予app这个权限会带来的后果

android:permissionGroup:权限所属权限组的名称,是可选属性,只是用于帮助系统向用户显示权限。大多数情况下,您要将此设为标准系统组(列在android.Manifest.permission_group 中),但您也可以自己定义一个组。建议使用现有的组,因为这样可简化向用户显示的权限 UI。

android:protectionLevel:权限的等级,

normal是最低的等级,声明此权限的app,系统会默认授予此权限,不会提示用户。

dangerous权限对应的操作有安全风险,系统在安装声明此类权限的app时会提示用户。

signature权限表明的操作只针对使用同一个证书签名的app开放。

signatureOrSystem与signature类似,只是增加了rom中自带的app。(系统app)。

注:系统不允许多个软件包使用同一名称声明权限,除非所有软件包都使用同一证书签署。如果软件包声明权限,则系统不允许用户安装具有相同权限名称的其他软件包,除非这些软件包使用与第一个软件包相同的证书签署。为避免命名冲突,建议对自定义权限使用相反域名样式命名,例如com.example.myapp.ENGAGE_HYPERSPACE。

一、实施权限

如果你开发一套只在自己设备上运行的应用,那可以开发出一个管理权限的软件包,各个应用之间的调用可以通过<uses-permission> 元素请求这些权限。

 

如果应用的调用和被调用方仅适用于相同的签名才可以,那就可以采用验证签名的方式,而不需要自定义权限。

 

多个不同签名的应用互相调用,尽可能每个权限只定义一次,多个相同签名的应用也是如此。

实施 AndroidManifest.xml 中的权限

可能在程序运行期间的多个位置实施特定权限

在调用系统时,防止应用执行某些功能。

在启动 Activity 时,防止应用启动其他应用的 Activity。

在发送和接收广播时,控制谁可以接收您的广播,谁可以向您发送广播。

在访问和操作内容提供程序时。

绑定至服务或启动服务。

 

Activity权限:在定义Activtiy中设置权限,在startActivity和startActvitiyForResult调用时检查权限,没有权限将会抛出SecurityException。

Service权限:限制谁可以启动或者绑定该Service。在 Context.startService()、Context.stopService() 和 Context.bindService() 时会检查权限;如果调用方没有所需的权限,则调用会抛出 SecurityException。

BroadcastReceiver 权限:限制谁可以发送广播给相关的接收方。在 Context.sendBroadcast() 返回后检查权限,因为系统会尝试将提交的广播传递到指定的接收方。因此,权限失效不会导致向调用方抛回异常;只是不会传递该 intent。同样,可以向 Context.registerReceiver() 提供权限来控制谁可以广播到以编程方式注册的接收方。另一方面,可以在调用 Context.sendBroadcast() 时提供权限来限制允许哪些 BroadcastReceiver 对象接收广播。

ContentProvider 权限:限制谁可以访问 ContentProvider 中的数据。与其他组件不同,您可以设置两个单独的权限属性:android:readPermission 限制谁可以读取提供程序,android:writePermission 限制谁可以写入提供程序。请注意,如果提供程序有读取和写入权限保护,仅拥有写入权限并不表示您可以读取提供程序。第一次检索提供程序时将会检查权限(如果没有任何权限,将会抛出 SecurityException),对提供程序执行操作时也会检查权限。使用 ContentResolver.query() 需要拥有读取权限;使用 ContentResolver.insert()、ContentResolver.update()、ContentResolver.delete() 需要写入权限。在所有这些情况下,没有所需的权限将导致调用抛出 SecurityException。

通常,权限失效会导致 SecurityException 被扔回应用。但不能保证每个地方都是这样。例如,sendBroadcast(Intent) 方法在数据传递到每个接收者时会检查权限,在方法调用返回后,即使权限失效,您也不会收到异常。但在几乎所有情况下,权限失效会记入系统日志。

发送广播时实施权限

除了实施谁可以向注册的 BroadcastReceiver 发送 intent 的权限(如上所述),您还可以指定在发送广播时需要的权限。通过使用权限字符串调用 Context.sendBroadcast(),您可以要求接收方的应用必须拥有该权限才可接收您的广播。

请注意,接收者和广播者可能需要权限。此时,这两项权限检查都必须通过后方可将 intent 传递到相关的目标。

其他权限实施

可对任何服务调用实施任意细化的权限。这可通过Context.checkCallingPermission() 方法完成。使用所需的权限字符串调用,它将返回一个整数,表示权限是否已授予当前的调用进程。请注意,仅在执行从另一个进程传入的调用(通常是通过从服务发布的 AIDL 或者指定给另一进程的某种其他方式完成)时才可使用此方法。

检查权限还有许多其他有用的方法。如果您有另一个进程的 pid,可以使用 Context 方法Context.checkPermission(String, int, int) 检查针对该 pid 的权限。如果您有另一个应用的软件包名称,可以使用直接的 PackageManager 方法PackageManager.checkPermission(String, String) 了解是否已为特定软件包授予特定权限。

// 检查调用者是否具有 permission权限  // 此方法仅在调用IPC(interprocess communication)方法时有用  public static boolean checkPermission(Context context, String permission) {      //检查如果是当前应用则返回真      if (Binder.getCallingPid() == Process.myPid()) {          return true;      }      if (context.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {          return true;      }      return false;  }


判断本程序是否拥有某权限的方法:

private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE";    private static boolean hasExternalStoragePermission(Context context) {          int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION);          return perm == PackageManager.PERMISSION_GRANTED;      } 

判断某个程序是否拥有某权限的方法:

private static boolean checkPermission(Context context, String permName, String pkgName){          PackageManager pm = context.getPackageManager();          if(PackageManager.PERMISSION_GRANTED == pm.checkPermission(permName, pkgName)){              System.out.println(pkgName + "has permission : " + permName);              return true;          }else{              //PackageManager.PERMISSION_DENIED == pm.checkPermission(permName, pkgName)              System.out.println(pkgName + "not has permission : " + permName);              return false;          }      }


URI 权限

到目前为止所述的是标准权限系统,内容提供程序仅仅使用此系统通常是不够的。内容提供程序可能需要通过读取和写入权限保护自己,而其直接客户端也需要将特定 URI 传给其他应用以便于它们运行。邮件应用中的附件是一个典型的示例。应通过权限保护对邮件的访问,因为这是敏感的用户数据。但是,如果将图像附件的 URI 提供给图像查看程序,该图像查看程序不会有打开附件的权限,因为它没有理由拥有访问所有电子邮件的权限。

此问题的解决方法是采用 per-URI 权限机制:在启动 Activity 或返回结果给 Activity 时,调用方可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION 和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这将授予接收 Activity 权限访问 intent 中的特定数据 URI,而不管它是否具有访问 intent 对应的内容提供程序中数据的任何权限。此机制支持常见的能力式模型,其中用户交互(打开附件、从列表中选择联系人等)驱动临时授予细化的权限。这是一项关键功能,可将应用所需的权限缩小至只与其行为直接相关的权限。但授予细化的 URI 权限需要与拥有这些 URI 的内容提供程序进行一定的合作。强烈建议内容提供程序实施此功能,并且通过 android:grantUriPermissions 属性或<grant-uri-permissions> 标记声明支持此功能。详细使用信息见:URI权限