目录
安卓系统用户管理
查询用户
查看用户列表
查看当前登录用户
添加用户
创建新用户
切换用户
切换账号
删除用户
删除一个账户
安卓系统用户应用权限
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 对应的解析代码
查看用户列表
查看当前登录用户
非常注意:
多用户的创建、启动、停止等行为是系统级的
因此只有具有 root、system 权限的进程才能操作
创建新用户
然后发现盒子竟然也不能创建用户!!!
无奈只好换上旧手机:
如果之前创建多了也没关系 删除多余的用户之后重启手机 UID 就会重新计算:
切换账号
启动和切换用户语法如下:
启动指定用户:
切换成指定用户:
正在切换用户:
然后就发现无法调试了:
然后手机竟然开始首次登陆配置:
这个新建立的用户确确实实不允许 USB 调试:
无奈 只好重启手机之后默认登陆主账户再次开启调试功能:
删除一个账户
示例删除刚刚创建的 ranchui 用户:
Android 4.0 开始 Google 就开始在 Android 上布局多用户
UserManager 由此诞生 然而此时尚未对应的 Binder 服务 真正支持多用户是从 Android 4.2 开始
即使如此系统中也依然存在各类 Bug 和兼容性问题
直到 Android 6.0 多用户才比较完善
国内外的厂家也纷纷开始针对多用户这个噱头来作各类 花里胡哨 的操作
手机分身
分身应用
应用双开
等等等等 不得不说 国内的厂家在多用户这方面定制化到现在已经很是稳定和完善了
对于 Android 中的每一个进程都有一个单独的 uid、gid 以及 gids 集合
经过这三者 Android 系统实现了一套文件和数据访问权限规则系统
例如 访问某个文件 文件系统规定了该文件在磁盘中的 rwx 权限
uid
UID 是用户 id
在 Linux 上一个用户 uid 标识着一个给定的用户 而 Android 上也沿用了 Linux 用户的概念
root 用户 uid 为 0
system Uid 为 1000
而且每一个应用程序在安装时也被赋予了单独的 uid
这个 uid 将伴随着应用从安装到卸载包括缓存
gid
GID 是用户组 id
Linux 上规定每一个应用都应该有一个用户组
对于 Android 应用程序来讲每一个应用的所属用户组与 uid 相同
gids
GIDS 是应用在安装后所得到权限的 id 集合
在 Android 上每一个权限均可能对应一个或多个 group 而每一个 group 都有一个 gid name
所以 gids 就是经过对每一个 gid name 计算得出的 id 集合
查看 uid/gid/gids
获取 system_server 的 PID 然后根据这个 PID 查看 UID/GID/Groups :
应用的 appid 和 appuid
在 Android 中应用的 uid 是和当前的用户有关的
其 uid 的计算方式为:
在主用户中 uid 就等于 appId
为了多用户下的数据安全性
在每个新用户创建之初不管是 外部存储器/External Storage 还是 app data 目录
Android 都为其准备了独立的文件存储
新用户创建时 Android 在
/storage/emulated
/data/user
目录下为每个用户都创建了名为用户 id 的家目录
外部存储器 /storage/emulated/用户_UID 家目录
目录 /storage/emulated/ 下拥有不同的用户分区
当我们在代码中使用
获取外部存储路径时返回的就是当前用户下的对应目录
例如 userId = 11 则返回为
多用户下的 /storage 分区有不同的用户家目录
可以看出常用的 /sdcard 目录其实最终也是软链到了 /storage/emulated/0 目录:
内部存储器 /data/user/用户_UID 家目录
多用户下的 /data 分区也有不同用户的家目录
也是以用户 UID 命名
与 External Storage/外部存储器 相同
新用户创建时 Android 也会在 /data/user 目录下创建名为 userId 的目录
用于存储该用户中所有 App 的隐私数据
也可以看出来平常说到的 /data/data 目录其实也是软链到了 /data/user/0
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 中:
将新创建新 userId 固化到 /data/system/users/userlist.xml 中:
第三步 准备文件系统
通过 vold 这个 Android 存储守护进程为新用户进行文件系统加密
创建 /data/system/users/${userId} 并设置 0700 权限
创建 /data/misc/users/${userId} 并设置 0750 权限
第四步 为已安装应用准备数据目录并记录其组件和默认权限配置
在 /data/user/${userId}/ 下创建各个已安装应用的 package 目录
在 /data/system/users/${userId}/package-restrictions.xml 中写入非默认启动组件的信息
更新 /data/system/packages.list 文件主要是最后一串 gids 可能会改变
这个改变的可能性是根据 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
第六步 切换超时消息到达时需要继续进行的切换操作
解冻屏幕和输入
设置 Keyguard 如果切换用户设置了指纹 则需要开始监听指纹信息
通知监听者用户已经完成了切换
第七步 完成切换用户
如果是后台切换
则直接调用 UserController.finishUserBoot()
如果是前台切换
ActivityThread 会在 handleResumeActivity 时设置 Main 线程 MessageQueue 的 mIdleHandlers
在 MessageQueue 执行 next() 会检查该列表并最终调用到 AMS 的 activityIdle() 中
此时会检查正在切换的用户列表并调用最终调用到 UserController.finishUserBoot()
设置切换用户的状态为 STATE_RUNNING_LOCKED
安装时权限存储位置
安装 APP 时权限的获取记录存储在
/data/system/packages.xml
运行时权限存储位置
运行时权限的获取记录存储在
/data/system/users/$userId/runtime-permissions.xml
权限声明存储位置
Android 系统和应用安装后的权限声明保存在
/etc/permissions/
目录下:
如果要查看最常使用的 platform 权限可以:
可以简单看一下这个配置文件 每个我们常见的权限都可能对应一个或多个 group gid
而我们上面说的 gids 就是由这个 group gid 生成的集合
授予/移除 权限
示例为终端模拟器添加删除写文件权限:
其她常用权限:
User ID 和 Group ID
Android 系统上每一个独立的应用运行在不同的系统空间
以 User ID 和 Group ID 来标识
不同应用之间互相访问数据接口资源就牵涉到权限问题
关于用户 ID
那么用户 id 到底是什么:
简单的说用户 id 就是当前用户下为了各个应用之间数据共享和访问的
在 Android 系统中有些常用的 userid 是提前定义好的
例如 system 的用户 id 就是 1000 这个是在代码中提前定义好的
利用 dumpsys package 从指定应用信息中获取 userid 和 gids
示例使用 dumpsys package 命令获取 终端模拟器 这个应用的所有信息:
获取 userid 可以使用 dumpsys package 搭配 grep 筛选 userId 和 gids:
查看用户权限
不同用户具有的权限不同
使用 dumpsys user 命令可以查看所有的用户信息 例如 userId、name、restrictions 等等:
解释一下 访客用户/Guest 的默认权限限制:
这些权限可以在创建用户时规定也可以后期由系统动态设置
特殊权限的用户:
查看应用权限
不同用户下的 App 应用权限也是独立的
上面说了 uid 与 userId 存在一种计算关系
而在系统中对于权限控制也是根据 uid 和对应的 userId 来判定的
因此不同用户下相同应用可以具有不同的权限
查看所有已知的权限组和单独权限组的权限:
permissions 的参数可以组合使用:
例如:
示例:
再或者查看描述:
更简短的:
平台版本与 API 级别
CM311-1A 盒子 Android 9 所有已知的权限组
联系人权限组 android.permission-group.CONTACTS
android.permission-group.CONTACTS
电话权限组 android.permission-group.PHONE
android.permission-group.PHONE
日历权限组 android.permission-group.CALENDAR
android.permission-group.CALENDAR
通话记录权限组 android.permission-group.CALL_LOG
android.permission-group.CALL_LOG
相机权限组 android.permission-group.CAMERA
android.permission-group.CAMERA
身体传感器权限组 android.permission-group.SENSORS
android.permission-group.SENSORS
位置信息权限组 android.permission-group.LOCATION
android.permission-group.LOCATION
存储空间权限组 android.permission-group.STORAGE
android.permission-group.STORAGE
系统控制权限组 androidlogic.permission-group.SYSTEM_CONTROL
droidlogic.permission-group.SYSTEM_CONTROL
麦克风权限组 android.permission-group.MICROPHONE
android.permission-group.MICROPHONE
短信权限组 android.permission-group.SMS
android.permission-group.SMS
CM311-1A 盒子和手机设备都没有看到 WIFI 网络相关的权限
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 时发生了什么:
权限控制主要放置在 AndroidManifest.xml 文件中
最后镜像生成在 systemetcpermissionsplatform.xml 配置文件中
声明时权限 安全等级/protectionLevel 分类
不同用户下相同 App 能够独立运行是因为系统为她们创造了不同的运行环境和权限
protectionLevel 分为三类:
normal 是普通权限
在 AndroidManifest.xml 中声明就可以获取的权限
如 INTERNET 权限
dangerous 敏感权限
需要动态申请告知用户才能获取
signature|privileged 签名|特权
具有系统签名的系统应用才可以获取的权限
对应上方的安装在 /system/priv-app 的特权应用!
第一层是在开发人员编写代码时由应用设置 主要是修改 AndroidManifest.xml 文件
AndroidManifest.xml 是 APP 的运行配置文件 她是一个 XML 描述文件 指定了 APP 的运行配置信息
一般都存放在 APP 包下的 manifests 目录下
不过我也见过放在 src/main/res/AndroidManifest.xm 下面的
AndroidManifest.xml 文件的作用:
我们将 apk 文件后缀修改成 zip 就可以使用平常的解压工具进行解压了:
第一眼看到的就是 AndroidManifest.xml 配置文件:
AndroidManifest 官方解释是 应用清单 manifest 意思是货单
每个应用的根目录中都必须包含一个 并且文件名必须一模一样
这个文件中包含了 APP 的配置信息 系统需要根据里面的内容运行 APP 的代码 显示界面
AndroidManifest.xml 是每个 apk 文件解压后根目录下的一个文件
每个 apk 都必须包含一个 AndroidManifest.xml 文件 且名字必须与此完全一致.
示例 AndroidManifest.xml 中的一段配置代码:
AndroidManifest.xml 配置文件权限分类
通过 shareduserid 来实现数据共享有一个限制就是相同的签名
这个是很高要求的
一般都是同一个公司开发出来的 app 才能满足获取内置到第三方 ROM 里面去才能满足
通常的做法是通过 uses-permission 来实现 我们自己定义一个权限!
在需要被访问的地方加上这个权限限制这样就能到达目的!
signature 和 signatureOrSystem 要求是很高 一般的只有是相同公司开发出来的应用才能满足
AndroidManifest.xml 配置文件详解
我们在安装 Android 软件的时候系统会提示该软件所需要的权限
相对于其她系统 Android 的权限非常多
我们在开发软件的时候也需要声明相应的权限,比如希望软件能发短信,需要声明软件调用短信的权限
否则软件运行的时候就会报错
Android 的权限在 AndroidManifest.xml 文件里配置
AndroidManifest 文件中有四个标签与 permission 有关:
<permission>
<permission-group>
<permission-tree>
<uses-permission>
其中最常用的是 <uses-permission>
当我们需要获取某个权限的时候就必须在我们的 manifest 文件中声明 <uses-permission>
<permission> 和 <uses-permission> 的作用相似 两者之间的不同之处在于
<uses-permission> 是 android 预定义的权限
<permission> 是自己定义的权限
<permission> 定义方法如下:
解释一下:
<uses-permission> 是我们用的最多的 例如短信和电话权限的定义:
常见权限:
platform.xml 其实是将 aosp 中的配置文件直接拷贝到手机目录中
AOSP 全称 Android Open Source Project 中文意为 Android 开放源代码项目
应用包中 preferences.xml 文件的位置在 resxmlpreferences.xml
设备目录存放的位置在 /system/etc/permissions/platform.xml
在 aosp 中的存放位置是 framework/base/data/etc/platform.xml
示例一个 root 过的手机修改板子 sd 权限
编辑的是
文件 看到代码如下:
修改为:
再然后重启就行了
CM311-1A 的 platform.xml 配置文件详解
这是对 platform.xml 的解析 注意看注释:
platform.xml 对应的解析代码
xml 肯定是需要解析才能用的
这个 SystemConfig 就是来解析 platform.xml 然后供一个个系统接口用作返回值的依据: