[CM311-1A]-全网最全 Android 用户管理及用户应用权限

##################################################

目录

安卓系统用户管理

查询用户

查看用户列表

查看当前登录用户

添加用户

创建新用户

切换用户

切换账号

删除用户

删除一个账户

安卓系统用户应用权限

Android 是一个多用户系统

Android 用户机制

uid

gid

gids

查看 uid/gid/gids

应用的 appid 和 appuid

用户内部外部家目录

外部存储器 /storage/emulated/用户_UID 家目录

内部存储器 /data/user/用户_UID 家目录

Android 是如何创造出来一个虚拟的多用户运行环境的?

Android 多用户的本质

创建多用户流程解析

多用户切换流程解析

应用权限记录

安装时权限存储位置

运行时权限存储位置

权限声明存储位置

权限控制

授予/移除 权限

User ID 和 Group ID

利用 dumpsys package 从指定应用信息中获取 userid 和 gids

查看用户权限

查看应用权限

权限组详解

平台版本与 API 级别

CM311-1A 盒子 Android 9 所有已知的权限组

联系人权限组 android.permission-group.CONTACTS

电话权限组 android.permission-group.PHONE

日历权限组 android.permission-group.CALENDAR

通话记录权限组 android.permission-group.CALL_LOG

相机权限组 android.permission-group.CAMERA

身体传感器权限组 android.permission-group.SENSORS

位置信息权限组 android.permission-group.LOCATION

存储空间权限组 android.permission-group.STORAGE

系统控制权限组 androidlogic.permission-group.SYSTEM_CONTROL

麦克风权限组 android.permission-group.MICROPHONE

短信权限组 android.permission-group.SMS

CM311-1A 盒子和手机设备都没有看到 WIFI 网络相关的权限

三层 Android 权限详解

第三层 —— 系统权限以及软件安装权限真相

Android 底层映射为 Linux 权限

Android 应用程序权限机制

安装一个 APK 的详细过程

声明时权限 安全等级/protectionLevel 分类

第一层 —— 开发层 AndroidManifest.xml

AndroidManifest.xml 配置文件权限分类

AndroidManifest.xml 配置文件详解

第二层 —— 框架层 preferences.xml

示例一个 root 过的手机修改板子 sd 权限

CM311-1A 的 platform.xml 配置文件详解

platform.xml 对应的解析代码


##################################################

安卓系统用户管理

——————————

查询用户

%%%%%

查看用户列表

cmcc_jiangsu:/ # pm list users
Users:
        UserInfo{0:机主:13} running
cmcc_jiangsu:/ # 

%%%%%

查看当前登录用户

cmcc_jiangsu:/ # whoami
root
cmcc_jiangsu:/ # 


——————————

添加用户

        非常注意:

        多用户的创建、启动、停止等行为是系统级的

        因此只有具有 root、system 权限的进程才能操作

%%%%%

创建新用户

        语法如下:

pm create-user 用户名

        然后发现盒子竟然也不能创建用户!!!

cmcc_jiangsu:/ # pm create-user "user_1"
Error: couldn't create User.
1|cmcc_jiangsu:/ # 

        无奈只好换上旧手机:

C:\Users\byme>adb devices    /* USB 插上之后查看连接状态 */
List of devices attached
8OOXXOOXXxxooxxF        device


C:\Users\byme>adb root    /* 看来无法获得 root 权限 */
adbd cannot run as root in production builds

C:\Users\byme>adb shell
shell@GIONEE_G1605A:/ $ pm list users    /* 查看用户列表 */
Users:
        UserInfo{0:机主:13} running
shell@GIONEE_G1605A:/ $ whoami    /* 查看登陆账户 */
shell
shell@GIONEE_G1605A:/ $ pm create-user "ranchui"    /* 创建用户燃吹 */
Success: created user id 10
1|shell@GIONEE_G1605A:/ $ pm list users    /* 用户 UID 是 10 */
Users:
        UserInfo{0:机主:13} running
        UserInfo{10:ranchui:0}
shell@GIONEE_G1605A:/ $ 

        如果之前创建多了也没关系 删除多余的用户之后重启手机 UID 就会重新计算:

shell@GIONEE_G1605A:/ $ pm create-user "user"
Success: created user id 10
1|shell@GIONEE_G1605A:/ $ pm list users
Users:
        UserInfo{0:机主:13} running
        UserInfo{10:user:0}
shell@GIONEE_G1605A:/ $ 


——————————

切换用户

%%%%%

切换账号

        启动和切换用户语法如下:

am start-user USER_ID    /* 启用一个账户 */
am switch-user USER_ID    /* 切换到指定账户 */

        启动指定用户:

shell@GIONEE_G1605A:/ $ pm list users    /* 当前 ranchui 未上线 */
Users:
        UserInfo{0:机主:13} running
        UserInfo{10:ranchui:0}
shell@GIONEE_G1605A:/ $ am start-user 10    /* 根据 UID 启用用户 ranchui */
Success: user started
shell@GIONEE_G1605A:/ $ pm list users    /* 用户 ranchui 已经在线 */
Users:
        UserInfo{0:机主:13} running
        UserInfo{10:ranchui:10} running
shell@GIONEE_G1605A:/ $ 

        切换成指定用户:

shell@GIONEE_G1605A:/ $ am switch-user 10    /* 切换成 ranchui 用户 */
shell@GIONEE_G1605A:/ $ 

        正在切换用户:

[CM311-1A]-全网最全 Android 用户管理及用户应用权限_第1张图片

        然后就发现无法调试了:

shell@GIONEE_G1605A:/ $     /* 这边莫名退出去了 */
C:\Users\byme>adb shell    /* 查看连接还在 但是不支持调试 */
adb.exe: device unauthorized.
This adb server's $ADB_VENDOR_KEYS is not set
Try 'adb kill-server' if that seems wrong.
Otherwise check for a confirmation dialog on your device.

C:\Users\byme>adb kill-server    /* 关掉一次 adb */

C:\Users\byme>adb devices    /* 发现不是 device 是个未经授权的 unauthorized */
* daemon not running; starting now at tcp:5037
* daemon started successfully
List of devices attached
8ooxxOOOXXXooxxF        unauthorized


C:\Users\byme>

        然后手机竟然开始首次登陆配置:

[CM311-1A]-全网最全 Android 用户管理及用户应用权限_第2张图片

[CM311-1A]-全网最全 Android 用户管理及用户应用权限_第3张图片

[CM311-1A]-全网最全 Android 用户管理及用户应用权限_第4张图片

        这个新建立的用户确确实实不允许 USB 调试:

[CM311-1A]-全网最全 Android 用户管理及用户应用权限_第5张图片

        无奈 只好重启手机之后默认登陆主账户再次开启调试功能:

C:\Users\byme>adb devices    /* 此时还未授权 */
List of devices attached
8ooxxOOOXXXooxxF        unauthorized


C:\Users\byme>adb devices    /* 授权过后 */
List of devices attached
8ooxxOOOXXXooxxF        device


C:\Users\byme>


——————————

删除用户

%%%%%

删除一个账户

        语法如下:

pm remove-user USER_ID

        示例删除刚刚创建的 ranchui 用户:

C:\Users\byme>adb shell
shell@GIONEE_G1605A:/ $ pm list users    /* 查看用户列表 */
Users:
        UserInfo{0:机主:13} running
        UserInfo{10:ranchui:10}
shell@GIONEE_G1605A:/ $ pm remove-user 10    /* 根据 UID 删除用户 */
Success: removed user
shell@GIONEE_G1605A:/ $ pm list users    /* 再次查看用户列表 */
Users:
        UserInfo{0:机主:13} running
shell@GIONEE_G1605A:/ $ am start-user 10    /* 因为没有该账户所以报错 */
Error: could not start user
shell@GIONEE_G1605A:/ $ am switch-user 10    /* 没有该账户所以屏幕也没切换过去 */
shell@GIONEE_G1605A:/ $

##################################################

安卓系统用户应用权限

——————————

Android 是一个多用户系统

        Android 4.0 开始 Google 就开始在 Android 上布局多用户
        UserManager 由此诞生 然而此时尚未对应的 Binder 服务 真正支持多用户是从 Android 4.2 开始
        即使如此系统中也依然存在各类 Bug 和兼容性问题

        直到 Android 6.0 多用户才比较完善

        国内外的厂家也纷纷开始针对多用户这个噱头来作各类 花里胡哨 的操作

手机分身

分身应用

应用双开

        等等等等 不得不说 国内的厂家在多用户这方面定制化到现在已经很是稳定和完善了

——————————

Android 用户机制

        对于 Android 中的每一个进程都有一个单独的 uid、gid 以及 gids 集合
        经过这三者 Android 系统实现了一套文件和数据访问权限规则系统

        例如 访问某个文件 文件系统规定了该文件在磁盘中的 rwx 权限

%%%%%

uid

        UID 是用户 id

        在 Linux 上一个用户 uid 标识着一个给定的用户 而 Android 上也沿用了 Linux 用户的概念

root 用户 uid 为 0
system Uid 为 1000

    Android 在创建每个用户时 都会分配一个整型的 userId
    对于主用户 就是正常下的默认用户 来说 userId 为 0
    之后创建的 userId 将从 10 开始计算 每增加一个用户 userId 就会加 1

        而且每一个应用程序在安装时也被赋予了单独的 uid
        这个 uid 将伴随着应用从安装到卸载包括缓存

%%%%%

gid

        GID 是用户组 id

Linux 上规定每一个应用都应该有一个用户组
对于 Android 应用程序来讲每一个应用的所属用户组与 uid 相同

%%%%%

gids

        GIDS 是应用在安装后所得到权限的 id 集合

在 Android 上每一个权限均可能对应一个或多个 group 而每一个 group 都有一个 gid name
所以 gids 就是经过对每一个 gid name 计算得出的 id 集合

    一个 uid 能够关联 gids 代表该 uid 拥有多种权限

%%%%%

查看 uid/gid/gids


        获取 system_server 的 PID 然后根据这个 PID 查看 UID/GID/Groups :

cmcc_jiangsu:/ # ps -A | grep system_server    /* 得到 PID 为 3620 */
system        3620  2604 1386312 181192 SyS_epoll_wait      0 S system_server
cmcc_jiangsu:/ # cat /proc/3620/status    /* 查看该进程的 status */
Name:   system_server
Umask:  0077
State:  S (sleeping)
Tgid:   3620
Ngid:   0
Pid:    3620
PPid:   2604
TracerPid:      0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
FDSize: 512
Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 10
32 1065 3001 3002 3003 3006 3007 3009 3010
VmPeak:  1419816 kB
VmSize:  1386312 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:    206464 kB
VmRSS:    181088 kB
RssAnon:           15028 kB
RssFile:          165708 kB
RssShmem:            352 kB
VmData:  1018244 kB
VmStk:      8192 kB
VmExe:        24 kB
VmLib:     80572 kB
VmPTE:       524 kB
VmPMD:         0 kB
VmSwap:        0 kB
Threads:        97
SigQ:   0/10948
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000001204
SigIgn: 0000000000000001
SigCgt: 00000006400084f8
CapInh: 0000001806897c20
CapPrm: 0000001806897c20
CapEff: 0000001806897c20
CapBnd: 0000000000000000
CapAmb: 0000000000000000
Seccomp:        0
Speculation_Store_Bypass:       unknown
Cpus_allowed:   f
Cpus_allowed_list:      0-3
Mems_allowed:   1
Mems_allowed_list:      0
voluntary_ctxt_switches:        2526
nonvoluntary_ctxt_switches:     1818
cmcc_jiangsu:/ # 

——————————

应用的 appid 和 appuid

        在 Android 中应用的 uid 是和当前的用户有关的

    同一个应用具有相同的 appId

        其 uid 的计算方式为:

appUid = userId * 1000000 + appId

        在主用户中 uid 就等于 appId

——————————

用户内部外部家目录

        为了多用户下的数据安全性

        在每个新用户创建之初不管是 外部存储器/External Storage 还是 app data 目录

         Android 都为其准备了独立的文件存储

        新用户创建时 Android 在

/storage/emulated

/data/user

        目录下为每个用户都创建了名为用户 id 的家目录

%%%%%

外部存储器 /storage/emulated/用户_UID 家目录

        目录 /storage/emulated/ 下拥有不同的用户分区

        当我们在代码中使用

Environment.getExternalStorageDirectory().absolutePath

        获取外部存储路径时返回的就是当前用户下的对应目录

        例如 userId = 11 则返回为

/storage/emulated/11

        多用户下的 /storage 分区有不同的用户家目录

        可以看出常用的 /sdcard 目录其实最终也是软链到了 /storage/emulated/0 目录:

cmcc_jiangsu:/ # ls -l /storage/emulated/
total 8
drwxrwx--x 32 root sdcard_rw 4096 2022-07-25 11:35 0
drwxrwx--x  2 root sdcard_rw 4096 2015-01-01 08:00 obb
cmcc_jiangsu:/ # ls -l /sdcard
lrwxrwxrwx 1 root root 21 1970-01-01 08:00 /sdcard -> /storage/self/primary
cmcc_jiangsu:/ # ls -l /storage/self/primary
lrwxrwxrwx 1 root root 19 2015-01-01 08:00 /storage/self/primary -> /mnt/user/0/primary
cmcc_jiangsu:/ # ls -l /mnt/user/0/primary
lrwxrwxrwx 1 root reserved_disk 19 2015-01-01 08:00 /mnt/user/0/primary -> /storage/emulated/0
cmcc_jiangsu:/ # 

%%%%%

内部存储器 /data/user/用户_UID 家目录

        多用户下的 /data 分区也有不同用户的家目录

        也是以用户 UID 命名

        与 External Storage/外部存储器 相同
        新用户创建时 Android 也会在 /data/user 目录下创建名为 userId 的目录
        用于存储该用户中所有 App 的隐私数据

    如果在代码中使用 Context.getFilesDir() 来获取应用的 data 目录不同 User 下也会有不同

        也可以看出来平常说到的 /data/data 目录其实也是软链到了 /data/user/0

cmcc_jiangsu:/ # ls /data/user -alh
total 6.0K
drwx--x--x  2 system system 4.0K 2015-01-01 08:00 .
drwxrwx--x 40 system system 4.0K 2015-01-01 08:00 ..
lrwxrwxrwx  1 root   root     10 2015-01-01 08:00 0 -> /data/data
cmcc_jiangsu:/ # ls -l /data/user/0/
android.ext.services/                   com.android.sharedstoragebackup/
android.ext.shared/                     com.android.shell/
android/                                com.android.statementservice/
com.android.backupconfirm/              com.android.superuser/
com.android.bluetooth/                  com.android.systemui/
com.android.certinstaller/              com.android.dialogs/
com.android.companiondevicemanager/     com.android.webview/
com.android.defcontainer/               com.cmcc.mid.softdetector/
com.android.externalstorage/            com.dangbei.tvlauncher/
com.android.inputdevices/               com.dangbeimarket/
com.android.inputmethod.latin/          com.droidlogic.BluetoothRemote/
com.android.keychain/                   com.droidlogic.inputmethod.remote/
com.android.location.fused/             com.droidlogic/
com.android.managedprovisioning/        com.fengyun.live/
com.android.packageinstaller/           com.iflytek.bt.auto/
com.android.pacprocessor/               com.iflytek.xiri/
com.android.providers.downloads/        com.iflytek.xiri2.system/
com.android.providers.media/            com.mylejia.store/
com.android.providers.settings/         com.tv.kuaisou/
com.android.proxyhandler/               com.xiaodianshi.tv.yst/
com.android.se/                         jackpal.androidterm/
com.android.settings/                   me.thomastv.rebootupdate/
cmcc_jiangsu:/ # ls -lh /data/user/0/com.android.shell/
total 4.0K
drwxrws--x 2 shell shell 4.0K 2015-01-01 08:00 cache
drwxrws--x 2 shell shell 4.0K 2015-01-01 08:00 code_cache
cmcc_jiangsu:/ # 

——————————

Android 是如何创造出来一个虚拟的多用户运行环境的?

%%%%%

Android 多用户的本质

        多用户其实是系统为应用的 data 目录和 storage 目录分配了一份不同且独立的存储空间

        不同用户下的存储空间互不影响且没有权限访问

        /storage 目录不可以跨用户访问
例如用户 10 的 app 是无法访问 /storage/emulated/0 下的文件的
        是不可以互相访问的

        同时系统中的 AMS、PMS、WMS 等各大服务都会针对 userId/UserHandle 进行多用户适配

        并在用户启动、切换、停止、删除等生命周期时做出相应策略的改变

%%%%%

创建多用户流程解析

        多用户的创建流程主要在

UserManagerService.createUserInternalUnchecked()

        函数中完成

        用户创建的过程主要是应用运行环境例如文件系统、权限等的准备过程

        主要可以分为六个步骤

        第一步 为新用户创建一个新的 userId

        新用户的 userId 从 10 开始递增

        第二步 固化新用户信息和创建状态

        构造包含新用户信息的 UserData 并固化到 /data/system/users/${userId}.xml 中:

cmcc_jiangsu:/ # cat /data/system/users/0.xml


    

cmcc_jiangsu:/ # 

        将新创建新 userId 固化到 /data/system/users/userlist.xml 中:

cmcc_jiangsu:/ # cat /data/system/users/userlist.xml


    
        
    
    
    

cmcc_jiangsu:/ # 

        第三步 准备文件系统

        通过 vold 这个 Android 存储守护进程为新用户进行文件系统加密
        创建 /data/system/users/${userId} 并设置 0700 权限

cmcc_jiangsu:/ # ls -alh /data/system/users/
total 10K
drwxrwxr-x  3 system system 4.0K 2015-01-01 08:00 .
drwxrwxr-x 19 system system 4.0K 2022-07-27 19:29 ..
drwx------  2 system system 4.0K 2022-07-27 19:20 0
-rw-------  1 system system  298 2015-01-01 08:00 0.xml
-rw-------  1 system system  335 2015-01-01 08:00 userlist.xml
cmcc_jiangsu:/ # 

        创建 /data/misc/users/${userId} 并设置 0750 权限

cmcc_jiangsu:/ # ls /data/misc/user/ -alh
total 6.0K
drwxrwx--x  3 root   root      4.0K 2015-01-01 08:00 .
drwxrwx--t 43 system misc      4.0K 2015-01-01 08:00 ..
drwxr-x---  2 system everybody 4.0K 2015-01-01 08:00 0
cmcc_jiangsu:/ # 

        第四步 为已安装应用准备数据目录并记录其组件和默认权限配置

        在 /data/user/${userId}/ 下创建各个已安装应用的 package 目录

cmcc_jiangsu:/ # ls /data/user/0/
android                            com.android.sharedstoragebackup
android.ext.services               com.android.shell
android.ext.shared                 com.android.statementservice
com.android.backupconfirm          com.android.superuser
com.android.bluetooth              com.android.systemui
com.android.certinstaller          com.android.dialogs
com.android.companiondevicemanager com.android.webview
com.android.defcontainer           com.cmcc.mid.softdetector
com.android.externalstorage        com.dangbei.tvlauncher
com.android.inputdevices           com.dangbeimarket
com.android.inputmethod.latin      com.droidlogic
com.android.keychain               com.droidlogic.BluetoothRemote
com.android.location.fused         com.droidlogic.inputmethod.remote
com.android.managedprovisioning    com.fengyun.live
com.android.packageinstaller       com.iflytek.bt.auto
com.android.pacprocessor           com.iflytek.xiri
com.android.providers.downloads    com.iflytek.xiri2.system
com.android.providers.media        com.mylejia.store
com.android.providers.settings     com.tv.kuaisou
com.android.proxyhandler           com.xiaodianshi.tv.yst
com.android.se                     jackpal.androidterm
com.android.settings               me.thomastv.rebootupdate
cmcc_jiangsu:/ # 

        在 /data/system/users/${userId}/package-restrictions.xml 中写入非默认启动组件的信息

cmcc_jiangsu:/ # cat /data/system/users/0/package-restrictions.xml


    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
        
            
        
    
    
    
    
    
    
    
    
    
    
        
            
        
    
    
    
    
    
    
    
    

cmcc_jiangsu:/ # 

        更新 /data/system/packages.list 文件主要是最后一串 gids 可能会改变

cmcc_jiangsu:/ # cat /data/system/packages.list
com.iflytek.xiri 10021 0 /data/user/0/com.iflytek.xiri default:targetSdkVersion=19 3002,3003,3001
com.droidlogic.inputmethod.remote 10022 0 /data/user/0/com.droidlogic.inputmethod.remote default:targetSdkVersion=28 none
com.xiaodianshi.tv.yst 10032 0 /data/user_de/0/com.xiaodianshi.tv.yst default:targetSdkVersion=26 3003
com.android.providers.media 10002 0 /data/user/0/com.android.providers.media media:privapp:targetSdkVersion=28 2001,1065,1023,1015,3003,3007,1024
com.mylejia.store 10026 0 /data/user_de/0/com.mylejia.store default:targetSdkVersion=24 3003,1007
com.android.externalstorage 10004 0 /data/user/0/com.android.externalstorage platform:privapp:targetSdkVersion=28 1023,1015
com.android.companiondevicemanager 10012 0 /data/user/0/com.android.companiondevicemanager default:targetSdkVersion=28 3002,3001
com.android.providers.downloads 10002 0 /data/user/0/com.android.providers.downloads media:privapp:targetSdkVersion=28 2001,1065,1023,1015,3003,3007,1024
com.fengyun.live 10027 0 /data/user_de/0/com.fengyun.live default:targetSdkVersion=24 3003
com.droidlogic 1000 0 /data/user/0/com.droidlogic platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
com.dangbei.tvlauncher 10016 0 /data/user/0/com.dangbei.tvlauncher default:targetSdkVersion=22 3002,3003,3001
com.android.defcontainer 10000 0 /data/user_de/0/com.android.defcontainer platform:privapp:targetSdkVersion=28 2001
com.android.pacprocessor 10017 0 /data/user/0/com.android.pacprocessor platform:targetSdkVersion=28 3003
com.android.certinstaller 10013 0 /data/user/0/com.android.certinstaller platform:targetSdkVersion=28 none
me.thomastv.rebootupdate 10024 0 /data/user_de/0/me.thomastv.rebootupdate default:targetSdkVersion=25 none
android 1000 0 /data/system platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
com.android.backupconfirm 10001 0 /data/user/0/com.android.backupconfirm platform:privapp:targetSdkVersion=28 none
com.android.statementservice 10009 0 /data/user/0/com.android.statementservice default:privapp:targetSdkVersion=28 3003
com.android.superuser 10019 1 /data/user/0/com.android.superuser default:targetSdkVersion=22 none
com.android.providers.settings 1000 0 /data/user_de/0/com.android.providers.settings platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
com.android.sharedstoragebackup 10007 0 /data/user/0/com.android.sharedstoragebackup platform:privapp:targetSdkVersion=28 1023,1015
com.iflytek.xiri2.system 1000 0 /data/user/0/com.iflytek.xiri2.system platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
com.android.webview 10023 0 /data/user/0/com.android.webview default:targetSdkVersion=28 3003
com.android.se 1068 0 /data/user/0/com.android.se platform:privapp:targetSdkVersion=28 none
com.android.inputdevices 1000 0 /data/user_de/0/com.android.inputdevices platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
com.droidlogic.BluetoothRemote 1000 0 /data/user/0/com.droidlogic.BluetoothRemote platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
android.ext.shared 10015 0 /data/user_de/0/android.ext.shared platform:targetSdkVersion=28 none
com.android.keychain 1000 0 /data/user/0/com.android.keychain platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
android.ext.services 10003 0 /data/user_de/0/android.ext.services platform:privapp:targetSdkVersion=28 none
com.android.packageinstaller 10008 0 /data/user_de/0/com.android.packageinstaller platform:privapp:targetSdkVersion=28 1065
com.android.proxyhandler 10006 0 /data/user_de/0/com.android.proxyhandler platform:privapp:targetSdkVersion=28 3003
com.android.inputmethod.latin 10020 0 /data/user_de/0/com.android.inputmethod.latin default:targetSdkVersion=23 none
com.android.managedprovisioning 10005 0 /data/user/0/com.android.managedprovisioning platform:privapp:targetSdkVersion=28 3003
jackpal.androidterm 10031 0 /data/user_de/0/jackpal.androidterm default:targetSdkVersion=22 3003
com.iflytek.bt.auto 10014 0 /data/user/0/com.iflytek.bt.auto default:targetSdkVersion=28 3002,3001
com.android.settings 1000 1 /data/user/0/com.android.settings platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
com.android.dialogs 10010 0 /data/user/0/com.android.dialogs platform:privapp:targetSdkVersion=28 none
com.android.shell 2000 0 /data/user_de/0/com.android.shell platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,1002,3010,3011
com.dangbeimarket 10025 0 /data/user_de/0/com.dangbeimarket default:targetSdkVersion=19 3002,3003,3001
com.android.location.fused 1000 0 /data/user_de/0/com.android.location.fused platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3003,3001,1007
com.android.systemui 10011 0 /data/user_de/0/com.android.systemui platform:privapp:targetSdkVersion=28 1065,3002,1023,1015,3001,3006
com.tv.kuaisou 10028 0 /data/user_de/0/com.tv.kuaisou default:targetSdkVersion=22 3002,3003,3001
com.android.bluetooth 1002 0 /data/user_de/0/com.android.bluetooth platform:privapp:targetSdkVersion=28 3002,3003,3001,3007,1002,3010,3011,3005,1016
com.cmcc.mid.softdetector 10018 0 /data/user/0/com.cmcc.mid.softdetector default:targetSdkVersion=17 3003
cmcc_jiangsu:/ # 

        这个改变的可能性是根据 permUser 的配置来决定

        第五步 固化新用户创建完成的状态、通知 PMS 为新用户和应用赋予默认的权限

        第六步 发送 ACTION_USER_ADDED 广播 新用户创建完成

%%%%%

多用户切换流程解析


        Android 多用户的切换函数入口是

ActivityManagerService.switchUser()

        AMS 的 startUser 方法只是判断了是否展示切换用户的 Dialog
        最终都会调用到 UserController.startUser 这个函数中
        方法很长 涉及到 AMS 和 WMS 的方法分支也很多

        切换分为前台切换和后台切换 这里从前台切换 并且对用户未启动的情况总结下关键的切换过程

        第一步 切换前冻结屏幕 禁止一切输入操作

这个过程在屏幕旋转的过程中也会执行 因此截取屏幕并展示也是采用和横竖屏切换一样的方式

        冻结输入事件
        强制结束 App 动画
        截取当前屏幕并显示

        如果是待启动用户 则初始化待启动用户的状态为 STATE_BOOTING

        第二步 为待切换用户更改系统配置 设置 Keyguard

从 SettingsProvider 读取待切换用户的字体、语言、地区等配置并更新到系统

        如果是初创用户 则字体使用默认配置 语言和地区使用当前用户的配置

为待切换用户更新资源 如 Attributes、Drawable、Color、Animator、StateList 等

        有兴趣可以重点看下 AMS 的 updateGlobalConfiguration()

        修改当前用户下所有 Window 的可见性 启动 Keyguard 切换过程中关闭 Keyguard 的指纹监听 并设置锁屏

            在 Android 8.0 以前 Keyguard 是一个单独的 System App
        8.0 后将其移至 SystemUI 中 该模块的功能主要有 展示和隐藏锁屏界面 认证和校验锁屏密码、指纹密码 等

        如果是待启动用户 为待启动用户设置权限 校验或准备待启动用户的 App 存储目录
        通知系统所有服务新用户正在启动 如 JobSchedulerService 会根据 Job 对应的用户是否启动来决定 Job 的维护

        第三步 并行通知系统所有服务用户开始切换

        系统所有服务及相关监听者在收到开始切换的消息后进行一系列的操作

        也是用户切换所要完成的核心任务

        所有系统服务及相关监听者完成切换任务后 执行 UserController.continueUserSwitch()

        第四步 设置切换超时定时器

        设置 3s 的延迟消息

        如果 3s 内没有完成用户切换 则取消该消息 终止切换过程并执行 UserController.continueUserSwitch()

        第五步 将待切换用户拉到前台

        stop 当前用户下所有的 Activity

        修改所有 ActivityStack 中 TaskRecord 的顺序 将切换用户或者在两个用户中都能运行的 Task 移动到栈顶

        将最顶端 Task 对应的 Window 移动到最顶端

        取出切换应用之前存在的前台 Activity 置于前台并 resume 如果没有前台应用 则启动 HomeActivity

        发送用户切换广播

        如果是后台切换 则发送 ACTION_USER_BACKGROUND
        如果是前台切换 则发送 ACTION_USER_FOREGROUND 和 ACTION_USER_SWITCHED

        第六步 切换超时消息到达时需要继续进行的切换操作

    在
设置切换超时定时器
    和
将待切换用户拉到前台
    中切换超时消息到达时需要继续进行的切换操作 continueUserSwitch

        解冻屏幕和输入


        设置 Keyguard 如果切换用户设置了指纹 则需要开始监听指纹信息

        通知监听者用户已经完成了切换

        第七步 完成切换用户

        如果是后台切换

        则直接调用 UserController.finishUserBoot()

        如果是前台切换

        ActivityThread 会在 handleResumeActivity 时设置 Main 线程 MessageQueue 的 mIdleHandlers

        在 MessageQueue 执行 next() 会检查该列表并最终调用到 AMS 的 activityIdle() 中

        此时会检查正在切换的用户列表并调用最终调用到 UserController.finishUserBoot()

        设置切换用户的状态为 STATE_RUNNING_LOCKED

	如果是新启动的用户,则通知系统所有用户监听者用户已经启动
	并发送 ACTION_LOCKED_BOOT_COMPLETED 广播
	在 Keyguard 第一次解锁时 会发送 ACTION_BOOT_COMPLETED 广播

——————————

应用权限记录

%%%%%

安装时权限存储位置

        安装 APP 时权限的获取记录存储在

/data/system/packages.xml

cmcc_jiangsu:/ # cat /data/system/packages.xml | grep xiaodianshi    /* 查看关于 bilibili tv 的权限 */
        
    
            
cmcc_jiangsu:/ # cat /data/system/packages.xml | grep jackpal.androidterm
        
        
        
    
cmcc_jiangsu:/ # pm list packages -3    /* 查看第三方应用 */
package:com.xiaodianshi.tv.yst
package:com.mylejia.store
package:com.fengyun.live
package:me.thomastv.rebootupdate
package:jackpal.androidterm
package:com.dangbeimarket
package:com.tv.kuaisou
cmcc_jiangsu:/ # cat /data/system/packages.xml  | grep com.dangbeimarket    /* 查看当贝市场权限 */
        
        
        
    
            
            
            
cmcc_jiangsu:/ # 

%%%%%

运行时权限存储位置

        运行时权限的获取记录存储在

/data/system/users/$userId/runtime-permissions.xml

cmcc_jiangsu:/ # cat /data/system/users/0/runtime-permissions.xml


cmcc_jiangsu:/ # 

%%%%%

权限声明存储位置

        Android 系统和应用安装后的权限声明保存在

/etc/permissions/

        目录下:

cmcc_jiangsu:/ # ls /etc/permissions/
android.hardware.bluetooth.xml    com.android.media.remotedisplay.xml
android.hardware.bluetooth_le.xml com.android.mediadrm.signer.xml
android.hardware.hdmi.cec.xml     platform.xml
android.software.webview.xml      privapp-permissions-platform.xml
com.android.location.provider.xml
cmcc_jiangsu:/ # 

        如果要查看最常使用的 platform 权限可以:

cat /etc/permissions/platform.xml | more

        可以简单看一下这个配置文件 每个我们常见的权限都可能对应一个或多个 group gid

        而我们上面说的 gids 就是由这个 group gid 生成的集合

——————————

权限控制

%%%%%

授予/移除 权限

        语法如下:

pm grant 包名 权限    /* 授予权限 */
pm revoke 包名 权限    /*  移除权限*/

        示例为终端模拟器添加删除写文件权限:

cmcc_jiangsu:/ # pm grant jackpal.androidterm android.permission.WRITE_EXTERNAL_STORAGE
cmcc_jiangsu:/ # pm revoke jackpal.androidterm android.permission.WRITE_EXTERNAL_STORAGE
cmcc_jiangsu:/ # 

        其她常用权限:

android.permission.CAMERA	/* 相机权限 */
android.permission.READ_EXTERNAL_STORAGE	/* 读文件权限 */
android.permission.WRITE_EXTERNAL_STORAGE	/* 写文件权限 */

%%%%%

User ID 和 Group ID

        Android 系统上每一个独立的应用运行在不同的系统空间
        以 User ID 和 Group ID 来标识
        不同应用之间互相访问数据接口资源就牵涉到权限问题

        关于用户 ID

    我们在安装一个应用的时候系统就会为这个应用分配一个 userid
这个 userid 是全局唯一的
	在同一 Android 系统的机器上不可能存在两个相同 userid 的应用
	也就是说 userid 对于一个应用来说是唯一不变的 除非你卸载重新安装了该应用
但是 userid 并不像 MAC 地址一样是全球唯一的 同一个应用在不同的设备上 userid 可能会不同
	这里所说的唯一是指在同一个 Android 设备上

	Android 的安全机制是进程级别的
	通常情况下一个应用就是一个进程
	并且一个应用也只有一个进程
	当然应用内实现多进程这个是没有任何问题的
	这里是说一般情况下
应用 A 是无法直接运行在应用 B 的进程当中的

	但是有一点就是除非应用 A 和应用 B 使用了相同的 shareduserid
	配置了相同的 shareduserid 的应用被系统视作同一个应用 对应的 userid 和权限都是相同的
	但是如果仅仅是配置了相同的 shareduserid 就能够到达目的 显然这是一个很大的漏洞
	为此谷歌做了另外一个限制 就是必须保证签名一致
	一般的 A 公司的签名肯定是跟 B 公司的签名是不一样的
	也就是说想要应用 A 和应用 B 运行在同一个进程当中那么肯定得保证签名和 shareduserid 是一致的
	shareduserid 保持一致这个很容易满足 但是签名一致恐怕只能是同一个公司

	应用的数据和 user id 是相对应的 默认情况下其她应用是无法访问的
	但是如果我们设置了 MODE_WORLD_READABLE 或者 MODE_WORLD_WRITEABLE 的 flag
	那么其她应用就可以变得可读可写了 也就是说这些数据就变成了全局的数据

	我们知道 Android 系统从 4.2 版本开始支持多用户
	也就产生了用户 id 的概念 uid
	在这里 uid 和 userid 是两个完全不同的概念
	android 中的用户是存在物理文件级数据差异的
	例如有两个用户 用户 id 分别是 0 和 10
	用户 10 安装了应用 A 此时对于用户 0 来说是完全不可见的
	不像两个应用 A 和 B 两者的数据虽然默认情况下是不能互相访问的 但是我们有办法能够实现

        那么用户 id 到底是什么:

简单的说用户 id 就是当前用户下为了各个应用之间数据共享和访问的

        在 Android 系统中有些常用的 userid 是提前定义好的

        例如 system 的用户 id 就是 1000 这个是在代码中提前定义好的

%%%%%

利用 dumpsys package 从指定应用信息中获取 userid 和 gids

        示例使用 dumpsys package 命令获取 终端模拟器 这个应用的所有信息:

cmcc_jiangsu:/ # dumpsys package jackpal.androidterm
Activity Resolver Table:
  Full MIME Types:
      */*:
        6eb5070 jackpal.androidterm/.TermHere filter 85f0431
          Action: "android.intent.action.SEND"
          Category: "android.intent.category.DEFAULT"
          Type: "*"
          mPriority=0, mOrder=0, mHasPartialTypes=true
        b538fe9 jackpal.androidterm/.shortcuts.FSNavigator filter e12d733
          Action: "android.intent.action.GET_CONTENT"
          Category: "android.intent.category.DEFAULT"
          Category: "android.intent.category.OPENABLE"
          Type: "*"
          mPriority=0, mOrder=0, mHasPartialTypes=true

  Wild MIME Types:
      *:
        6eb5070 jackpal.androidterm/.TermHere filter 85f0431
          Action: "android.intent.action.SEND"
          Category: "android.intent.category.DEFAULT"
          Type: "*"
          mPriority=0, mOrder=0, mHasPartialTypes=true
        b538fe9 jackpal.androidterm/.shortcuts.FSNavigator filter e12d733
          Action: "android.intent.action.GET_CONTENT"
          Category: "android.intent.category.DEFAULT"
          Category: "android.intent.category.OPENABLE"
          Type: "*"
          mPriority=0, mOrder=0, mHasPartialTypes=true

  Non-Data Actions:
      jackpal.androidterm.RUN_SHORTCUT:
        3602f82 jackpal.androidterm/.RunShortcut filter 45c4897
          Action: "jackpal.androidterm.RUN_SHORTCUT"
          Category: "android.intent.category.DEFAULT"
      jackpal.androidterm.private.SWITCH_WINDOW:
        65c1ce jackpal.androidterm/.TermInternal filter 2d8abbb
          Action: "jackpal.androidterm.private.SWITCH_WINDOW"
          Category: "android.intent.category.DEFAULT"
      jackpal.androidterm.OPEN_NEW_WINDOW:
        d877d32 jackpal.androidterm/.RemoteInterface filter 83648d8
          Action: "jackpal.androidterm.OPEN_NEW_WINDOW"
          Category: "android.intent.category.DEFAULT"
      android.intent.action.MAIN:
        6ee9b65 jackpal.androidterm/.Term filter 8b301b5
          Action: "android.intent.action.MAIN"
          Category: "android.intent.category.LAUNCHER"
          Category: "android.intent.category.MULTIWINDOW_LAUNCHER"
        c0f203a jackpal.androidterm/.shortcuts.AddShortcut filter 5331a84
          Action: "android.intent.action.MAIN"
      android.intent.action.PICK:
        b538fe9 jackpal.androidterm/.shortcuts.FSNavigator filter 34d5ea2
          Action: "android.intent.action.PICK"
          Category: "android.intent.category.DEFAULT"
      jackpal.androidterm.RUN_SCRIPT:
        c6dd21d jackpal.androidterm/.RunScript filter 2a1e716
          Action: "jackpal.androidterm.RUN_SCRIPT"
          Category: "android.intent.category.DEFAULT"
      jackpal.androidterm.private.OPEN_NEW_WINDOW:
        65c1ce jackpal.androidterm/.TermInternal filter 5bb104a
          Action: "jackpal.androidterm.private.OPEN_NEW_WINDOW"
          Category: "android.intent.category.DEFAULT"
      android.intent.action.CREATE_SHORTCUT:
        c0f203a jackpal.androidterm/.shortcuts.AddShortcut filter eeeb26d
          Action: "android.intent.action.CREATE_SHORTCUT"

  MIME Typed Actions:
      android.intent.action.SEND:
        6eb5070 jackpal.androidterm/.TermHere filter 85f0431
          Action: "android.intent.action.SEND"
          Category: "android.intent.category.DEFAULT"
          Type: "*"
          mPriority=0, mOrder=0, mHasPartialTypes=true
      android.intent.action.GET_CONTENT:
        b538fe9 jackpal.androidterm/.shortcuts.FSNavigator filter e12d733
          Action: "android.intent.action.GET_CONTENT"
          Category: "android.intent.category.DEFAULT"
          Category: "android.intent.category.OPENABLE"
          Type: "*"
          mPriority=0, mOrder=0, mHasPartialTypes=true

Service Resolver Table:
  Non-Data Actions:
      jackpal.androidterm.action.START_TERM.v1:
        3f40e99 jackpal.androidterm/.TermService filter f7579ec
          Action: "jackpal.androidterm.action.START_TERM.v1"
          Category: "android.intent.category.DEFAULT"

Permissions:
  Permission [jackpal.androidterm.permission.PREPEND_TO_PATH] (40775e5):
    sourcePackage=jackpal.androidterm
    uid=10031 gids=null type=0 prot=dangerous
    perm=Permission{13728ba jackpal.androidterm.permission.PREPEND_TO_PATH}
    packageSetting=PackageSetting{cc1336b jackpal.androidterm/10031}

Permissions:
  Permission [jackpal.androidterm.permission.RUN_SCRIPT] (9ec705e):
    sourcePackage=jackpal.androidterm
    uid=10031 gids=null type=0 prot=dangerous
    perm=Permission{a9cac3f jackpal.androidterm.permission.RUN_SCRIPT}
    packageSetting=PackageSetting{cc1336b jackpal.androidterm/10031}

Permissions:
  Permission [jackpal.androidterm.permission.APPEND_TO_PATH] (13fd63a):
    sourcePackage=jackpal.androidterm
    uid=10031 gids=null type=0 prot=dangerous
    perm=Permission{65012eb jackpal.androidterm.permission.APPEND_TO_PATH}
    packageSetting=PackageSetting{cc1336b jackpal.androidterm/10031}

Key Set Manager:
  [jackpal.androidterm]
      Signing KeySets: 17

Packages:
  Package [jackpal.androidterm] (cc1336b):
    userId=10031
    pkg=Package{ab23cd9 jackpal.androidterm}
    codePath=/data/app/jackpal.androidterm-sAgHElvcbHuZtUuQdCSIdQ==
    resourcePath=/data/app/jackpal.androidterm-sAgHElvcbHuZtUuQdCSIdQ==
    legacyNativeLibraryDir=/data/app/jackpal.androidterm-sAgHElvcbHuZtUuQdCSIdQ=
=/lib
    primaryCpuAbi=armeabi
    secondaryCpuAbi=null
    versionCode=71 minSdk=4 targetSdk=22
    versionName=1.0.70
    splits=[base]
    apkSigningVersion=1
    applicationInfo=ApplicationInfo{544369e jackpal.androidterm}
    flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]
    privateFlags=[ DEFAULT_TO_DEVICE_PROTECTED_STORAGE DIRECT_BOOT_AWARE ]
    dataDir=/data/user_de/0/jackpal.androidterm
    supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
    usesLibraries:
      org.apache.http.legacy
    usesLibraryFiles:
      /system/framework/org.apache.http.legacy.boot.jar
    timeStamp=2022-07-23 23:53:02
    firstInstallTime=2022-07-23 23:53:02
    lastUpdateTime=2022-07-23 23:53:02
    signatures=PackageSignatures{2e3957f version:1, signatures:[e7b54ff0], past
signatures:[]}
    installPermissionsFixed=true
    pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]
    declared permissions:
      jackpal.androidterm.permission.RUN_SCRIPT: prot=dangerous, INSTALLED
      jackpal.androidterm.permission.APPEND_TO_PATH: prot=dangerous, INSTALLED
      jackpal.androidterm.permission.PREPEND_TO_PATH: prot=dangerous, INSTALLED
    requested permissions:
      android.permission.INTERNET
      android.permission.READ_EXTERNAL_STORAGE
      android.permission.WRITE_EXTERNAL_STORAGE
      android.permission.ACCESS_SUPERUSER
      android.permission.WAKE_LOCK
    install permissions:
      android.permission.INTERNET: granted=true
      android.permission.READ_EXTERNAL_STORAGE: granted=true
      android.permission.WRITE_EXTERNAL_STORAGE: granted=true
      android.permission.WAKE_LOCK: granted=true
    User 0: ceDataInode=-4294966708 installed=true hidden=false suspended=false
stopped=false notLaunched=false enabled=0 instant=false virtual=false
      gids=[3003]
      runtime permissions:

Package Changes:
  Sequence number=0


Dexopt state:
  [jackpal.androidterm]
    path: /data/app/jackpal.androidterm-sAgHElvcbHuZtUuQdCSIdQ==/base.apk
      arm: [status=speed-profile] [reason=install]


Compiler stats:
  [jackpal.androidterm]
    (No recorded stats)
cmcc_jiangsu:/ # 

        获取 userid 可以使用 dumpsys package 搭配 grep 筛选 userId 和 gids:

cmcc_jiangsu:/ # dumpsys package jackpal.androidterm | grep userId
    userId=10031
cmcc_jiangsu:/ # dumpsys package jackpal.androidterm | grep gids
    uid=10031 gids=null type=0 prot=dangerous
    uid=10031 gids=null type=0 prot=dangerous
    uid=10031 gids=null type=0 prot=dangerous
      gids=[3003]
cmcc_jiangsu:/ # 

%%%%%

查看用户权限


        不同用户具有的权限不同

        使用 dumpsys user 命令可以查看所有的用户信息 例如 userId、name、restrictions 等等:

cmcc_jiangsu:/ # dumpsys user
Users:
  UserInfo{0:null:13} serialNo=0
    State: RUNNING_UNLOCKED
    Created: 
    Last logged in: +2762d4h27m19s3ms ago
    Last logged in fingerprint: CM311-1a-YST/CM311-1a-YST/CM311-1a-YST:9/PPR1.18
0610.011/V.955.05:userdebug/test-keys
    Start time: +1h8m33s992ms ago
    Unlock time: +1h8m33s812ms ago
    Has profile owner: false
    Restrictions:
      none
    Device policy global restrictions:
      null
    Device policy local restrictions:
      null
    Effective restrictions:
      none

  Device owner id:-10000

  Guest restrictions:
    no_sms
    no_install_unknown_sources
    no_config_wifi
    no_outgoing_calls

  Device managed: false
  Started users state: {0=3}

  Max users: 1
  Supports switchable users: false
  All guests ephemeral: false
cmcc_jiangsu:/ # 

        解释一下 访客用户/Guest 的默认权限限制:

  Guest restrictions:    /* 来宾账户限制 */
    no_sms    /* 限制发送短信 */
    no_install_unknown_sources    /* 限制安装软件 */
    no_config_wifi    /* 限制配置 WiFi */
    no_outgoing_calls    /* 限制拨打电话 */

        这些权限可以在创建用户时规定也可以后期由系统动态设置

            特殊权限的用户:

用户    uid
system    1000
radio    1001

%%%%%

查看应用权限

        不同用户下的 App 应用权限也是独立的

        上面说了 uid 与 userId 存在一种计算关系

appUid = userId * 1000000 + appId

        而在系统中对于权限控制也是根据 uid 和对应的 userId 来判定的

        因此不同用户下相同应用可以具有不同的权限

        查看所有已知的权限组和单独权限组的权限:

cmcc_jiangsu:/ # pm list permission-groups    /* 查看所有已知的权限组 */
permission group:android.permission-group.CONTACTS
permission group:android.permission-group.PHONE
permission group:android.permission-group.CALENDAR
permission group:android.permission-group.CALL_LOG
permission group:android.permission-group.CAMERA
permission group:android.permission-group.SENSORS
permission group:android.permission-group.LOCATION
permission group:android.permission-group.STORAGE
permission group:droidlogic.permission-group.SYSTEM_CONTROL
permission group:android.permission-group.MICROPHONE
permission group:android.permission-group.SMS
cmcc_jiangsu:/ # pm list permissions android.permission-group.CONTACTS    /* 查看单独的权限组都有什么权限 */
All Permissions:

permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
cmcc_jiangsu:/ # pm list permissions android.permission-group.PHONE
All Permissions:

permission:android.permission.ACCESS_UCE_OPTIONS_SERVICE
permission:android.permission.ANSWER_PHONE_CALLS
permission:android.permission.READ_PHONE_NUMBERS
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.ACCESS_UCE_PRESENCE_SERVICE
permission:android.permission.ACCEPT_HANDOVER
permission:android.permission.USE_SIP
permission:com.android.voicemail.permission.ADD_VOICEMAIL
cmcc_jiangsu:/ # pm list permissions android.permission-group.CALENDAR
All Permissions:

permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
cmcc_jiangsu:/ # pm list permissions android.permission-group.CALL_LOG
All Permissions:

permission:android.permission.READ_CALL_LOG
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.PROCESS_OUTGOING_CALLS
cmcc_jiangsu:/ # pm list permissions android.permission-group.CAMERA
All Permissions:

permission:android.permission.CAMERA
cmcc_jiangsu:/ # pm list permissions android.permission-group.SENSORS
All Permissions:

permission:android.permission.BODY_SENSORS
permission:android.permission.USE_FINGERPRINT
permission:android.permission.USE_BIOMETRIC
cmcc_jiangsu:/ # pm list permissions android.permission-group.LOCATION
All Permissions:

permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
cmcc_jiangsu:/ # pm list permissions android.permission-group.STORAGE
All Permissions:

permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
cmcc_jiangsu:/ # pm list permissions droidlogic.permission-group.SYSTEM_CONTROL
All Permissions:

permission:droidlogic.permission.SYSTEM_CONTROL
cmcc_jiangsu:/ # pm list permissions android.permission-group.MICROPHONE
All Permissions:

permission:android.permission.RECORD_AUDIO
cmcc_jiangsu:/ # pm list permissions android.permission-group.SMS
All Permissions:

permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
cmcc_jiangsu:/ # 

        permissions 的参数可以组合使用:

-g	按组进行列出权限
-f	打印所有信息
-s	简短的摘要
-d	只有危险的权限列表
-u	只有权限的用户将看到列表 用户自定义权限

        例如:

pm list permissions -g -d 权限组

        示例:

cmcc_jiangsu:/ # pm list permissions -g -d android.permission-group.CONTACTS
Dangerous Permissions:

group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.ANSWER_PHONE_CALLS
  permission:android.permission.READ_PHONE_NUMBERS
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.ACCEPT_HANDOVER
  permission:android.permission.USE_SIP
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CALL_LOG
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.PROCESS_OUTGOING_CALLS

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:droidlogic.permission-group.SYSTEM_CONTROL

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

ungrouped:
cmcc_jiangsu:/ # 

        再或者查看描述:

cmcc_jiangsu:/ # pm list permissions -s
All Permissions:

通讯录: 修改您的通讯录, 查找设备上的帐号, 读取联系人

电话: null, 接听来电, 读取电话号码, 读取手机状态和身份, 拨打电话, null, null, 拨
打/接听SIP电话, 添加语音邮件

日历: 读取日历活动和详情, 添加或修改日历活动,并在所有者不知情的情况下向邀请对象
发送电子邮件

通话记录: 读取通话记录, 新建/修改/删除通话记录, 重新设置外拨电话的路径

相机: 拍摄照片和视频

身体传感器: 访问身体传感器(如心率监测器), 使用指纹硬件, 使用生物特征硬件

位置信息: 访问确切位置信息(以 GPS 和网络为依据), 访问大致位置信息(以网络为依
据)

存储空间: 读取您的USB存储设备中的内容, 修改或删除您的USB存储设备中的内容

droidlogic权限组: droidlogic 系统控制权限

麦克风: 录音

短信: 读取短信, 接收讯息 (WAP), 接收讯息(彩信), 接收讯息(短信), 发送短信, 读
取小区广播消息

ungrouped:
null, null, null, null, null, null, null, 修改系统设置, null, null, null, null,
null, 建立或中断 WiMAX 网络连接, null, null, null, null, null, null, null, null,
 关闭其他应用, null, null, null, null, null, null, null, null, 更改您的音频设置,
 null, null, null, null, “勿扰”模式使用权限, null, null, null, null, null, nul
l, null, null, null, null, null, null, null, null, null, null, null, null, 此应
用可显示在其他应用上方, null, 绑定到运营商服务, null, null, null, null, null, nu
ll, null, null, null, null, 将蓝牙设备列入访问权限白名单。, null, null, null, nu
ll, null, null, 控制近距离通信, null, null, null, null, null, null, null, null,
null, null, 发送下载通知。, null, null, null, null, null, 更改网络连接性, null,
null, 运行前台服务, 让应用始终运行, null, 启用和停用同步, null, 在后台使用数据,
null, null, null, null, 开机启动, null, null, null, null, null, null, 设置时区,
null, null, null, 展开/收拢状态栏, 卸载快捷方式, 管理个人资料和设备所有者, 请求
忽略电池优化, null, null, 与蓝牙设备配对, null, 允许接收WLAN多播, null, null, nu
ll, 设置闹钟, null, null, null, null, null, null, null, null, 检索正在运行的应用
, null, null, null, null, null, 拥有完全的网络访问权限, null, null, 发射红外线,
null, 对正在运行的应用重新排序, null, null, null, 在后台运行, 访问蓝牙设置, null
, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, 计算应用存储空间, null, null, null, nu
ll, null, null, null, null, 访问所有系统下载内容, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, nu
ll, null, null, null, null, null, null, null, null, null, null, null, null, 获取
额外的位置信息提供程序命令, null, null, null, 使用即时通讯通话服务, null, 访问下
载管理器。, 发送持久广播, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, 连接WLAN
网络和断开连接, null, null, 读取安装会话, null, null, null, null, null, null, nu
ll, null, null, 使用下载管理器。, null, null, null, null, null, null, null, null
, null, null, null, null, null, 查看网络连接, null, null, null, 停用屏幕锁定, nu
ll, null, null, null, null, null, null, null, null, null, 设置壁纸, null, null,
在后台使用数据, null, null, null, null, null, null, null, null, null, null, null
, null, null, null, null, null, 关闭其他应用, null, null, null, null, null, null
, null, null, null, null, null, 读取同步统计信息, 通过系统转接来电, null, null,
null, null, 保留下载缓存中的空间, null, null, null, null, null, 请求删除文件包,
null, null, null, null, null, null, null, null, null, null, null, null, null, nu
ll, null, null, null, null, null, null, null, null, null, null, null, null, null
, null, 调整您的壁纸大小, null, 读取同步设置, null, null, null, null, 在后台运行
, null, null, null, null, null, null, null, 控制振动, null, null, null, null, nu
ll, null, null, null, null, null, null, null, null, null, null, null, null, null
, null, null, null, null, null, null, null, null, null, null, null, null, null,
查看WLAN连接, null, null, null, null, null, 更改 WiMAX 状态, null, null, 请求安
装文件包, null, null, null, null, null, 安装快捷方式, null, null, null, null, nu
ll, null, null, null, null, 防止手机休眠, null, 高级下载管理器功能。, null, null
, null, null, null, null, null, null, null, null, null, null, null
cmcc_jiangsu:/ # 

        更简短的:

cmcc_jiangsu:/ # pm list permissions -g -d -s android.permission-group.CONTACTS
Dangerous Permissions:

通讯录: 修改您的通讯录, 查找设备上的帐号, 读取联系人

电话: 接听来电, 读取电话号码, 读取手机状态和身份, 拨打电话, null, 拨打/接听SIP电
话, 添加语音邮件

日历: 读取日历活动和详情, 添加或修改日历活动,并在所有者不知情的情况下向邀请对象
发送电子邮件

通话记录: 读取通话记录, 新建/修改/删除通话记录, 重新设置外拨电话的路径

相机: 拍摄照片和视频

身体传感器: 访问身体传感器(如心率监测器)

位置信息: 访问确切位置信息(以 GPS 和网络为依据), 访问大致位置信息(以网络为依
据)

存储空间: 读取您的USB存储设备中的内容, 修改或删除您的USB存储设备中的内容

droidlogic权限组: droidlogic 系统控制权限

麦克风: 录音

短信: 读取短信, 接收讯息 (WAP), 接收讯息(彩信), 接收讯息(短信), 发送短信, 读
取小区广播消息

ungrouped:

cmcc_jiangsu:/ # 

——————————

权限组详解

%%%%%

平台版本与 API 级别

平台版本        版本名称        API 级别        版本代码

Android 1.0        无        1        BASE

Android 1.1        无        2        BASE_1_1

Android 1.5        Cupcake 纸杯蛋糕        3        CUPCAKE

Android 1.6        Donut 甜甜圈        4        DONUT

Android 2.0        Eclair 闪电泡芙        5        ECLAIR

Android 2.0.1        Eclair 闪电泡芙        6        ECLAIR_0_1

Android 2.1.x        Eclair 闪电泡芙        7        ECLAIR_MR1

Android 2.2.x        Froyo 冻酸奶        8        FROYO

Android 2.3/2.3.1/2.3.2        Gingerbread 姜饼        9        GINGERBREAD

Android 2.3.3/2.3.4        Gingerbread 姜饼        10        GINGERBREAD_MR1

Android 3.0.x        Honeycomb 蜂巢        11        HONEYCOMB

Android 3.1.x        Honeycomb 蜂巢        12        HONEYCOMB_MR1

Android 3.2        Honeycomb 蜂巢        13        HONEYCOMB_MR2

Android 4.0/4.0.1/4.0.2        Ice Cream Sandwich 冰淇凌三明治        14        ICE_CREAM_SANDWICH

Android 4.0.3/4.0.4        Ice Cream Sandwich 冰淇凌三明治        15        ICE_CREAM_SANDWICH_MR1

Android 4.1/4.1.1        Jelly Bean 糖豆        16        JELLY_BEAN

Android 4.2/4.2.2        Jelly Bean 糖豆        17        JELLY_BEAN_MR2

Android 4.3        Jelly Bean 糖豆        18        JELLY_BEAN_MR3

Android 4.4        KitKat 奇巧巧克力棒        19        KITKAT

Android 4.4W        KitKat 奇巧巧克力棒        20        KITKAT_WATCH

Android 5.0        Lollipop 棒棒糖        21        LOLLIPOP

Android 5.1        Lollipop 棒棒糖        22        LOLLIPOP_MR1

Android 6.0        Marshmallow 棉花糖        23        M

Android 7.0        Nougat 牛轧糖        24        N

Android 7.1        Nougat 牛轧糖        25        N_MR1

Android 8.0        Oreo 奥利奥        26        O

Android 8.1        Oreo 奥利奥        27        O_MR1

Android 9.0        Pie 馅饼        28        P

Android 10.0        Android 10        29        Q

Android 11        Android 11        R        R

%%%%%

CM311-1A 盒子 Android 9 所有已知的权限组

    android.permission-group.CONTACTS	/* 联系人权限组 */
通讯录: 修改您的通讯录, 查找设备上的帐号, 读取联系人

    android.permission-group.PHONE	/* 电话权限组 */
电话: 接听来电, 读取电话号码, 读取手机状态和身份, 拨打电话, null, 拨打/接听SIP电
话, 添加语音邮件

    android.permission-group.CALENDAR	/* 日历权限组组 */
日历: 读取日历活动和详情, 添加或修改日历活动,并在所有者不知情的情况下向邀请对象
发送电子邮件

    android.permission-group.CALL_LOG	/* 呼叫功能的权限组 */
通话记录: 读取通话记录, 新建/修改/删除通话记录, 重新设置外拨电话的路径

    android.permission-group.CAMERA	/* 相机权限组 */
相机: 拍摄照片和视频

    android.permission-group.SENSORS	/* 身体传感器权限组 */
身体传感器: 访问身体传感器(如心率监测器)

    android.permission-group.LOCATION	/* 位置权限组 */
位置信息: 访问确切位置信息(以 GPS 和网络为依据), 访问大致位置信息(以网络为依
据)

    android.permission-group.STORAGE	/* 外部存储权限组 */
存储空间: 读取您的USB存储设备中的内容, 修改或删除您的USB存储设备中的内容

    droidlogic.permission-group.SYSTEM_CONTROL	/* 系统控制权限组 */
droidlogic权限组: droidlogic 系统控制权限

    android.permission-group.MICROPHONE	/* 麦克风权限组 */
麦克风: 录音

    android.permission-group.SMS	/* 短信权限组 */
短信: 读取短信, 接收讯息 (WAP), 接收讯息(彩信), 接收讯息(短信), 发送短信, 读
取小区广播消息

%%%%%

联系人权限组 android.permission-group.CONTACTS

android.permission-group.CONTACTS    /* 联系人权限组_API 级别 23_联系人和配置文件相关的运行时权限 */

android.permission.WRITE_CONTACTS	/* 写入联系人权限 允许应用写入用户联系人数据_保护等级 危险_API 级别 1 */
android.permission.GET_ACCOUNTS	/* 访问谷歌账户权限 国内用不了…… 该权限意味着是否允许访问帐户服务中的帐户列表_保护等级 危险_API 级别 1 */
android.permission.READ_CONTACTS	/* 读取联系人权限 允许应用读取用户联系人数据_保护等级 危险_API 级别 1 */

%%%%%

电话权限组 android.permission-group.PHONE

android.permission-group.PHONE    /* 电话权限组_API 级别 23_电话功能相关的权限 */

android.permission.ACCESS_UCE_OPTIONS_SERVICE	/* 允许应用访问 UCE-OPTIONS 权限_保护等级 为 签名|专用 */
android.permission.ANSWER_PHONE_CALLS	/* 允许应用接听来电权限_保护等级为 危险_API 级别 26 */
android.permission.READ_PHONE_NUMBERS	/* 允许应用读取设备的电话号码权限_保护等级为 危险_API 级别 26 */
android.permission.READ_PHONE_STATE	/* 读取手机状态权限 就是允许应用访问电话状态_保护等级为 危险_API 级别 1 */
android.permission.CALL_PHONE	/* 允许应用在不经过拨号界面的情况下启动电话呼叫以便用户确认呼叫权限_保护等级为 危险_API 级别 1 */
android.permission.ACCESS_UCE_PRESENCE_SERVICE	/* 允许应用访问 UCE-Presence 权限_保护等级 为 签名|专用 */
android.permission.ACCEPT_HANDOVER	/* 允许呼叫应用继续在其她应用中启动的呼叫权限 如视频通话应用希望在用户的移动网络上继续语音通话_保护等级为 危险_API 级别 28 */
android.permission.USE_SIP	/* 允许应用使用 SIP 服务权限_保护等级为 危险_API 级别 9 */
com.android.voicemail.permission.ADD_VOICEMAIL	/* 允许应用向系统中添加语音邮件权限_保护等级为 危险_API 级别 14 */
/* 其她权限例如 MANAGE_OWN_CALLS 允许调用应用通过自我管理的 ConnectionService API 管理自己的调用_保护等级为 正常_API 级别 26 */

%%%%%

日历权限组 android.permission-group.CALENDAR

android.permission-group.CALENDAR    /* 日历权限组_API 级别 17_用户日历相关的运行时权限 */

android.permission.READ_CALENDAR	/* 允许应用读取用户日历数据权限_保护等级 危险_API 级别 1 */
android.permission.WRITE_CALENDAR	/* 允许应用写入用户日历数据权限_保护等级 危险_API 级别 1 */

%%%%%

通话记录权限组 android.permission-group.CALL_LOG

android.permission-group.CALL_LOG    /* 通话记录的权限_API 级别 28 */

android.permission.READ_CALL_LOG	/* 允许应用读取用户通话日志权限_危险_16 */
android.permission.WRITE_CALL_LOG	/* 允许应用写入用户通话日志_危险_16 */
android.permission.PROCESS_OUTGOING_CALLS	/* 允许应用查看拨出呼叫期间拨出的号码_危险_1 */

%%%%%

相机权限组 android.permission-group.CAMERA

android.permission-group.CAMERA    /* 访问相机或从设备捕获 图像/视频 相关的权限_17 */

android.permission.CAMERA	/* 允许访问摄像头_危险|立即_1 */

%%%%%

身体传感器权限组 android.permission-group.SENSORS

android.permission-group.SENSORS    /* 身体或环境传感器相关的权限_23 */

android.permission.BODY_SENSORS	/* 允许应用访问身体传感器的数据_危险_20 */
android.permission.USE_FINGERPRINT	/* 允许应用使用指纹硬件 API 级别 28 以后不再建议使用_正常_23 */
android.permission.USE_BIOMETRIC	/* 允许应用使用设备支持的生物识别模式_正常_28 */

%%%%%

位置信息权限组 android.permission-group.LOCATION

android.permission-group.LOCATION    /* 允许访问设备位置信息的权限_1 */

android.permission.ACCESS_FINE_LOCATION	/* 允许应用访问精确的位置_危险_1 */
android.permission.ACCESS_COARSE_LOCATION	/* 允许应用访问大概的位置_危险_1 */

%%%%%

存储空间权限组 android.permission-group.STORAGE

android.permission-group.STORAGE    /* 外部存储相关的运行时权限 API 级别 Q 以后不再建议使用_4 */

android.permission.READ_EXTERNAL_STORAGE	/* 允许应用读取扩展存储 API 级别 Q 以后不再建议使用_危险_16 */
android.permission.WRITE_EXTERNAL_STORAGE	/* 允许应用写入扩展存储 API 级别 Q 以后不再建议使用_危险_4 */

%%%%%

系统控制权限组 androidlogic.permission-group.SYSTEM_CONTROL

droidlogic.permission-group.SYSTEM_CONTROL    /* 未知权限 字面翻译为 安卓逻辑系统.授权-组.系统_控制 */

droidlogic.permission.SYSTEM_CONTROL

%%%%%

麦克风权限组 android.permission-group.MICROPHONE

android.permission-group.MICROPHONE    /* 访问麦克风相关的权限 请注意 电话呼叫也会捕获音频 但属于单独的权限组_17 */

android.permission.RECORD_AUDIO	/* 允许应用录音_危险_1 */

%%%%%

短信权限组 android.permission-group.SMS

android.permission-group.SMS    /* 短信相关的运行时权限_23 */

android.permission.READ_SMS	/* 允许应用访问 SMS 信息_危险_1 */
android.permission.RECEIVE_WAP_PUSH	/* 允许应用接收 WAP 推送信息_危险_1 */
android.permission.RECEIVE_MMS	/* 允许应用监视传入的彩信_危险_1 */
android.permission.RECEIVE_SMS	/* 允许应用接收短信_危险_1 */
android.permission.SEND_SMS	/* 允许应用发送短信_危险_1 */
android.permission.READ_CELL_BROADCASTS	/* 允许应用读取小区广播消息_危险 */

%%%%%

CM311-1A 盒子和手机设备都没有看到 WIFI 网络相关的权限

shell@GIONEE_G1605A:/ $ pm list permission-groups
permission group:android.permission-group.CONTACTS
permission group:android.permission-group.PHONE
permission group:android.permission-group.CALENDAR
permission group:android.permission-group.CAMERA
permission group:android.permission-group.SENSORS
permission group:android.permission-group.LOCATION
permission group:android.permission-group.STORAGE
permission group:android.permission-group.MICROPHONE
permission group:android.permission-group.SMS
shell@GIONEE_G1605A:/ $ pm list permissions -g -d
Dangerous Permissions:

group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

ungrouped:
shell@GIONEE_G1605A:/ $ pm list permissions -s
All Permissions:

通讯录: 修改您的通讯录, 查找设备上的帐户, 读取您的通讯录

电话: 读取通话记录, 读取手机状态和身份, 使用即时通讯通话服务, 直接拨打电话号码,
写入通话记录, 拨打/接听SIP电话, 重新设置外拨电话的路径, 添加语音邮件

日历: 读取日历活动和机密信息, 添加或修改日历活动,并在所有者不知情的情况下向邀请
对象发送电子邮件

相机: 拍摄照片和视频

身体传感器: 人体传感器(如心跳速率检测器), 使用指纹硬件

位置信息: 精确位置(基于GPS和网络), 大致位置(基于网络)

存储空间: 读取您的SD卡中的内容, 修改或删除您的SD卡中的内容

麦克风: 录音

短信: 读取您的讯息(短信或彩信), 接收讯息 (WAP), 接收讯息(彩信), 接收讯息(短
信), 发送和查看短信, 读取小区广播消息

ungrouped:
null, null, null, null, null, 修改系统设置, null, null, 建立或中断 WiMAX 网络连
接, null, null, null, null, null, null, Error: java.lang.NullPointerException: A
ttempt to read from field 'java.lang.String android.content.pm.ApplicationInfo.p
ublicSourceDir' on a null object reference
1|shell@GIONEE_G1605A:/ $ 

##################################################

三层 Android 权限详解

——————————

第三层 —— 系统权限以及软件安装权限真相

%%%%%

Android 底层映射为 Linux 权限

        每个程序在安装时都有建立一个系统 ID

用以保护数据不被其她应用获取

        例如 app_15

        Android 系统会根据不同的用户和组来分配不同权限

        比如访问 SD 卡、访问网络等等

        底层均映射为 Linux 权限!

%%%%%

Android 应用程序权限机制

        Android 安全模型基于 Linux 的权限管理

android 系统充分利用了 linux 的用户权限管理方法

        使用沙箱隔离机制将每个应用的进程资源隔离

Android 应用程序在安装时赋予一个 UID
UID 不同的应用程序完全隔离

        另一方面 应用如果想使用某种服务 需要在 AndroidManifest.xml 中申请
        比如想使用网络的话需要在 AndroidManifest.xml 中添加:

        INTERNET 权限将被映射到底层的 GID
        所以一个应用有一个 UID 可以有多个 GID 来获得多个权限

        Android 本身支持在应用程序的 AndroidManifest.xml 中自定义权限
        但这种自定义的权限没有被映射到系统底层的用户组中 没有独立的 GID
        如果在系统中有一个 C 语言写的服务 只有应用申请了权限才可以使用 我们就需要将这个权限映射到底层
        例如在开发中自定义一个类似于上面的 INTERNET 的系统级权限组

    一个用户可以属于多个组
    一个文件只能属于某个组

        这里主要是在 AndroidManifest.xml 中声明权限

        主要是通过在 AndroidManifest.xml 中显式地声明应用程序需要的权限 防止应用程序错误的使用服务 不恰当访问资源

        Android 中每种权限都用一个独立的标签表示 示例:




        当在安装应用程序时 Android 就会给予一个 UID

        这个 UID 可连接到该应用程序的 AndroidManifest.xml 文件的内容

        所以 User 在安装你的应用程序时在屏幕上的窗口里可以看到这个 AndroidManifest.xml 文件的内容

        用户会看到你对应用程序的目的、权限等说明

        当你接受这支程序的意图、权限说明之后 Android 就安装她 并给她一个 UID

        万一在你的应用程序执行期间有越轨 企图做出非权限范围 的行为时 用户将会得到 Android 的警告讯息

        Android 的系统权限不是由用户控制 而是由开发者根据开发的需要控制相关权限的开放与否

        例如 AndroidManifest.xml 中有如下内容:


        表示需要使用存储设备和录音设备 在安装的时候 就会提示用户她需要的权限

%%%%%

安装一个 APK 的详细过程

        安装 APK 时发生了什么:

        在安装 apk 的时候会解析
AndroidManifest.xml
        把相应的信息保存起来

        解析权限调用的是函数
private Package parsePackage
        这里保存的都是
android.permission.WRITE_EXTERNAL_STORAGE
        这样的字符串

        在解析完后会调用
private void grantPermissionsLP
        函数获取对应的
group_id
        该函数把相应的组都保存到了 gids 中

        当应用程序启动的过程中会调用
private final void startProcessLocked
        这里就是获取前面保存的 gids
        再后面调用创建了一个新的进程 这里传的参数就有 gids

        创建新进程利用 jni 最终调用
static pid_t forkAndSpecializeCommon
        函数

        我们如果研究代码可以看到在子进程里调用
setgroupsIntarray
        设置该进程所属的组 这样她就拥有了该组的权限
        也通过 setgid 及 setuid 决定了应用程序的 uid 及 gid 值

        权限控制主要放置在 AndroidManifest.xml 文件中

        最后镜像生成在 system\etc\permissions\platform.xml 配置文件中

%%%%%

声明时权限 安全等级/protectionLevel 分类

        多用户下的应用其实只安装一次 不同用户下同一个应用的版本和签名都应该相同

        不同用户下相同 App 能够独立运行是因为系统为她们创造了不同的运行环境和权限

        protectionLevel 分为三类:







        normal 是普通权限

在 AndroidManifest.xml 中声明就可以获取的权限

如 INTERNET 权限


        dangerous 敏感权限

需要动态申请告知用户才能获取

        signature|privileged 签名|特权

具有系统签名的系统应用才可以获取的权限

对应上方的安装在 /system/priv-app 的特权应用!

——————————

第一层 —— 开发层 AndroidManifest.xml

        第一层是在开发人员编写代码时由应用设置 主要是修改 AndroidManifest.xml 文件

        AndroidManifest.xml 是 APP 的运行配置文件 她是一个 XML 描述文件 指定了 APP 的运行配置信息

        一般都存放在 APP 包下的 manifests 目录下

        不过我也见过放在 src/main/res/AndroidManifest.xm 下面的

        AndroidManifest.xml 文件的作用:

    描述 app 的包名
    描述 app 使用的 android 系统版本信息
    描述 app 本身的版本信息 这样对于同一个 app 的两个版本 系统就能区分那个新版本旧版本
    描述应用对外暴露的组件等等

        我们将 apk 文件后缀修改成 zip 就可以使用平常的解压工具进行解压了:

        第一眼看到的就是 AndroidManifest.xml 配置文件:

[CM311-1A]-全网最全 Android 用户管理及用户应用权限_第6张图片

        AndroidManifest 官方解释是 应用清单 manifest 意思是货单

        每个应用的根目录中都必须包含一个 并且文件名必须一模一样

        这个文件中包含了 APP 的配置信息 系统需要根据里面的内容运行 APP 的代码 显示界面

        AndroidManifest.xml 是每个 apk 文件解压后根目录下的一个文件

        每个 apk 都必须包含一个 AndroidManifest.xml 文件 且名字必须与此完全一致.

        示例 AndroidManifest.xml 中的一段配置代码:

      
      
      
      
      
      
      
      
      
      

%%%%%

AndroidManifest.xml 配置文件权限分类

        通过 shareduserid 来实现数据共享有一个限制就是相同的签名

        这个是很高要求的

一般都是同一个公司开发出来的 app 才能满足获取内置到第三方 ROM 里面去才能满足

        通常的做法是通过 uses-permission 来实现 我们自己定义一个权限!

        在需要被访问的地方加上这个权限限制这样就能到达目的!

	normal 权限 也就是一般的权限 不需要用户去确认的 例如一个应用申请连接网络等
	dangerous 权限 这种权限较 normal 权限高一些 需要用户手动点击确认的 例如应用需要读取联系人的信息 因为这些数据是比较隐私的可能会导致你的数据泄露等
	signature 只有当申请权限的应用程序的数字签名与声明此权限的应用程序的数字签名相同时 才能将权限授给她 如果是申请系统权限则需要与系统签名相同
	signatureOrSystem 签名相同 或者申请权限的应用为系统应用 在 system image 中

        signature 和 signatureOrSystem 要求是很高 一般的只有是相同公司开发出来的应用才能满足

%%%%%

AndroidManifest.xml 配置文件详解

        我们在安装 Android 软件的时候系统会提示该软件所需要的权限

        相对于其她系统 Android 的权限非常多

        我们在开发软件的时候也需要声明相应的权限,比如希望软件能发短信,需要声明软件调用短信的权限

        否则软件运行的时候就会报错

        Android 的权限在 AndroidManifest.xml 文件里配置

        AndroidManifest 文件中有四个标签与 permission 有关:

        其中最常用的是

        当我们需要获取某个权限的时候就必须在我们的 manifest 文件中声明



  
  
  
  
  
  
  ...

         的作用相似 两者之间的不同之处在于

是 android 预定义的权限

是自己定义的权限

         用的相对较少
            这两个标签就更少见了
        简单说  就是声明一个标签 该标签代表了一组 permissions
        而  是为一组 permissions 声明了一个 namespace

        定义方法如下:

        解释一下:

        propectionLevel 这个属性是必须声明的
        告诉系统通知用户的应用要求许可 或允许谁认为获得许可的情况下
        permissionGroup 这个是可选的 与  配合使用
        label, name 和 icon 用于描述权限

         是我们用的最多的 例如短信和电话权限的定义:


        常见权限:

    /* 允许读写访问 properties 表 在 checkin 数据库中 可以修改上传 */
    /* 允许一个程序访问 CellID 或 WiFi 热点来获取粗略的位置 */
    /* 允许一个程序访问精良位置 如 GPS */
    /* 允许应用程序访问额外的位置提供命令 */
    /* 允许程序创建模拟位置提供用于测试 */
    /* 允许程序访问有关 GSM 网络信息 */
    /* 允许程序使用 SurfaceFlinger 底层特性 */
    /* 允许程序访问 Wi-Fi 网络状态信息 */
    /* 允许程序发布系统级服务 */
    /* 允许程序更新手机电池统计信息 */
    /* 允许程序连接到已配对的蓝牙设备 */
    /* 允许程序发现和配对蓝牙设备 */
    /* 请求能够禁用设备 */
    /* 允许程序广播一个提示消息在一个应用程序包已经移除后 */
    /* 允许一个程序广播常用 intents */
    /* 允许一个程序初始化一个电话拨号不需通过拨号用户界面需要用户确认 */
    /* 允许一个程序拨打任何号码 包含紧急号码无需通过拨号用户界面需要用户确认 */
    /* 请求访问使用照相设备 */
    /* 允许一个程序是否改变一个组件或其她的启用或禁用 */
    /* 允许一个程序修改当前设置 如本地化 */
    /* 允许程序改变网络连接状态 */
    /* 允许程序改变 Wi-Fi 连接状态 */
    /* 允许一个程序清楚缓存从所有安装的程序在设备中 */
    /* 允许一个程序清除用户设置 */
    /* 允许启用禁止位置更新提示从无线模块 */
    /* 允许程序删除缓存文件 */
    /* 允许一个程序删除包 */
    /* 允许访问底层电源管理 */
    /* 允许程序 RW 诊断资源 */
    /* 允许程序禁用键盘锁 */
    /* 允许程序返回状态抓取信息从系统服务 */

android.permission.EXPAND_STATUS_BAR    /* 允许一个程序扩展收缩状态栏 */
android.permission.FACTORY_TEST    /* 作为一个工厂测试程序 运行在 root 用户 */
android.permission.FLASHLIGHT    /* 访问闪光灯 */
android.permission.FORCE_BACK    /* 允许程序强行一个后退操作是否在顶层 activities */
android.permission.FOTA_UPDATE android    /* 一个预留权限 */
android.permission.GET_ACCOUNTS    /* 访问一个帐户列表在 Accounts Service 中 */
android.permission.GET_PACKAGE_SIZE    /* 允许一个程序获取任何 package 占用空间容量 */
android.permission.GET_TASKS    /* 允许一个程序获取信息有关当前或最近运行的任务 一个缩略的任务状态 是否活动等等 */
android.permission.HARDWARE_TEST    /* 允许访问硬件 */
android.permission.INJECT_EVENTS    /* 允许一个程序截获用户事件如按键、触摸、轨迹球等等到一个时间流 */
android.permission.INSTALL_PACKAGES    /* 允许一个程序安装 packages */
android.permission.INTERNAL_SYSTEM_WINDOW    /* 允许打开窗口使用系统用户界面 */
android.permission.INTERNET    /* 允许程序打开网络套接字 */
android.permission.MANAGE_APP_TOKENS    /* 允许程序管理程序引用在窗口管理器中 创建、催后、z- order 默认向 z 轴推移 */
android.permission.MASTER_CLEAR    /* 恢复出厂设置权限 清除一切用户数据 */
android.permission.MODIFY_AUDIO_SETTINGS    /* 允许程序修改全局音频设置 */
android.permission.MODIFY_PHONE_STATE    /* 允许修改话机状态,如电源,人机接口等 */
android.permission.MOUNT_UNMOUNT_FILESYSTEMS    /* 允许挂载和反挂载文件系统可移动存储 */
android.permission.PERSISTENT_ACTIVITY    /* 允许一个程序设置她的 activities 显示 */
android.permission.PROCESS_OUTGOING_CALLS    /* 允许程序监视、修改有关播出电话 */
android.permission.READ_CALENDAR    /* 允许程序读取用户日历数据 */
android.permission.READ_CONTACTS    /* 允许程序读取用户联系人数据 */
android.permission.READ_FRAME_BUFFER    /* 允许程序屏幕波或和更多常规的访问帧缓冲数据 */
android.permission.READ_INPUT_STATE    /* 允许程序读取底层系统日志文件 */
android.permission.READ_OWNER_DATA    /* 允许程序读取所有者数据 */
android.permission.READ_SMS    /* 允许程序读取短信息 */
android.permission.READ_SYNC_SETTINGS    /* 允许程序读取同步设置 */
android.permission.READ_SYNC_STATS    /* 允许程序读取同步状态 */
android.permission.REBOOT    /* 请求能够重新启动设备 */
android.permission.RECEIVE_BOOT_COMPLETED    /* 允许一个程序接收到 ACTION_BOOT_COMPLETED 广播在系统完成启动 */
android.permission.RECEIVE_MMS    /* 允许一个程序监控将收到 MMS 彩信 记录或处理 */
android.permission.RECEIVE_SMS    /* 允许程序监控一个将收到短信息 记录或处理 */
android.permission.RECEIVE_WAP_PUSH    /* 允许程序监控将收到 WAP PUSH 信息 */
android.permission.RECORD_AUDIO    /* 允许程序录制音频 */
android.permission.REORDER_TASKS    /* 允许程序改变 Z 轴排列任务 */
android.permission.RESTART_PACKAGES    /* 允许程序重新启动其她程序 */
android.permission.SEND_SMS    /* 允许程序发送 SMS 短信 */
android.permission.SET_ACTIVITY_WATCHER    /* 允许程序监控或控制 activities 已经启动全局系统中 */
android.permission.SET_ALWAYS_FINISH    /* 允许程序控制是否活动间接完成在处于后台时 */
android.permission.SET_ANIMATION_SCALE    /* 修改全局信息比例 */
android.permission.SET_DEBUG_APP    /* 配置一个程序用于调试 */
android.permission.SET_ORIENTATION    /* 允许底层访问设置屏幕方向和实际旋转 */
android.permission.SET_PREFERRED_APPLICATIONS    /* 允许一个程序修改列表参数 PackageManager.addPackageToPreferred() 和 PackageManager.removePackageFromPreferred() 这两个方法 */
android.permission.SET_PROCESS_FOREGROUND    /* 允许程序当前运行程序强行到前台 */
android.permission.SET_PROCESS_LIMIT    /* 允许设置最大的运行进程数量 */
android.permission.SET_TIME_ZONE    /* 允许程序设置时间区域 */
android.permission.SET_WALLPAPER    /* 允许程序设置壁纸 */
android.permission.SET_WALLPAPER_HINTS    /* 允许程序设置壁纸 hits */
android.permission.SIGNAL_PERSISTENT_PROCESSES    /* 允许程序请求发送信号到所有显示的进程中 */
android.permission.STATUS_BAR    /* 允许程序打开、关闭或禁用状态栏及图标 */
android.permission.SUBSCRIBED_FEEDS_READ    /* 允许一个程序访问订阅 RSS Feed 内容提供 */
android.permission.SUBSCRIBED_FEEDS_WRITE    /* 系统暂时保留改设置 */
android.permission.SYSTEM_ALERT_WINDOW    /* 允许一个程序打开窗口使用 TYPE_SYSTEM_ALERT 显示在其她所有程序的顶层 */
android.permission.VIBRATE    /* 允许访问振动设备 */
android.permission.WAKE_LOCK    /* 允许使用 PowerManager 的 WakeLocks 保持进程在休眠时从屏幕消失 */
android.permission.WRITE_APN_SETTINGS    /* 允许程序写入 API 设置 */
android.permission.WRITE_CALENDAR    /* 允许一个程序写入但不读取用户日历数据 */
android.permission.WRITE_CONTACTS    /* 允许程序写入但不读取用户联系人数据 */
android.permission.WRITE_GSERVICES    /* 允许程序修改 Google 服务地图 */
android.permission.WRITE_OWNER_DATA    /* 允许一个程序写入但不读取所有者数据 */
android.permission.WRITE_SETTINGS    /* 允许程序读取或写入系统设置 */
android.permission.WRITE_SMS    /* 允许程序写短信 */
android.permission.WRITE_SYNC_SETTINGS    /* 允许程序写入同步设置 */

——————————

第二层 —— 框架层 preferences.xml

        platform.xml 其实是将 aosp 中的配置文件直接拷贝到手机目录中

        AOSP 全称 Android Open Source Project 中文意为 Android 开放源代码项目

    安卓开源项目 开源即开放源代码
    Android 是一个基于 Linux 由 Google 主导的开源系统
    严格意义上来说 Android = AOSP + GMS
    GMS 即谷歌移动服务 即常刷的谷歌服务包
    国内无法使用 GMS 所以国内的各种定制 ROM 都是 AOSP 的定制修改加上自家的云服务
    比如 MIUI、ColorOS 等
    所以所有的 ROM 最终的根源就是 AOSP 没有 AOSP 也就没有现在的 Android

        应用包中 preferences.xml 文件的位置在 res\xml\preferences.xml

        设备目录存放的位置在 /system/etc/permissions/platform.xml

        在 aosp 中的存放位置是 framework/base/data/etc/platform.xml

%%%%%

示例一个 root 过的手机修改板子 sd 权限

        编辑的是

root/system/etc/permissions/platform.xml

        文件 看到代码如下:

		
			
			
		

        修改为:

		
			
			
			
		

        再然后重启就行了

%%%%%

CM311-1A 的 platform.xml 配置文件详解

        这是对 platform.xml 的解析 注意看注释:




/** 第一部分 >>> 警告部分
    说明这个文件是处理普通用户和系统权限之间映射的 如果擅自改动会造成大的安全漏洞

    此文件用于定义较低级别系统之间的映射 管理的用户和组 ID 以及更高级别的权限名称在平台旁。
    编辑此文件时要非常小心!这里犯的错误可能会打开巨大的安全漏洞。
 */




    
    
    

/** 第二部分 >>> 权限映射
     将低等级的 groups id 和权限名称相关联
    通过指定这样的映射 表明了一个授予了给定权限的应用进程将附带这所给的 group id 运行
    因此她可以执行这个群组所允许的读写和执行操作

    以下标记将低级组 ID 与权限名称关联
    通过指定这样一个映射,您的意思是,任何被授予给定权限的应用程序进程也将在其进程附加给定组 ID 的情况下运行
    因此,她可以执行该组允许的任何文件系统 读、写、执行 操作。
 */

    

    
        
    

    
        
    

    
        
        
        
    

    
        
    

    
        
    

    
        
    

    
        
	
    

    
        
    

    
        
    

    
    
        
    

    
    
        
        
    

    
    
        
    

    
    
        
    

    
        
    

    
    
        
    

    
        
        
    

    
        
    

/** 第三部分 >>> 权限映射的例外部分

    这些权限映射到 GID 但我们需要将其保留在此处 直到支持从 L 升级到当前版本
    这些权限是内置的 在 L 中没有存储在包中
    如果在解析包时未在此处定义 则返回 xml
    xml 我们将忽略授予应用程序的这些权限 而不传播授予的状态
    从 N 开始 我们将内置权限存储在包中
    与脆弱性相比 作为保存的存储的 xml 可以忽略不计 带有权限的一个标记
    因为可以删除不再需要映射到 GID 和中断授权传播的内置权限
 */

    
    
    

    
    
    

/** 第四部分 >>> 分配权限
     有些用户比较特殊 她们是系统核心用户
    Android 会分配特定的高级权限来允许她们来完成一些高级的操作

    比如 shell 用户有很多权限 这是因为开发者需要比较开放的环境来和 system 进行交互
    shell 用户的权限有时候需要 adb root/adb remount 才能享受
    对应的需要 userdebug 或者 root 版本
    user 版本下 shell 命令还是有所限制的
 */

    

    
    
    
    
    
    

    
    
    
    
    
    

    
    
    
    
    
    
    
    

    
    
    
    
    

    
    

    
    
    
    

/** 第五部分 >>> 库
     标签设置应用可以链接到的库
    可以理解为应用可以调用到的 jar 吧?

    这是可供应用程序代码链接的所有库的列表
 */

    

    
    
    
    
    

/** 第六部分 >>> 省电优化白名单
     表示这个对应于手机中的省电优化设置
    用这个 tag 配置在这张表中就代表该应用不受省电优化显示 可以在后台访问网络
    对应应用在原生设置中的电源菜单也是灰显 不可设置优化的

    这些是白名单上的标准软件包始终具有互联网功能
    可以在省电模式下访问 即使她们不在前台

    这个 tag 也和 doze mode 有些关系
	就是低耗电模式 什么是低耗电模式:
如果用户设备未插电源、处于静止状态一段时间且屏幕关闭 设备将进入该模式
该模式下系统会尝试通过限制应用对网络和 CPU 密集型服务的访问来节省电量
这还可以阻止引用访问网络并推迟其作业、同步和标准闹铃
	
	系统会定期退出低耗电模式一会儿 好让应用完成其已推迟的 Activity
	在此维护时段内 系统会运行所有特定同步 作业和闹铃并允许应用访问网络
	在每个维护时段结束后系统会再次进入低耗电模式 暂停网络并推迟作业、同步和闹铃
	随着时间推移 系统安排维护时段的次数越来越少 这有助于设备未连接至充电器情况下长期处于不活动状态时降低电池消耗

	一旦用户通过移动设备、打开屏幕或连接到充电器唤醒设备 系统就会立即退出低耗电模式 并且所有应用都将返回到正常 Activity

	低电耗模式限制:
暂停访问网络
系统将忽略 wake locks
标准 AlarmManager 闹铃将推迟到下一维护时段 包括 setExact() 和 setWindow() 如果需要设置低耗电模式下触发的闹铃请使用 setAndAllowWhileIdle()/setExactAndAllowWhileIdle() 一般情况下使用 setAlarmClock() 设置的闹铃将继续出发 但是系统会在这些闹铃出发之前不久退出低耗电模式
系统不执行 Wi-Fi 扫描
系统不允许运行 同步适配器
系统不允许运行 JobScheduler

    可以使用 dumpsys deviceidle whitelist 命令查看:
cmcc_jiangsu:/ # dumpsys deviceidle whitelist
system-excidle,com.android.providers.downloads,10002
system-excidle,com.android.shell,2000
system,com.android.providers.downloads,10002
system,com.android.shell,2000
cmcc_jiangsu:/ #
 */

    
    

/** 第七部分 >>> 数据白名单

    这些是白名单上的标准软件包 在数据模式下始终可以访问互联网 即使她们不在前台
 */

    
    

/** 第八部分 >>> 省电优化白名单

    这是一个需要在后台自由运行的核心平台组件

     是白名单系统提供商
 */

    
    
    

    
    
    

/** 第九部分 >>> 系统应用白名单

    这些是白名单上的软件包 可以作为系统用户运行
 */

    
    

/** 第十部分 >>> 系统应用黑名单

    这些软件包不应作为系统用户运行
 */

    
    

%%%%%

platform.xml 对应的解析代码

        xml 肯定是需要解析才能用的

/frameworks/core/java/com/android/server/SystemConfig.java

        这个 SystemConfig 就是来解析 platform.xml 然后供一个个系统接口用作返回值的依据:

    void readPermissions(File libraryDir, int permissionFlag) {
        // Read permissions from given directory.
        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
            if (permissionFlag == ALLOW_ALL) {
                Slog.w(TAG, "No directory " + libraryDir + ", skipping");
            }
            return;
        }
        if (!libraryDir.canRead()) {
            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
            return;
        }
 
        // Iterate over the files in the directory and scan .xml files
        File platformFile = null;
        for (File f : libraryDir.listFiles()) {
            // We'll read platform.xml last
            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
                platformFile = f;
                continue;
            }
 
            if (!f.getPath().endsWith(".xml")) {
                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
                continue;
            }
            if (!f.canRead()) {
                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
                continue;
            }
 
            readPermissionsFromXml(f, permissionFlag);
        }
 
        // Read platform permissions last so it will take precedence
        if (platformFile != null) {
            readPermissionsFromXml(platformFile, permissionFlag);
        }
    }
 
    private void readPermissionsFromXml(File permFile, int permissionFlag) {
        FileReader permReader = null;
        try {
            permReader = new FileReader(permFile);
        } catch (FileNotFoundException e) {
            Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
            return;
        }
 
        final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
 
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(permReader);
 
            int type;
            while ((type=parser.next()) != parser.START_TAG
                       && type != parser.END_DOCUMENT) {
                ;
            }
 
            if (type != parser.START_TAG) {
                throw new XmlPullParserException("No start tag found");
            }
 
            if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
                throw new XmlPullParserException("Unexpected start tag in " + permFile
                        + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
            }
 
            boolean allowAll = permissionFlag == ALLOW_ALL;
            boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
            boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
            boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
            boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
            boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
            boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;
            while (true) {
                XmlUtils.nextElement(parser);
                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
                    break;
                }
 
                String name = parser.getName();
                if ("group".equals(name) && allowAll) {
                    String gidStr = parser.getAttributeValue(null, "gid");
                    if (gidStr != null) {
                        int gid = android.os.Process.getGidForName(gidStr);
                        mGlobalGids = appendInt(mGlobalGids, gid);
                    } else {
                        Slog.w(TAG, " without gid in " + permFile + " at "
                                + parser.getPositionDescription());
                    }
 
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                } else if ("permission".equals(name) && allowPermissions) {
                    String perm = parser.getAttributeValue(null, "name");
                    if (perm == null) {
                        Slog.w(TAG, " without name in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    perm = perm.intern();
                    readPermission(parser, perm);
 
                } else if ("assign-permission".equals(name) && allowPermissions) {
                    String perm = parser.getAttributeValue(null, "name");
                    if (perm == null) {
                        Slog.w(TAG, " without name in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    String uidStr = parser.getAttributeValue(null, "uid");
                    if (uidStr == null) {
                        Slog.w(TAG, " without uid in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    int uid = Process.getUidForName(uidStr);
                    if (uid < 0) {
                        Slog.w(TAG, " with unknown uid \""
                                + uidStr + "  in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    perm = perm.intern();
                    ArraySet perms = mSystemPermissions.get(uid);
                    if (perms == null) {
                        perms = new ArraySet();
                        mSystemPermissions.put(uid, perms);
                    }
                    perms.add(perm);
                    XmlUtils.skipCurrentTag(parser);
 
                } else if ("library".equals(name) && allowLibs) {
                    String lname = parser.getAttributeValue(null, "name");
                    String lfile = parser.getAttributeValue(null, "file");
                    if (lname == null) {
                        Slog.w(TAG, " without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else if (lfile == null) {
                        Slog.w(TAG, " without file in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        //Log.i(TAG, "Got library " + lname + " in " + lfile);
                        mSharedLibraries.put(lname, lfile);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("feature".equals(name) && allowFeatures) {
                    String fname = parser.getAttributeValue(null, "name");
                    int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
                    boolean allowed;
                    if (!lowRam) {
                        allowed = true;
                    } else {
                        String notLowRam = parser.getAttributeValue(null, "notLowRam");
                        allowed = !"true".equals(notLowRam);
                    }
                    if (fname == null) {
                        Slog.w(TAG, " without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else if (allowed) {
                        addFeature(fname, fversion);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("unavailable-feature".equals(name) && allowFeatures) {
                    String fname = parser.getAttributeValue(null, "name");
                    if (fname == null) {
                        Slog.w(TAG, " without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mUnavailableFeatures.add(fname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without package in "
                                + permFile + " at " + parser.getPositionDescription());
                    } else {
                        mAllowInPowerSaveExceptIdle.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("allow-in-power-save".equals(name) && allowAll) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without package in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mAllowInPowerSave.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("allow-in-data-usage-save".equals(name) && allowAll) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without package in " + permFile
                                + " at " + parser.getPositionDescription());
                    } else {
                        mAllowInDataUsageSave.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("allow-unthrottled-location".equals(name) && allowAll) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without package in "
                            + permFile + " at " + parser.getPositionDescription());
                    } else {
                        mAllowUnthrottledLocation.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("allow-implicit-broadcast".equals(name) && allowAll) {
                    String action = parser.getAttributeValue(null, "action");
                    if (action == null) {
                        Slog.w(TAG, " without action in " + permFile
                                + " at " + parser.getPositionDescription());
                    } else {
                        mAllowImplicitBroadcasts.add(action);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;
 
                } else if ("app-link".equals(name) && allowAppConfigs) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without package in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mLinkedApps.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                } else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without package in " + permFile
                                + " at " + parser.getPositionDescription());
                    } else {
                        mSystemUserWhitelistedApps.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                } else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without service in "
                                + permFile + " at " + parser.getPositionDescription());
                    } else {
                        ComponentName cn = ComponentName.unflattenFromString(serviceName);
                        if (cn == null) {
                            Slog.w(TAG,
                                    " with invalid service name "
                                    + serviceName + " in "+ permFile
                                    + " at " + parser.getPositionDescription());
                        } else {
                            mBackupTransportWhitelist.add(cn);
                        }
                    }
                    XmlUtils.skipCurrentTag(parser);
                } else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name)
                        && allowAppConfigs) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage");
                    if (pkgname == null || carrierPkgname == null) {
                        Slog.w(TAG, " associatedPkgs =
                                mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
                                        carrierPkgname);
                        if (associatedPkgs == null) {
                            associatedPkgs = new ArrayList<>();
                            mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
                                    carrierPkgname, associatedPkgs);
                        }
                        associatedPkgs.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
                    readPrivAppPermissions(parser);
                } else if ("hidden-api-whitelisted-app".equals(name) && allowApiWhitelisting) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, " without package in " + permFile
                                + " at " + parser.getPositionDescription());
                    } else {
                        mHiddenApiPackageWhitelist.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                } else {
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
            }
        } catch (XmlPullParserException e) {
            Slog.w(TAG, "Got exception parsing permissions.", e);
        } catch (IOException e) {
            Slog.w(TAG, "Got exception parsing permissions.", e);
        } finally {
            IoUtils.closeQuietly(permReader);
        }
 
        // Some devices can be field-converted to FBE, so offer to splice in
        // those features if not already defined by the static config
        if (StorageManager.isFileEncryptedNativeOnly()) {
            addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);
            addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
        }
 
        if (ActivityManager.isLowRamDeviceStatic()) {
            addFeature(PackageManager.FEATURE_RAM_LOW, 0);
        } else {
            addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
        }
 
        for (String featureName : mUnavailableFeatures) {
            removeFeature(featureName);
        }
    }
 
    private void addFeature(String name, int version) {
        FeatureInfo fi = mAvailableFeatures.get(name);
        if (fi == null) {
            fi = new FeatureInfo();
            fi.name = name;
            fi.version = version;
            mAvailableFeatures.put(name, fi);
        } else {
            fi.version = Math.max(fi.version, version);
        }
    }
 
    private void removeFeature(String name) {
        if (mAvailableFeatures.remove(name) != null) {
            Slog.d(TAG, "Removed unavailable feature " + name);
        }
    }
 
    void readPermission(XmlPullParser parser, String name)
            throws IOException, XmlPullParserException {
        if (mPermissions.containsKey(name)) {
            throw new IllegalStateException("Duplicate permission definition for " + name);
        }
 
        final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
        final PermissionEntry perm = new PermissionEntry(name, perUser);
        mPermissions.put(name, perm);
 
        int outerDepth = parser.getDepth();
        int type;
        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
               && (type != XmlPullParser.END_TAG
                       || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG
                    || type == XmlPullParser.TEXT) {
                continue;
            }
 
            String tagName = parser.getName();
            if ("group".equals(tagName)) {
                String gidStr = parser.getAttributeValue(null, "gid");
                if (gidStr != null) {
                    int gid = Process.getGidForName(gidStr);
                    perm.gids = appendInt(perm.gids, gid);
                } else {
                    Slog.w(TAG, " without gid at "
                            + parser.getPositionDescription());
                }
            }
            XmlUtils.skipCurrentTag(parser);
        }
    }

你可能感兴趣的:(#,CM311-1A,查询添加切换删除用户,安卓用户及应用权限机制,创建/切换,多用户流程解析,权限控制以及权限组详解,安卓权限等级分类及配置)