Android应用双开实现中用到installExistingPackageAsUser方法,下面分析下该方法的流程
public int installExistingPackageAsUser(String packageName, int userId) {
//检查安装app权限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
enforceCrossUserPermission(uid, userId,
true /* requireFullPermission */, true /* checkShell */,
"installExistingPackage for user " + userId);
//是否被限制安装app
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
}
long callingId = Binder.clearCallingIdentity();
try {
boolean installed = false;
// writer
//写入相关xml文件
synchronized (mPackages) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
if (!pkgSetting.getInstalled(userId)) {
pkgSetting.setInstalled(true, userId);
pkgSetting.setHidden(false, userId);
mSettings.writePackageRestrictionsLPr(userId);
installed = true;
}
}
if (installed) {
if (pkgSetting.pkg != null) {
synchronized (mInstallLock) {
// We don't need to freeze for a brand new install
prepareAppDataAfterInstallLIF(pkgSetting.pkg);
}
}
//发送ACTION_PACKAGE_ADDED广播
sendPackageAddedForUser(packageName, pkgSetting, userId);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
return PackageManager.INSTALL_SUCCEEDED;
}
由于app已经安装,所以该方法实际上是创建app在新user目录下的相关数据
private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
final PackageSetting ps;
//KernelMapping对应的文件夹/config/sdcardfs,sdcardfs是用于app文件访问的文件系统
//sdcardfs详细可见https://www.zhihu.com/question/24112546
synchronized (mPackages) {
ps = mSettings.mPackages.get(pkg.packageName);
mSettings.writeKernelMappingLPr(ps);
}
final UserManager um = mContext.getSystemService(UserManager.class);
UserManagerInternal umInternal = getUserManagerInternal();
for (UserInfo user : um.getUsers()) {
...
prepareAppDataLIF(pkg, user.id, flags);
...
}
}
两个工作:一是处理sdcardfs,二是prepareAppDataLIF
private void prepareAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
prepareAppDataLeafLIF(pkg, userId, flags);
//childPackages是AndroidManifest中package属性指定的,不过sdk文档中并无package属性的说明。
//在PackageParser.java代码中还可以看到一些sdk中没有说明的属性。
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
prepareAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
}
}
继续看prepareAppDataLeafLIF
private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
...
try {
//createAppData之前已详细分析过
mInstaller.createAppData(volumeUuid, packageName, userId, flags,
appId, app.seinfo, app.targetSdkVersion);
} catch (InstallerException e) {
...
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
//获取app data inode然后存储,代码省略
...
}
prepareAppDataContentsLeafLIF(pkg, userId, flags);
}
主要工作还是通过installd创建app相关目录,最后一行prepareAppDataContentsLeafLIF,从名字看不出是啥作用
private void prepareAppDataContentsLeafLIF(PackageParser.Package pkg, int userId, int flags) {
...
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
// Create a native library symlink only if we have native libraries
// and if the native libraries are 32 bit libraries. We do not provide
// this symlink for 64 bit libraries.
//创建32位native库的连接,向前兼容
if (app.primaryCpuAbi != null && !VMRuntime.is64BitAbi(app.primaryCpuAbi)) {
final String nativeLibPath = app.nativeLibraryDir;
try {
mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
nativeLibPath, userId);
} catch (InstallerException e) {
Slog.e(TAG, "Failed to link native for " + packageName + ": " + e);
}
}
}
}
代码就一个功能,处理64位机器对32位库的兼容性问题。linkNativeLibraryDirectory实际调用了installd的linklib方法
int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
{
...
if (symlink(asecLibDir, libsymlink) < 0) {
ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,
strerror(errno));
rc = -errno;
goto out;
}
...
}
使用了linux C库标准方法symlink建立符号连接
ps:createAppData在创建user,开启user和安装app的时候都会调用:创建user的时候只是针对system app,因为对于所有user来说system app是必须的;安装app的时候是针对要安装的app,这个很好理解;最后开启user的时候从代码看是会有一个特殊的合并system app data目录的操作(system app对所有用户来说是一样的,所以没必要搞多个目录占地方)。