下面几篇介绍的不错,推荐看看

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 多用户的更多相关文章

  1. Android 多用户多缓存的简单处理方案

    需求:1.在缓存中记录用户登录信息.例如:用户名,密码 2.记录用户操作数据.例如:是否记住用户名密码.设置7天内自动登录等 简单设计:1)使用sqlite设计一张用户数据表,有用户名.密码.操作数据 ...

  2. android 5.0 创建多用户 双开多开应用(2)

    上一讲 讲了如何创建一个user android 5.0 创建多用户 双开多开应用(1) 为什么要创建User  例如window 系统创建了一个user 会在当前用户下进行操作,而android 多 ...

  3. android 5.0 创建多用户 双开多开应用(1)

    Andriod5.0多用户 双开应用 android多用户是5.0之后有的,类似windows的账户系统 不过官方还没有完全确认,API大都是hide状态 我这里提供一种方式并不适用所有的,由于我们有 ...

  4. Android 虚拟多开系列二——技术原理

    目录         Android虚拟多开应用有哪些?         Android虚拟多开应用技术原理有哪几类?         Android虚拟多开需求分析         反虚拟多开技术 ...

  5. 修改Android源码实现原生应用双开,应用多开

    1. 准备 把某系统双开的两个app的信息进行对比 1.1 目录的对比 1.1.1 data目录对比 原应用: /data/user/0/com.luoyesiqiu.crackme/files 被复 ...

  6. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  7. Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池

    前言:由于最近在做SDK的功能,需要设计线程池.看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到 ...

  8. Android应用开发基础之十二:版本控制

    为什么需要版本控制? 场景1: 你的代码正常工作 你改了其中的几行代码 程序出了问题 你把代码改回来 程序还是不能正常工作——为什么? 场景2: 你的程序昨天还能正常运行 昨天晚上你修改了很多内容,做 ...

  9. Android进程绝杀技--forceStop

    一.概述 1.1 引言 话说Android开源系统拥有着App不计其数,百家争鸣,都想在这"大争之世"寻得系统存活的一席之地.然则系统资源有限,如若都割据为王,再强劲的CPU也会忙 ...

随机推荐

  1. docker 安装 oracle

    docker 安装 oracle 部署链接:https://www.linuxidc.com/Linux/2017-03/141760.htm 本次使用docker安装oracle 1.搜索可使用的镜 ...

  2. 【HCIA Gauss】学习汇总-数据库管理(SQL语法 数据类型 函数)-4

    DDL data definition language 数据库定义语言 定义修改等DML data manipulation language 数据库操控语言 增删改 DCL data crontr ...

  3. Kali下的内网劫持(一)

    ettercap利用计算机在局域网内进行通信的ARP协议的缺陷进行攻击,在目标主机与服务器之间充当中间人,嗅探两者之间的数据流量,从中窃取用户的数据信息,那么接下来我就给大家演示一下客户端的图片是怎么 ...

  4. 关于free的使用疑惑

    #include <stdio.h> #include <string.h> #include <stdlib.h> #include "mainc26. ...

  5. node基础学习——path的处理与路径转换

    处理与转换路径path normalize该方法将非标准路径字符串转换为标准路径字符串,在转换过程中执行以下操作: ①解析路径字符串中的’..’字符串与’.’字符串,返回解析后的标准路径. ②将多个斜 ...

  6. <? extends T> 及 <? super T> 重温

    <? extends T> 及<? super T> 重温 本文针对泛型中<? extends T> 及<? super T>的主要区别及使用用途进行讨 ...

  7. 从输入 URL 到页面展示到底发生了什么?

    1.输入地址 当我们开始在浏览器中输入网址的时候,浏览器其实就已经在智能的匹配可能得 url 了,他会从历史记录,书签等地方,找到已经输入的字符串可能对应的 url,然后给出智能提示,让你可以补全ur ...

  8. springboot,eclipse打包出错处理

    打jar包报错 [WARNING] The POM for org.apache.maven.plugins:maven-clean-plugin:jar:3.1.0 is invalid, tran ...

  9. SpringMVC_原理(转)

    在整个Spring MVC框架中,DispatcherServlet处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应的工作.具体流程为:1)客户端发送http请求,web应用服务器接收到这 ...

  10. Python 3.6 抓取微博m站数据

    Python 3.6 抓取微博m站数据 2019.05.01 更新内容 containerid 可以通过 "107603" + user_id 组装得到,无需请求个人信息获取: 优 ...