Android 多用户
下面几篇介绍的不错,推荐看看
https://www.jianshu.com/p/3ad2163f7d34 Android 9.x 多用户机制 1 #Profile user创建过程
https://www.jianshu.com/p/12dd5408943a Android 9.x多用户机制 2 #Profile User启动过程
https://www.jianshu.com/p/4aaa181a44ec Android9.x多用户机制#Profile user 桌面图标显示过程
多用户相关 adb 命令:
查看支持最多用户数
adb shell pm get-max-users
查询系统所有用户
adb shell pm list users
创建新用户
adb shell pm create-user user_name
移除指定id用户
adb shell pm remove-user user_id
安装应用到某个用户
adb install –user USER_ID name.apk
多用户切换:
adb shell am switch-user USER_ID
//一般0是本机机主,10或者11 或及以后 都是新用户
切换到userid 12的用户
https://www.itcodemonkey.com/article/6169.html Android 多用户 —— 从入门到应用分身 (上)
// 第一步,创新新用户:
$ adb shell pm craete-user 'test-user' // 第二步,得道新用户的userId:
$ adb shell dumpsys user
...
UserInfo{10:test-user:0} serialNo=1001
...
// 得道UserId = 10 // 第三步, 启动新用户:
$ adb shell am start-user 10 // 第四步, 将新用户切到前台来:
$ adb shell am switch-user 10 // 第五步, 校验切换用户成功:
$ adb shell dumpsys activity a | grep 'Hist '
* Hist #0: ActivityRecord{8636857 u10 com.meizu.flyme.launcher/.Launcher t1100001} // 看到桌面"com.meizu.flyme.launcher/.Launcher"运行在"u10"即运行在userId=10的用户下, 说明新用户正处与前台. // ps: 如切回主用户不赘述, 可自行查询 "adb shell pm / am"命令.
安装应用到 影子用户
// 首先要获取影子用户的userID:
$ adb shell dumpsys user
...
UserInfo{10:FlymeParallelSpace:30} serialNo=10
... // UserInfo{10:FlymeParallelSpace:30}表示:
// userId=10,
// userName="FlymeParallelSpace",
// flags=0x30
于是我们得到影子用户的UserId // 如微信已经安装了, 使用adb命令重安装到影子用户:
$ adb shell pm install -r --user 10 `adb shell pm path com.tencent.mm | awk -F':' '{print $2}'`
// "--user 10" 指定安装userId为10. // 或者调用API:
// PMS.installExistingPackageAsUser()
// PMS.installPackageAsUser()
分别启动主用户和影子用户下的微信
// 首先找到微信的首页Activity:
$ adb shell dumpsys package com.tencent.mm | grep "android.intent.action.MAIN:" -A 5
android.intent.action.MAIN:
8f8990c com.tencent.mm/.ui.LauncherUI filter 3d82835
Action: "android.intent.action.MAIN"
Category: "android.intent.category.LAUNCHER"
Category: "android.intent.category.MULTIWINDOW_LAUNCHER"
AutoVerify=false
// 得到首页Activity为"com.tencent.mm/.ui.LauncherUI" //于是启动影子用户下的微信为:
$ adb shell am start --user 10 com.tencent.mm/.ui.LauncherUI
// "--user 10"为指定userId为10, 不指定则默认为主用户, 即userId=0为默认.
// ps: 并不是所有可指定userId的命令都这样设定.
// 如 "am force-stop"命令是默认情况下杀所有用户下进程, 而非仅杀主用户下进程. // 启动主用户下微信:
$ adb shell am start --user 0 com.tencent.mm/.ui.LauncherUI
或:
$ adb shell am start com.tencent.mm/.ui.LauncherUI 检查微信进程:
$ adb shell ps | grep com.tencent.mm
u10_a110 19794 11620 2157444 185912 SyS_epoll_ 00eb0ce428 S com.tencent.mm
u10_a110 19882 11620 1832116 121932 SyS_epoll_ 00eb0ce428 S com.tencent.mm:push
u0_a110 19989 11620 2151704 194924 SyS_epoll_ 00eb0ce428 S com.tencent.mm
u0_a110 20072 11620 1830048 122600 SyS_epoll_ 00eb0ce428 S com.tencent.mm:push
可以看到,微信出现两组进程组, 一组在u0_a110用户下, 一组在u10_a110用户下。且观察界面可以看到他们同时运行在同一个桌面下。基于多用户, 我们很容易将创建了任意应用(微信)的分身乃至多开(多创建几个影子用户即可)。
adb shell ps | grep u10
u10_a26 20491 11620 1274140 99920 SyS_epoll_ 00eb0ce428 S com.meizu.flyme.input
u10_a35 20507 11623 1987644 138320 SyS_epoll_ 75ce5b88a0 S com.android.systemui
u10_system 20599 11623 1822584 112340 SyS_epoll_ 75ce5b88a0 S com.meizu.flyme.xtemui
u10_a35 20630 11623 1805016 98724 SyS_epoll_ 75ce5b88a0 S com.android.systemui:recents
u10_a97 20826 11623 1808060 112752 SyS_epoll_ 75ce5b88a0 S com.meizu.flyme.weather
u10_a3 20839 11623 1800452 120916 SyS_epoll_ 75ce5b88a0 S android.process.acore
u10_a12 24815 11623 1777532 77556 SyS_epoll_ 75ce5b88a0 S android.process.media
https://blog.csdn.net/stephen8341/article/details/43196519 android多用户下应用安装详解三(特殊需求实现)
https://blog.csdn.net/stephen8341/article/details/43195621 android多用户下应用安装详解二(开机读取流程)
https://blog.csdn.net/stephen8341/article/details/43192015 android多用户下应用安装详解一(新应用安装情况)
Android P
Settings.java
/**
* Creates a new {@code PackageSetting} object.
* Use this method instead of the constructor to ensure a settings object is created
* with the correct base.
*/
static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg,
PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
UserHandle installUser, boolean allowInstall, boolean instantApp,
boolean virtualPreload, String parentPkgName, List<String> childPkgNames,
UserManagerService userManager,
String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
final PackageSetting pkgSetting;
if (originalPkg != null) {
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
+ pkgName + " is adopting original package " + originalPkg.name);
pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
pkgSetting.childPackageNames =
(childPkgNames != null) ? new ArrayList<>(childPkgNames) : null;
pkgSetting.codePath = codePath;
pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
pkgSetting.parentPackageName = parentPkgName;
pkgSetting.pkgFlags = pkgFlags;
pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
pkgSetting.primaryCpuAbiString = primaryCpuAbi;
pkgSetting.resourcePath = resourcePath;
pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
// NOTE: Create a deeper copy of the package signatures so we don't
// overwrite the signatures in the original package setting.
pkgSetting.signatures = new PackageSignatures();
pkgSetting.versionCode = versionCode;
pkgSetting.usesStaticLibraries = usesStaticLibraries;
pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
// Update new package state.
pkgSetting.setTimeStamp(codePath.lastModified());
} else {
pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
parentPkgName, childPkgNames, 0 /*sharedUserId*/, usesStaticLibraries,
usesStaticLibrariesVersions);
pkgSetting.setTimeStamp(codePath.lastModified());
pkgSetting.sharedUser = sharedUser;
// If this is not a system app, it starts out stopped.
if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
if (DEBUG_STOPPED) {
RuntimeException e = new RuntimeException("here");
e.fillInStackTrace();
Slog.i(PackageManagerService.TAG, "Stopping package " + pkgName, e);
}
List<UserInfo> users = getAllUsers(userManager);
final int installUserId = installUser != null ? installUser.getIdentifier() : 0;
if (users != null && allowInstall) {
for (UserInfo user : users) {
// By default we consider this app to be installed
// for the user if no user has been specified (which
// means to leave it at its original value, and the
// original default value is true), or we are being
// asked to install for all users, or this is the
// user we are installing for.
final boolean installed = installUser == null
|| (installUserId == UserHandle.USER_ALL
&& !isAdbInstallDisallowed(userManager, user.id))
|| installUserId == user.id;
pkgSetting.setUserState(user.id, 0, COMPONENT_ENABLED_STATE_DEFAULT,
installed,
true /*stopped*/,
true /*notLaunched*/,
false /*hidden*/,
false /*suspended*/,
null /*suspendingPackage*/,
null /*dialogMessage*/,
null /*suspendedAppExtras*/,
null /*suspendedLauncherExtras*/,
instantApp,
virtualPreload,
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
0, PackageManager.INSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/);
}
}
}
if (sharedUser != null) {
pkgSetting.appId = sharedUser.userId;
} else {
// Clone the setting here for disabled system packages
if (disabledPkg != null) {
// For disabled packages a new setting is created
// from the existing user id. This still has to be
// added to list of user id's
// Copy signatures from previous setting
pkgSetting.signatures = new PackageSignatures(disabledPkg.signatures);
pkgSetting.appId = disabledPkg.appId;
// Clone permissions
pkgSetting.getPermissionsState().copyFrom(disabledPkg.getPermissionsState());
// Clone component info
List<UserInfo> users = getAllUsers(userManager);
if (users != null) {
for (UserInfo user : users) {
final int userId = user.id;
pkgSetting.setDisabledComponentsCopy(
disabledPkg.getDisabledComponents(userId), userId);
pkgSetting.setEnabledComponentsCopy(
disabledPkg.getEnabledComponents(userId), userId);
}
}
}
}
}
return pkgSetting;
}
PackageManagerService.java
final boolean createNewPackage = (pkgSetting == null);
if (createNewPackage) {
final String parentPackageName = (pkg.parentPackage != null)
? pkg.parentPackage.packageName : null;
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
// REMOVE SharedUserSetting from method; update in a separate call
pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting,
disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
user, true /*allowInstall*/, instantApp, virtualPreload,
parentPackageName, pkg.getChildPackageNames(),
UserManagerService.getInstance(), usesStaticLibraries,
pkg.usesStaticLibrariesVersions);
}
UserMS.java
userInfo.lastLoggedInFingerprint = Build.FINGERPRINT; //记录上一次指纹信息,可以用于ota?
/**
* Called right before a user is started. This gives us a chance to prepare
* app storage and apply any user restrictions.
*/
public void onBeforeStartUser(int userId) {
UserInfo userInfo = getUserInfo(userId);
if (userInfo == null) {
return;
}
final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE, migrateAppsData); // if (userId != UserHandle.USER_SYSTEM) {
synchronized (mRestrictionsLock) {
applyUserRestrictionsLR(userId);
}
}
}
Android 多用户的更多相关文章
- Android 多用户多缓存的简单处理方案
需求:1.在缓存中记录用户登录信息.例如:用户名,密码 2.记录用户操作数据.例如:是否记住用户名密码.设置7天内自动登录等 简单设计:1)使用sqlite设计一张用户数据表,有用户名.密码.操作数据 ...
- android 5.0 创建多用户 双开多开应用(2)
上一讲 讲了如何创建一个user android 5.0 创建多用户 双开多开应用(1) 为什么要创建User 例如window 系统创建了一个user 会在当前用户下进行操作,而android 多 ...
- android 5.0 创建多用户 双开多开应用(1)
Andriod5.0多用户 双开应用 android多用户是5.0之后有的,类似windows的账户系统 不过官方还没有完全确认,API大都是hide状态 我这里提供一种方式并不适用所有的,由于我们有 ...
- Android 虚拟多开系列二——技术原理
目录 Android虚拟多开应用有哪些? Android虚拟多开应用技术原理有哪几类? Android虚拟多开需求分析 反虚拟多开技术 ...
- 修改Android源码实现原生应用双开,应用多开
1. 准备 把某系统双开的两个app的信息进行对比 1.1 目录的对比 1.1.1 data目录对比 原应用: /data/user/0/com.luoyesiqiu.crackme/files 被复 ...
- [Android]从Launcher开始启动App流程源码分析
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...
- Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池
前言:由于最近在做SDK的功能,需要设计线程池.看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到 ...
- Android应用开发基础之十二:版本控制
为什么需要版本控制? 场景1: 你的代码正常工作 你改了其中的几行代码 程序出了问题 你把代码改回来 程序还是不能正常工作——为什么? 场景2: 你的程序昨天还能正常运行 昨天晚上你修改了很多内容,做 ...
- Android进程绝杀技--forceStop
一.概述 1.1 引言 话说Android开源系统拥有着App不计其数,百家争鸣,都想在这"大争之世"寻得系统存活的一席之地.然则系统资源有限,如若都割据为王,再强劲的CPU也会忙 ...
随机推荐
- Oracle 12cR1 RAC集群安装(二)--使用图形界面安装
Oracle 12cR1 RAC集群安装文档:Oracle 12cR1 RAC集群安装(一)--环境准备Oracle 12cR1 RAC集群安装(二)--使用图形界面安装Oracle 12cR1 RA ...
- php string常用函数
<?php $a[]='a'; $a[]='b'; $a[]='C'; echo "</br>"; /* implode — 将一个一维数组的值转化为字符串 说明 ...
- python coding style guide 的快速落地实践——业内python 编码风格就pep8和谷歌可以认作标准
python coding style guide 的快速落地实践 机器和人各有所长,如coding style检查这种可自动化的工作理应交给机器去完成,故发此文帮助你在几分钟内实现coding st ...
- 基于Docker容器使用NVIDIA-GPU训练神经网络
一,nvidia K80驱动安装 1, 查看服务器上的Nvidia(英伟达)显卡信息,命令lspci |grep NVIDIA 05:00.0 3D controller: NVIDIA Corpo ...
- G1垃圾收集器系统化说明【官方解读】
还是继续G1官网解读,上一次已经将这三节的东东读完了,如下: 所以接一来则继续往下读: Reviewing Generational GC and CMS[回顾一下CMS收集器] The Concur ...
- python笔记42-http请求命令行工具(httpie)
前言 通常我们需要快速的测试某个接口通不通,一般linux上用curl去发http请求,但是这个命令行工具语法有点复杂了,不够直观. python有一个给人类使用的requests库,非常的简单方便. ...
- c++实现按行读取文本文件
包含头文件fstream既可以读又可以写(我的理解是头文件fstream中包含ifstream和ofstream),可以同时创建ifstream对象和ofstream对象,分别实现读写:也可以直接创建 ...
- 2019-2020-1 20199301《Linux内核原理与分析》第九周作业
第八章 进程的切换和系统的一般执行过程 进程的调度实际与进程的切换 ntel定义的中断类型 硬中断:就是CPU的两根引脚(可屏蔽中断和不可屏蔽中断) 软中断/异常:包括除零错误.系统调用.调试断点等在 ...
- Linux——CentOS7没有第二张网卡的配置信息
前言 为了一个实验做测试,在VMware中配置了环境,但是配置了双网卡后发现第二张网卡没有配置文件. 都是些基本命令就不写了,图里也有. 系统 : CentOS7.6 步骤 查看网卡信息 使用ip a ...
- Array.Sort(valuesArry);
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...