Android权限

权限的本质

  1. 在Android中,一个权限,简单来说只是一个字符串,表示执行特定操作的能力,目标操作可以是任何操作,从访问一个物理资源,或是共享数据,到启动或访问一个第三方应用中的组件。
  2. 使用pm list permissions命令可查看当前系统已知的权限列表,权限名称以定义它的包名为前缀,再接上.permission字符串。
generic_x86:/data/system/users/0 # pm list permissions
All Permissions:

permission:com.google.android.gms.auth.api.phone.permission.SEND
permission:android.permission.REAL_GET_TASKS
permission:android.permission.ACCESS_CACHE_FILESYSTEM
permission:android.permission.REMOTE_AUDIO_PLAYBACK
permission:com.google.android.apps.photos.permission.C2D_MESSAGE
permission:android.permission.MANAGE_APPOPS
permission:android.permission.REGISTER_WINDOW_MANAGER_LISTENERS
permission:android.permission.INTENT_FILTER_VERIFICATION_AGENT
permission:android.permission.BIND_INCALL_SERVICE
  1. 权限的申请
  • 应用程序通过在AndroidManifest.xml文件内添加一至多个<uses-permission>标签来申请权限,并且使用<permission>标签来定义新的权限。
  • 在每个应用程序安装时,系统使用包管理器服务,将权限赋给它们,包管理器维护一个已安装程序包的核心数据库,包括预先安装和用户安装的程序包,其中包括安装的路径、版本、签名证书和每个包的权限,还包括一个所有已定义的权限列表。
  • 这个包数据库以XML文件的形式存放于/data/system/packages.xml,它随着每次应用的安装、升级或卸载而进行更新。
generic_x86:/data/system/users/0 # cat /data/system/packages.xml   
 <package name="com.example.recyclerview" codePath="/data/app/com.example.recyclerview-Rw5fEnSeHOTTH9okLcQhVA==" nativeLibraryPath="/data/app/com.example.recyclerview-Rw5fEnSeHOTTH9okLcQhVA==/lib" publicFlags="541638470" privateFlags="0" ft="17303486940" it="173001ec31d" ut="17303486d09" version="1" userId="10137">
        <sigs count="1" schemeVersion="2">
            <cert index="14" />
        </sigs>
        <proper-signing-keyset identifier="29" />
    </package>
     <perms>
            <item name="android.permission.INTERNET" granted="true" flags="0" />
            <item name="android.permission.READ_PRIVILEGED_PHONE_STATE" granted="true" flags="0" />
            <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
            <item name="android.permission.MAINLINE_NETWORK_STACK" granted="true" flags="0" />
            <item name="android.permission.CONNECTIVITY_INTERNAL" granted="true" flags="0" />
            <item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
            <item name="android.permission.READ_DEVICE_CONFIG" granted="true" flags="0" />
            <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
        </perms>
  • 每个包都使用<package>标签表示,包含UID(userId属性)、签名证书(<cert>标签)和所授予的权限(<perms>),使用android.content.pm.PackageManager类的getPackageInfo()方法,可以获取已安装程序的相关信息,该方法会返回一个PackageInfo实例,封装在<packge>标签下的所有信息。
 		PackageManager pm = getPackageManager();
        PackageInfo packageInfo = null;
        try {
            packageInfo = pm.getPackageInfo("com.google.android.networkstack.permissionconfig", 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "packageinfo->"+packageInfo.packageName);

权限保护级别

  1. 一个权限的保护级别是:暗示权限中隐含的潜在风险,并且指出当决定是否赋予权限时系统应遵循的校验程序的流程。
  2. normal级别
  • 这是权限保护级别的默认值,它定义了访问系统或其他应用程序的低风险权限,normal保护级别的权限无须用户确认,会自动授予。
  1. dangerous级别
  • dangerous级别的保护权限,可以访问到用户数据或者在某种形式上控制设备。
  1. signature级别
  • signature级别的权限只会赋予那些与声明权限使用相同证书的应用程序,这是最严格的权限级别,因为它需要持有加密密钥,而这往往是由应用或平台的所有者控制着。
  1. signatureOrSystem级别
  • 它们被赋予系统镜像的部分应用,或者与声明权限具有相同签名的应用程序,这允许厂商无须共享签名密钥,即可预装自己的应用来共享一个需要权限的特定功能。

权限的赋予

  1. 高层的组件,如应用和系统服务,通过包管理器查询应用程序被赋予的是哪些权限,并决定是否准许访问,低层的组件,如本地守护进程,通常不访问包管理器,而依赖于进程的UID、GID和补充GID来决定赋予权限。
  2. Android进程有一组进程相关的属性,其中最为重要的是真实和有效UID、GID,以及一组补充GID。
  3. 每个Android应用在安装时都会被分配一个独一无二的UID,在一个专有的进程内执行,应用启动时,进程的UID和GID由包管理器设置为应用程序的UID,如果应用已被赋予了额外的权限,就把这些权限映射程一组GID,作为补充GID分配给进程。
  4. 内置权限到GID的映射定义在/etc/permission/platform.xml文件内:
generic_x86:/ # cat /etc/permissions/platform.xml                              
<?xml version="1.0" encoding="utf-8"?>

    <permission name="android.permission.LOOP_RADIO" >
        <group gid="loop_radio" />
    </permission>

    <!-- Hotword training apps sometimes need a GID to talk with low-level
         hardware; give them audio for now until full HAL support is added. -->
    <permission name="android.permission.MANAGE_VOICE_KEYPHRASES">
        <group gid="audio" />
    </permission>

    <permission name="android.permission.ACCESS_BROADCAST_RADIO" >
        <!-- /dev/fm is gid media, not audio -->
        <group gid="media" />
    </permission>

    <permission name="android.permission.USE_RESERVED_DISK">
        <group gid="reserved_disk" />
    </permission>
	....
    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
    <assign-permission name="android.permission.WAKE_LOCK" uid="media" />
    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
    <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
    <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />

  • <assign-permission>标签用于相反的目的,它用于哪些运行在特定UID下的、没有对应包文件的系统进程赋予更高层次的权限,如android.permission.MODIFY_AUDIO_SETTINGS被分配给uid=media的进程。
  • Android系统中并没有/etc/group文件,所以组名称到GID的映射是静态的,在android_fielsystem_config.h头文件中定义:
#define AID_ROOT             0  /* traditional unix root user */
41
42#define AID_SYSTEM        1000  /* system server */
43
44#define AID_RADIO         1001  /* telephony subsystem, RIL */
45#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
46#define AID_GRAPHICS      1003  /* graphics devices */
47#define AID_INPUT         1004  /* input devices */
48#define AID_AUDIO         1005  /* audio devices */
49#define AID_CAMERA        1006  /* camera devices */
50#define AID_LOG           1007  /* log devices */
...
114static const struct android_id_info android_ids[] = {
115    { "root",          AID_ROOT, },
116
117    { "system",        AID_SYSTEM, },
118
	...
170};
  • 包管理器在启动时读取platform.xml,并维护一个权限到GID的列表,当它给一个安装中的包授权时,包管理器检查每个权限是否有相对应的GID,如果有,则此GID加入到应用到补充GID列表中。
  1. 赋予进程属性
  • Android每个应用实际上就是一个执行应用字节码的Dalvik虚拟机进程,为了减少应用程序所占内存并加速启动时间,Android不会为每个应用开启新的Dalvik虚拟机进程,Android使用一个叫做zygote的已部分初始化的进程,当需要启动新的应用时,就使用fork()函数来复制zygote进程,使它从一般的zygote进程转化成具体的某个应用程序。
  • fork进程会继续继承zygote的进程空间,该进程空间已经加载了大部分核心和Java应用框架库,因为这些类从来不变,并且Linux在fork进程时,使用了copy-on-write机制,所以所有的zygote子进程会共享相同的Java类的副本。
  • zygote进程是由init.rc初始化脚本启动的,从一个同样名为zygote的UNIX域套接字接收指令,当zygote收到启动新应用的请求是,它首先fork自身,子进程大致使用如下代码做特殊化:
    在这里插入图片描述
  • 子进程首先使用setgroupsIntarray()设置补充GID,然后使用setlimitsFromArray()设置资源限制,然后在调用setresgid()和setresuid()分别设置实际用户/组ID、有效用户/组ID和保存用户/组ID。
  • 子进程跟zygote一样,是以root执行的,所以它可以更改自己的资源限制和进程属性,新进程属性设置完成后,子进程会以分配的UID和GID执行,再也无法切换回root了,因为其保存的用户ID不是0.
  • 在设置完UID和GID之后,进程使用setCapabilities()设置进程的权能,通过将自己加入一个预定义的控制组,来设置调度策略,再调用setSELinuxContext()来设置自己的nice name(通常为包名),和seinfo标签,用于SELinux。
  • 用ps命令查看进程:
generic_x86:/ # ps -e
USER           PID  PPID     VSZ    RSS WCHAN            ADDR S NAME            
root             1     0   31788   4140 SyS_epoll+          0 S init
root          1844     1 1734600  90712 poll_sche+          0 S zygote
system        2112  1844 2367864 328748 SyS_epoll+          0 S system_server
u0_a105       2314  1844 2170488 173572 SyS_epoll+          0 S com.android.sys+
network_sta+  2545  1844 1866300  95236 SyS_epoll+          0 S com.google.andr+
webview_zyg+  2561  1844 1736836  45932 poll_sche+          0 S webview_zygote
radio         2637  1844 1909236 105944 SyS_epoll+          0 S com.android.pho+
u0_a126       2897  1844 2073872 134620 SyS_epoll+          0 S com.google.andr+
u0_a97        3018  1844 1954228 139736 SyS_epoll+          0 S com.google.andr+
u0_a60        3072  1844 1855416  77568 SyS_epoll+          0 S com.google.andr+
secure_elem+  3328  1844 1842788  70264 SyS_epoll+          0 S com.android.se
radio         3372  1844 1843356  72932 SyS_epoll+          0 S com.android.ims+
  • PID列表示进程ID,PPID列表示父进程ID,NAME列表示进程名称,因此,zygote由init进程启动,所有应用进程的父进程均是zygote,每个进程在专用用户下执行,或者是内置的,或者是安装时自动分配的,进程名称被设置为每个应用的包名称。

权限执行

  1. 从zygote进程fork时,均分配了UID、GID和补充GID,系统内核和守护进程使用这些标识来决定,是否赋予进程到特定系统资源或功能的访问权限。
  2. 内核层的权限执行
  • Android系统对普通的文件、设备节点和本地套接字的访问控制,和任何Linux系统一样,Android增加的特有控制是,哪些创建网络套接字的进程需要属于inet组,这也被称为安卓的paranoid网络访问控制机制,由Android内核中的一个额外检查实现。
    在这里插入图片描述
  • 哪些不属于AID_INET组(GID 3003,名称inet)的进程,不会拥有CAP_INET_RAW的权能(允许使用RAW和PACKET套接字),因而会收到一个拒绝服务错误。
  • 非Android内核不会定义CONFIG_ANDROID_NETWORK,因此也就不需要特定的组对应创建套接字的操作。
  • 为使inet组可以被分配到应用进程,那么就需要赋予进程INTERNET权限,因此,仅当应用由INTERNET权限时,才可以创建套接字。
  1. 原生守护进程级别的权限执行
  • Binder是Android中的首选IPC机制,但底层但原生守护进程常常使用UNIX域套接字进行进程间通信,UNIX域套接字使用文件系统上但节点来表示,所以可以使用标准的文件系统权限机制进程权限控制。
  • 大多数套接字的访问权限只允许同用户/组的进程访问,而以不同UID和GID运行的客户端,是无法连接套接字的,系统守护进程的本地套接字由init.rc定义,该文件由init进程创建。
  • 设备卷管理守护进程vold在init.rc内如何定义的:
service vold /system/bin/vold
	class core
	socket vold stream 0660 root mount
	ioprio be 2
  • vold声明来一个同样名为vold,访问权限为0660的套接字,该套接字属于root,属于组为mount,vold守护进程需要以root运行,用以挂在/卸载卷设备,而mount组的成员(AID_MOUNT,GID 1009)可以通过本地套接字向它们发送指令,而无须以超级用户执行。
  • Android守护进程的本地套接字创建在/dev/socket/目录中:
generic_x86:/system/bin # ls -l /dev/socket/
total 0
srw-rw---- 1 system      system       0 2020-06-03 10:13 adbd
drwxrwxr-x 2 audioserver audioserver 40 2020-06-03 10:13 audioserver
srw-rw---- 1 root        inet         0 2020-06-03 10:13 dnsproxyd
srw-rw---- 1 root        inet         0 2020-06-03 10:13 fwmarkd
...
srw-rw---- 1 root        system       0 2020-06-03 10:13 zygote
  • UNIX域套接字允许使用SCM_CREDENTIALS控制消息和SO_PEERCRED套接字选项,来传递和查询用户凭证,和有效UID和有效GUID是Binder事物处理的一部分一样,与本地套接字相关的凭证由内核进行检查,从而无法被用户进程所伪造,这允许本地守护进程实现对特定客户端操作进行额外的、细粒度的控制。
  • vold中基于套接字客户端凭证的细粒度访问控制:
int CommandListener::CryptfsCmd::runCommand(SocketClient* cli,int argc,char** argv)
{
	if((cli->getUid() !=0 ) && (cli->getUid()!= AID_SYSTEM))
	{
		cli->sendMsg(ResponseCode::CommandNoPermission,"No permission to run cryptfs commands",false);
		return 0;
	}
}
  • vold守护进程只允许以root或system运行的客户端,发送加密的容器管理指令,这里UID由SocketClient->getUid()方法返回。
  1. 动态权限执行
  • Android的核心是由一系列相互协作的系统服务实现的,它们可以被使用Binder IPC机制的其他进程调用,核心服务注册到服务管理器,任何应用程序,只要知道它们的注册名称,就可以获取一个Binder引用,因为Binder没有一个内置的访问控制机制,当客户端拥有一个引用时,它们可以通过向Binder.transact()传递适当的参数,调用任何系统服务的方法,因此,每个系统服务均需要实现访问控制机制。
  • 系统服务通过直接检查调用者UID,来控制调用者对该服务的导出操作的访问权限,其中调用者UID通过Binder.getCallingUid()方法获取。
  • 在Android中,每个应用的UID与某个包唯一相关,并且包管理器负责记录那些赋予每个包的权限,所以通过查询包管理器即可获取某个应用的权限。
  • PackageManagerService类中的相关代码:
    在这里插入图片描述
  • 这里PackageManagerService首先基于传入参数UID,获取应用的app ID,然后获取所受权限,如果GrantedPermission类包含目标权限,则该方法返回PERMISSION_GRANTED,如果不包含,则基于UID检查目标权限是否可以被自动分配(基于platform.xml文件中的<assign-permission>标签),如果这个检查也失败,那么最终返回PERMISSION_DENIED。
  1. 静态权限的执行
  • 当某个应用试图与另一个应用所声明的组件进行交互时,便会涉及静态权限执行,权限执行过程需要充分考虑每个目标组件声明的权限,然后允许那些有相应授权的调用进程与目标组件交互。
  • 当系统收到一个隐式intent时,它首先搜索相匹配的组件,如果匹配到不止一个组件,则向用户呈现一个选择对话框,目标组件选定以后,Android检查它是否具有相应的权限,如果有,检查这些权限是否已被授权给调用者。
  • 使用Binder.getCallingUid()和Binder.getCallingPid()获取调用者的UID和PID,然后调用者UID映射到包名,接下来获得相关权限,如果调用者权限集合中包含目标组件需要的权限,则组件启动,否者抛出SecurityException异常。
  • 权限检查由ActivityManagerService进行,它负责解析具体的intent,并检查目标组件是否拥有相关的权限属性,如果有,则它将权限检查工作移交给包管理器执行,不同组件,其权限检查的时机和具体顺序略有不同。

系统权限

  1. Android框架的核心是一组由系统服务共享的类,其中的一些会通过公开SDK暴露,框架类均被打包程JAR文件,保存在/system/framework/目录下。
  2. 出了JAR库,框架还包含一个单独的APK文件,framework-res.apk,主要是将框架资源打包在一起,它包含一个AndroidManifest.xml文件,在该文件内什么了权限组和权限。
    在这里插入图片描述
    在这里插入图片描述
  • 一些系统广播被声明为protected,并且只能由系统进程发送,这些系统进程必须运行在以下UID:SYSTEM_UID、PHONE_UID、SHELL_UID、BLUETOOTH_UID或root。如果以其他UID进行的进程试图发送一个protected广播,那么当它调用sendBroadcast()方法时,回收到SecurityException异常。
  • 一个权限组为一组相关的权限制定了一个名称,单个权限可以通过在permissionGroup属性里,指定组名称,来将该权限加到权限组里。
  • 权限组用于在系统用户界面显示一组相关联的权限,但每个权限依旧需要单独申请,也就说,应用程序不能将所有权限合在一起申请授权。
  • 每个权限使用protectionLevel属性来声明相应但保护级别。
  • 保护级别可以结合保护标志做更近一步的授权约束,当前定义的标志是system和development,system标志要求应用必须是系统镜像的一部分,方可授权(即安装在只读的system分区中的应用),例如MANAGE_USB权限,它允许应用程序管理USB设备的参数和权限,只会被授权给既使用平台密钥签名又安装在system分区中的应用,development表示开发权限。
  1. signature权限
  • 系统应用由平台密钥签发,默认情况下,当前Android源码树🈶️4个不同的密钥文件:platform、shared、media和testkey。
  • 所有核心平台的包(如系统界面、设置、电话、蓝牙等)均使用平台密钥签发,搜索和通讯录相关的包使用共享密钥签发,图库和媒体相关的包使用谋体密钥签发,其他所有应用使用发布密钥签发。
  • 定义系统权限的framework-res.apk文件是使用平台密钥签发的,因此任何试图请求signature保护界别系统权限的应用程序,均需要使用与框架资源包相同的密钥进行签名。
  1. development权限
  • 开发权限可以根据需要使用pm grant和pm revoke命令,进行授权或撤销。

共享用户ID

  1. 使用相同密钥签发的Android应用可以使用相同UID运行,并且也可以运行在同一进程内,这个特性就是共享用户ID,该特性被核心框架服务和系统应用广泛使用。
  2. 共享用户ID可以通过在在AndroidManifest.xml文件的根元素中添加sharedUserId属性开启,在manifest文件中指定的用户ID,必须是Java包的格式,并且向应用程序包名一样,作为一个标识符。
  3. 像一个已安装的应用的新版本添加sharedUserId属性,会造成它改变自身的UID,从而导致失去对自己文件的访问权限,因此,系统不允许这么做,系统会返回INSTALL_FAILED_UID_CHANGED错误,从而拒绝更新应用。
  4. 共享UID自身是系统包数据(/data/system/packages.xml)中的一级对象,作为应用一样对待,因此共享UID也有相应的签名证书和权限,Android内置了5个共享UID,其中android.uid.system定义如下:
 <shared-user name="android.uid.systemui" userId="10105">
        <sigs count="1" schemeVersion="3">
            <cert index="4" />
        </sigs>
        <perms>
            <item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
            <item name="android.permission.REMOTE_AUDIO_PLAYBACK" granted="true" flags="0" />
            ...
        </perms>
    </shared-user>
  • 作为共享用户一部分的包,并没有相关联的授权权限列表,包继承了共享用户的权限,这些权限集合了所有以相同共享UID安装包的申请权限。
  • 有共享UID的应用的包声明。
 <package name="com.android.keychain" codePath="/system/app/KeyChain" nativeLibraryPath="/system/app/KeyChain/lib" primaryCpuAbi="x86" publicFlags="805846597" privateFlags="0" ft="17141002608" it="17141002608" ut="17141002608" version="29" sharedUserId="1000" isOrphaned="true">
        <sigs count="1" schemeVersion="3">
            <cert index="4" />
        </sigs>
        <proper-signing-keyset identifier="2" />
    </package>

  • 所有需要允许在同一进程内的应用,可以在应用的manifest文件内的<application>标签下的process属性里,指定同一个进程名称来实现,显然这也可以通过共享内存来实现进程间通信,但一些系统服务还同时允许同一进程内的组件进行特殊访问控制。

自定义权限

  1. 自定义权限是那些第三方应用简单地进行声明地权限,权限声明以后,它们可以被加到应用地组件中,由系统进行静态权限执行,或者应用通过Context类地checkPermission()或enforcePermission()方法,动态检查调用者是否已经被授权。
  2. 自定义权限树、权限组和权限声明
    在这里插入图片描述
  • 和系统权限一样,如果权限地保护级别是normal或dangerours,那么自定义权限会在用户点击确认后自动授权,为了能够控制哪个应用被赋予自定义权限,需要将自定义权限声明为signature,以确保只有以同一个密钥签名地应用程序可以被赋予该权限。
  • 应用可以使用android.content.pm.PackageManager.addPermission()接口动态添加新的权限,使用removePermission()删除权限,动态添加地权限,必须属于应用定义的权限树,应用只能对自身或者共享同一UID的包,在权限树中添加、删除权限。
	PackageManager pm = getPackageManager();
	PermissionInfo permissionInfo = new PermissionInfo();
	permissionInfo.name  = "com.example.permission.PERMISSION2";
	permissionInfo.labelRes = R.string.permission_label;
	permissionInfo.protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
	boolean added = pm.addPermission(permissionInfo);
  • 动态添加的权限会被写进应用包数据库中(/data/system/packages.xml),它们在系统重启后还会存在,就像manifest内定义的权限一样,不过会多处一个type为dynamic的额外属性。

公开和私有组件

  1. AndroidManifest.xml文件中定义的组件可以是公开的,也可以是私有的,私有组件只能被声明的特定应用调用,而公开组件可以被其他所有应用调用。
  2. 组件可以通过设定exported属性为true,来使得组件公开;或通过什么一个intent filter隐私地使得组件公开,而具有intent filter的组件,可以设置exported为false,从而使得组件不公开,如果组件私有,那么从外部应用的调用均会被活动管理器所阻塞,而不论调用进程是否已被授权。
 <activity android:name=".SnackbarActivity" android:exported="false">
            <intent-filter>
                <action android:name="com.example.FETCH_DATA" />
            </intent-filter>
 </activity>
  1. activity和service权限
  • activity和service均可以通过设置目标组件的permission属性进行保护,当其他应用使用intent调用Context.startActivity()或text.startActivityForResult()启动某个activity时,这个activity的权限就会被检查,对于服务来说,当其他应用调用Context.startService()、stopService()或bindService()启动一个服务时,也会触发相应的权限检查。
  • 下面两个自定义权限START_MY_ACTIVITY和START_MY_SERVICE,分别设置在activity和service上,那些想要使用这些组件的应用,需要他们的manifest文件中使用<uses-permission>标签来申请相应的权限。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <permission android:name="com.example.permission.START_MY_ACTIVITY"
        android:protectionLevel="signature"
        android:label="activity_label"
        android:description="activity_label_des"/>
    <permission android:name="com.example.permission.START_MY_SERVICE"
        android:protectionLevel="signature"
        android:label="service_label"
        android:description="service_label_des"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MyActivity"
            android:label="my_activity"
            android:permission="com.example.permission.START_MY_ACTIVITY">
            <intent-filter>
            </intent-filter>
        </activity>
        <activity android:name=".MyService"
            android:label="my_activity"
            android:permission="com.example.permission.START_MY_SERVICE">
            <intent-filter>
            </intent-filter>
        </activity>
       ...
    </application>

</manifest>
  1. 广播权限
  • 与activity和service不同,广播接收者的权限可以由接收者自己指定,也可以由发送广播的应用指定,当发送一个广播时,应用可以使用Context.sendBroadcast(Intent intent)方法,它会将广播传递给所有已注册的接收者,如果要做限制的话,可以使用Context.sendBroadcast(Intent intent,String receiverPermission)方法,而receiverPermission参数指定了那些对该广播感兴趣的接收者的权限。
  • 接收者可以通过设置manifest文件中的<receiver>标签的permission属性,来限制谁可以给它们发送广播,这种方法叫做静态广播接收者,或者向Context.registerReceiver(BroadcastReceiver receiver,IntentFilter filter,String broadcastPermission,Handler scheduler)方法传递权限参数,这种叫作动态注册广播接收者。
  • 同其他组件一样,私有广播接收者只能接收源于同一应用的广播。
  1. 静态provider权限
  • 虽然可以使用单一的权限控制对整个provider的访问,但大部分provider针对读、写使用不同的权限,甚至还可以为每个URI指定各自但权限。

在这里插入图片描述

  • provider使用readPermission属性为读数据指定权限,使用writePermission属性为写数据指定另一种不同但权限,因此,拥有读数据权限但应用,可以调用provider的query()方法,拥有写数据权限的应用,可以调用insert()、update()和delete()方法,对于contact provider读写均有需求时,应用程序应同时具备两种权限。
  • 考虑到全局的读和写权限缺乏灵活性,provider可以指定per-URI权限,从而保护它们数据的一个特定子集,per-URI权限比组件级权限具有更高的优先级,因此如果一个应用想要访问一个有关联权限的content provider URI时,它需要具有目标URI的权限,而不需要拥有组件级的权限。
  1. 动态provider权限
  • 虽然静态定义的per-URI权限的功能非常强大,但应用有时需要在不拥有特定权限的情况下,临时授权其他应用访问一块特定数据。
  • 应用可以使用Context.grantUriPermission(String toPackage,Uri uri,int modeFlags)方法,动态地赋予per-URI临时访问权限,并且可以使用revokeUriPermission(Uri uri,int modelFlags)方法撤销授权,可以通过设置全局的grantUriPermission属性为true,来临时开启per-URI访问,或者为指定URL添加<grantUriPermission>属性进行开启.
    在这里插入图片描述
  • 应用很少使用grantUriPermission()和revokePermission()方法直接允许per-URI访问,而是通过设置启动协作应用的intent的FLAG_GRANT_READ_URI_PERMISSION或FLAG_GRANTED_WRITE_URI_PERMISSION标志,来达到目的,当这些标志被设置时,intent接收者会具备在该intent数据URI上,执行读写操作的权限。
  • Per-URI访问,可以使用intent的FLAG_GRANT_*类的标志之一进行授权,并且当被调用者应用程序的任务完成时,自动撤销,所以没有必要调用revokeUriPermission()方法。在这里插入图片描述

pending intent

  1. 它允许一个应用将自己的权限授权给另一个应用。
  2. pending intent封装了一个intent和执行它的一个目标动作,与常规intent的主要区别是,pending intent同样包含创建它的应用的身份标识,这允许pending intent可被移交给其他应用程序,这些程序可以利用原始应用的身份和权限,执行指定操作,存在与pending intent中的身份表示,由系统的ActivityManagerService服务赋予,它也用于记录当前活动的pending intent。
  3. pending intent在Android中用于实现闹钟和通知机制,闹钟和通知机制,允许应用指定一个需要以它名义执行的动作,可以是在一个指定的时间警报,或者当前要哦那个户与系统交互时通知。
  4. 收到pendingIntent实例的应用程序,可以使用与创建pending intent的应用同样的权限和身份,执行指定的操作,因此,应当注意的是,当构建基础的intent时,基础intent一般应尽量具体(有一个显式指定的组件名称),从而确保收到的intent是目标组件。
  5. pending intent的实现相当复杂,但是它跟其他Android组件一样,同样基于IPC和沙箱原理构建,当一个应用创建一个pending intent时,系统使用Binder.getCallingUid()和Binder.getCallingPid(),获取它的UID和PID。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值