installExistingPackageAsUser方法的分析
来源:互联网 发布:淘宝商家发货信息 编辑:程序博客网 时间:2024/06/08 10:41
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对所有用户来说是一样的,所以没必要搞多个目录占地方)。
阅读全文
0 0
- installExistingPackageAsUser方法的分析
- 分析死锁的方法
- 木马的分析方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析死锁的方法
- 分析的一些方法
- 分析死锁的方法
- C语言知识体系
- Java homework4
- Eclipse常用快捷键
- mxnet学习笔记-安装
- Java中的反射机制
- installExistingPackageAsUser方法的分析
- 学习区块链途径
- vptr的初始化语义学
- php正则表达式
- Linux常用命令之关机命令(halt poweroff shutdown)的区别
- 软键盘挡住webview输入框
- 问题记录一:eclipse 创建maven项目的时候总是提示invalid project description
- Eclipse:使用Javadoc导出项目的API文档
- 【DIY】代码调用栈