系列目录 :第一篇:功能演进与系统架构总览 | 第二篇:核心数据结构深度剖析 | 第三篇:用户创建与删除流程全链路分析 | 第四篇:用户切换机制源码剖析 | 第五篇:应用安装与管理隔离机制 | 第六篇:数据隔离与存储机制详解 | 第七篇:系统服务的多用户感知处理 | [第八篇:广播分发与 Intent 路由的用户隔离](#第八篇:广播分发与 Intent 路由的用户隔离) | 第九篇:权限系统与多用户的交互机制 | [第十篇:Work Profile 企业模式与开发实战指南](#第十篇:Work Profile 企业模式与开发实战指南)
一、核心问题
- 同一个包,不同用户的"启用/停用/已安装"状态存在哪里?
- 新用户创建后,系统应用是怎么"自动出现在"新用户里的?
- 删除用户时,"只有这个用户装过"的第三方 APK 会被删掉吗?
答案都围绕一个数据结构:PackageSetting 里按用户维度的 PackageUserState。本篇按"数据结构 → 新用户初始化 → 用户删除清理"的顺序展开,修正原稿中整段虚构的 API(installPackageAsUser、getInstalledPackagesAsUser、INSTALL_ALL_USERS、UserSetting 均不存在)。
二、数据结构:每个包 × 每个用户 的状态
2.1 PackageSetting 与 PackageUserState
源码路径 :frameworks/base/services/core/java/com/android/server/pm/PackageSettingBase.java
java
abstract class PackageSettingBase extends SettingBase {
final String name;
final String realName;
File codePath;
// ... 版本、签名、安装状态等包级字段 ...
private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
// 核心:按 userId 维度保存每个用户下的包状态
private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
public PackageUserState readUserState(int userId) {
PackageUserState state = userState.get(userId);
if (state != null) {
return state;
}
return DEFAULT_USER_STATE;
}
private PackageUserState modifyUserState(int userId) {
PackageUserState state = userState.get(userId);
if (state == null) {
state = new PackageUserState();
userState.put(userId, state);
}
return state;
}
void setEnabled(int state, int userId, String callingPackage) {
modifyUserState(userId).enabled = state;
}
int getEnabled(int userId) {
return readUserState(userId).enabled;
}
void setInstalled(boolean inst, int userId) {
modifyUserState(userId).installed = inst;
}
boolean getInstalled(int userId) {
return readUserState(userId).installed;
}
boolean getStopped(int userId) {
return readUserState(userId).stopped;
}
// ... setNotLaunched / setHidden / setSuspended / setBlockUninstall /
// setEnabledComponents / setDisabledComponents ...
}
PackageUserState(frameworks/base/core/java/android/content/pm/PackageUserState.java)的字段:
java
public final class PackageUserState implements Parcelable {
public int enabled = COMPONENT_ENABLED_STATE_DEFAULT;
public boolean installed;
public long ceDataInode;
public boolean stopped;
public boolean notLaunched;
public boolean hidden;
public boolean suspended;
public String lastDisableAppCaller;
public ArraySet<String> enabledComponents;
public ArraySet<String> disabledComponents;
public boolean blockUninstall;
// ... 域名验证、应用链接状态 ...
}
关键设计 :包状态 = 包级字段(
codePath、versionCode、签名)+ 每用户PackageUserState(installed、enabled、stopped、hidden、组件级开关)。APK 本体是跨用户共享的 (一份/data/app),隔离的是"用户状态 + 数据目录"。这也是 AOSP 7 不支持"同一包不同用户不同版本"的根源------版本号是包级字段。
2.2 包级状态持久化:per-user 的 packages.xml
java
// Settings.java
File getUserPackagesStateFile(int userId) {
return new File(Environment.getDataSystemDirectory(),
"packages" + File.separator + "packages" + userId + XML_SUFFIX);
}
PMS 把每个用户的包状态单独写到 /data/system/packages/packages<userId>.xml(主用户是 packages.xml)。removeUserLPw 删除时连备份一起删:
java
// Settings.java
void removeUserLPw(int userId) {
for (Entry<String, PackageSetting> entry : mPackages.entrySet()) {
entry.getValue().removeUser(userId); // 清每个包的 PackageUserState
}
mPreferredActivities.remove(userId);
File file = getUserPackagesStateFile(userId);
file.delete();
file = getUserPackagesStateBackupFile(userId);
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
writePackageListLPr();
}
关键设计 :per-user 的
packages<id>.xml意味着"用户 A 对某应用的设置(停用、组件开关、隐停)"完全不落在用户 B 的文件里------文件级隔离,崩溃恢复也按用户独立。
三、新用户:系统应用如何"自动出现"
第三篇已看到创建链路尾部的两个 PMS 调用:mPm.createNewUser(userId) 与 mPm.onNewUserCreated(userId)。
3.1 createNewUser → Settings.createNewUserLI
源码路径 :frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
java
// Called by UserManagerService
void createNewUser(int userId) {
synchronized (mInstallLock) {
mSettings.createNewUserLI(this, mInstaller, userId);
}
synchronized (mPackages) {
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
applyFactoryDefaultBrowserLPw(userId);
primeDomainVerificationsLPw(userId);
}
}
// Settings.java
void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer,
int userHandle) {
String[] volumeUuids;
String[] names;
int[] appIds;
// ...
synchronized (mPackages) {
Collection<PackageSetting> packages = mPackages.values();
for (PackageSetting ps : packages) {
if (ps.pkg == null || ps.pkg.applicationInfo == null) {
continue;
}
// Only system apps are initially installed.
ps.setInstalled(ps.isSystem(), userHandle);
// 累积参数,锁外统一调 installer
// ...
}
}
for (int i = 0; i < packagesCount; i++) {
if (names[i] == null) {
continue;
}
final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
try {
installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i],
seinfos[i], targetSdkVersions[i]);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to prepare app data", e);
}
}
synchronized (mPackages) {
applyDefaultPreferredAppsLPw(service, userHandle);
}
}
关键设计 :新用户初始化 = 遍历所有
PackageSetting,只有系统应用(ps.isSystem())在新用户下置installed=true------这就是"新用户只有系统应用"的来源。installer.createAppData通过 Installer 服务(native 层installd)创建/data/user/<id>/<pkg>数据目录。注意ps.setInstalled(ps.isSystem(), userHandle)是对每个包 执行的:第三方包installed=false但enabled等字段保持默认,用户装包后才翻转为 true。
3.2 onNewUserCreated:权限补齐
java
// PackageManagerService.java
void onNewUserCreated(final int userId) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
// If permission review for legacy apps is required, we represent
// dangerous permissions for such apps as always granted runtime
// permissions to keep per user flag state whether review is needed.
if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
| UPDATE_PERMISSIONS_REPLACE_ALL);
}
}
关键设计 :新用户创建后,PMS 为新用户跑一遍默认权限策略 + 危险权限(legacy 应用的 review 状态)。运行时权限的授予本身就是 per-user 的(
mRuntimePermissionsPersistence),这里补齐的是"策略层"而非"用户授予记录"。
3.3 第三方应用的安装
AOSP 7 的公开安装 API 是 PackageManager.getPackageArchiveInfo / IPackageManager.installPackage(..., userId)------安装请求本身就带 userId ,PMS 安装成功后只对目标用户置 ps.setInstalled(true, userId) 并创建该用户的数据目录。同一 APK 被另一个用户安装时,复用已有 PackageSetting,只改自己的 PackageUserState:
java
// PackageSettingBase.java
void setInstalled(boolean inst, int userId) {
modifyUserState(userId).installed = inst;
}
boolean isAnyInstalled(int[] users) {
for (int user : users) {
if (readUserState(user).installed) {
return true;
}
}
return false;
}
关键设计 :跨用户共享判定靠
isAnyInstalled------只要还有一个用户installed=true,APK 文件就不删。这是"用户 A 卸载不影响用户 B"的数据基础(见下文removeUnusedPackagesLPw)。
四、用户删除:PMS 侧的清理
第三篇的删除链路中 mPm.cleanUpUser(this, userHandle) 在这里展开:
java
// PackageManagerService.java
void cleanUpUser(UserManagerService userManager, int userHandle) {
synchronized (mPackages) {
mDirtyUsers.remove(userHandle);
mUserNeedsBadging.delete(userHandle);
mSettings.removeUserLPw(userHandle); // 清包状态 + 删 packages<id>.xml
mPendingBroadcasts.remove(userHandle);
mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
removeUnusedPackagesLPw(userManager, userHandle);
}
}
/** We're removing userHandle and would like to remove any downloaded packages
* that are no longer in use by any other user. */
private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
int[] users = userManager.getUserIds();
for (PackageSetting ps : mSettings.mPackages.values()) {
if (ps.pkg == null) continue;
// Skip over if system app
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
boolean keep = shouldKeepUninstalledPackageLPr(packageName);
if (!keep) {
for (int i = 0; i < users.length; i++) {
if (users[i] != userHandle && ps.getInstalled(users[i])) {
keep = true; // 其他用户还装着 → 保留
break;
}
}
}
if (!keep) {
mHandler.post(() -> deletePackageX(packageName, userHandle, 0));
}
}
}
关键设计 :删除用户时,非系统包 逐个检查"其他用户是否还在用"(
ps.getInstalled(otherUser)),全都不用了才deletePackageX删 APK;系统包跳过(设备级资源)。这就是"isAnyInstalled语义在删除路径上的落地"。
五、跨用户查询与限制
5.1 查询 API 的 userId 参数
AOSP 7 的 PackageManager 查询 API 直接带 userId 参数(不是原稿写的 getXxxAsUser 后缀方法):
java
// PackageManagerService.java
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
enforceCrossUserPermission(uid, userId,
false /* requireFullPermission */, false /* checkShell */, "get enabled");
synchronized (mPackages) {
return mSettings.getApplicationEnabledSettingLPr(packageName, userId);
}
}
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
if (!sUserManager.exists(userId)) return;
if (callingPackage == null) {
callingPackage = Integer.toString(Binder.getCallingUid());
}
setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);
}
关键设计 :跨用户查询/修改受
enforceCrossUserPermission约束------普通 App 只能操作自己用户,INTERACT_ACROSS_USERS(部分操作)或INTERACT_ACROSS_USERS_FULL(全部操作)才能越界(第九篇展开)。
5.2 安装限制
DISALLOW_INSTALL_APPS 用户限制在安装路径检查,阻止受限用户(如受限 profile)安装应用;DISALLOW_UNINSTALL_APPS 对应卸载。这两个限制由 UserManager.hasUserRestriction 查询,DPC 可按用户下发(第十篇展开)。
六、本篇总结
- 状态结构 :
PackageSetting(包级:codePath/version/签名)+SparseArray<PackageUserState>(用户级:installed/enabled/stopped/hidden/组件开关); - 持久化 :per-user 的
packages<id>.xml,文件级隔离; - 新用户 :
createNewUserLI只把系统包 置installed=true并由 installd 建数据目录;onNewUserCreated补权限策略; - APK 共享 :一份 APK 多用户共享,
isAnyInstalled决定是否可删;删除用户时removeUnusedPackagesLPw清理"孤儿"第三方包; - 版本隔离不存在:AOSP 7 同一包所有用户同版本(包级 versionCode)。
下一篇进入存储层:/data/user/<id> 与 _ce/_de 目录如何由 FBE 加密隔离,createAppData 的 native 路径长什么样。