为什么80%的码农都做不了架构师?>>>   

这一章我们介绍APK的卸载过程,从前一章分析安装APK的过程,我们应该大致了解这里的卸载的过程如下:

1.从PMS的内部结构上删除acitivity、service、provider等信息

2.删除code、library和resource等信息

3.调用installd删除/data/data/packageName以及/data/dalvik-cache下面的文件

4.更新Settings中的package信息

当我们在Settings中的应用页面找到一个安装了的应用程序,并点击卸载后,就会发送一个Intent给UninstallerActivity,在UninstallerActivity最后会启动UninstallAppProgress的initView方法,并调用如下卸载函数:

[java]
getPackageManager().deletePackage(mAppInfo.packageName, observer,  mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);

上面的mAllUsers默认是false。getPackageManager()函数的实现在ContextImpl.java,它最后会调用到ApplicantPackageManger.java的deletePackage方法:

[java]
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {  try {  mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags);  } catch (RemoteException e) {  // Should never happen!  }
}

通过Binder调用,我们来看PMS中的deletePackageAsUser方法:

[java]
public void deletePackageAsUser(final String packageName,  final IPackageDeleteObserver observer,  final int userId, final int flags) {  mContext.enforceCallingOrSelfPermission(  android.Manifest.permission.DELETE_PACKAGES, null);  final int uid = Binder.getCallingUid();  if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {  try {  observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);  } catch (RemoteException re) {  }  return;  }  mHandler.post(new Runnable() {  public void run() {  mHandler.removeCallbacks(this);  final int returnCode = deletePackageX(packageName, userId, flags);  if (observer != null) {  try {  observer.packageDeleted(packageName, returnCode);  } catch (RemoteException e) {  Log.i(TAG, "Observer no longer exists.");  } //end catch  } //end if  } //end run  });
}

在deletePackageAsUser方法中,首先做权限检查,然后就调用deletePackageX方法去执行卸载任务:

[java]
private int deletePackageX(String packageName, int userId, int flags) {  final PackageRemovedInfo info = new PackageRemovedInfo();  final boolean res;  boolean removedForAllUsers = false;  boolean systemUpdate = false;  int[] allUsers;  boolean[] perUserInstalled;  synchronized (mPackages) {  PackageSetting ps = mSettings.mPackages.get(packageName);  allUsers = sUserManager.getUserIds();  perUserInstalled = new boolean[allUsers.length];  for (int i = 0; i < allUsers.length; i++) {  perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;  }  }  synchronized (mInstallLock) {  res = deletePackageLI(packageName,  (flags & PackageManager.DELETE_ALL_USERS) != 0  ? UserHandle.ALL : new UserHandle(userId),  true, allUsers, perUserInstalled,  flags | REMOVE_CHATTY, info, true);  systemUpdate = info.isRemovedPackageSystemUpdate;  if (res && !systemUpdate && mPackages.get(packageName) == null) {  removedForAllUsers = true;  }  if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate  + " removedForAllUsers=" + removedForAllUsers);  }  if (res) {  info.sendBroadcast(true, systemUpdate, removedForAllUsers);  }  Runtime.getRuntime().gc();  if (info.args != null) {  synchronized (mInstallLock) {  info.args.doPostDeleteLI(true);  }  }  return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}

deletePackageX在这里我们只考虑当前只有一个user的情况,来看deletePackageLI的实现:

[java] view plaincopy
private boolean deletePackageLI(String packageName, UserHandle user,  boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,  int flags, PackageRemovedInfo outInfo,  boolean writeSettings) {  PackageSetting ps;  boolean dataOnly = false;  int removeUser = -1;  int appId = -1;  synchronized (mPackages) {  ps = mSettings.mPackages.get(packageName);  if (ps == null) {  Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");  return false;  }  if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null  && user.getIdentifier() != UserHandle.USER_ALL) {  ps.setUserState(user.getIdentifier(),  COMPONENT_ENABLED_STATE_DEFAULT,  false, //installed  true,  //stopped  true,  //notLaunched  false, //blocked  null, null, null);  if (!isSystemApp(ps)) {  if (ps.isAnyInstalled(sUserManager.getUserIds())) {  } else {  removeUser = user.getIdentifier();  appId = ps.appId;  mSettings.writePackageRestrictionsLPr(removeUser);  }  }  }  boolean ret = false;  mSettings.mKeySetManager.removeAppKeySetData(packageName);  if (isSystemApp(ps)) {  ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,  flags, outInfo, writeSettings);  } else {  // Kill application pre-emptively especially for apps on sd.  killApplication(packageName, ps.appId, "uninstall pkg");  ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,  allUserHandles, perUserInstalled,  outInfo, writeSettings);  }  return ret;
}

在deletePackageLI函数中根据是否是systemApp调用不同的流程,如果是systemApp,则调用deleteSystemPackageLI完成卸载;如果非systemApp,则调用deleteInstalledPackageLI完成卸载,当然在卸载之前,首先会调用AMS的killApplication方法先让这个APP停止运行。我们主要介绍非systemApp的卸载过程,来看deleteInstalledPackageLI方法的实现:

[java] view plaincopy
private boolean deleteInstalledPackageLI(PackageSetting ps,  boolean deleteCodeAndResources, int flags,  int[] allUserHandles, boolean[] perUserInstalled,  PackageRemovedInfo outInfo, boolean writeSettings) {  if (outInfo != null) {  outInfo.uid = ps.appId;  }  removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags, writeSettings);  if (deleteCodeAndResources && (outInfo != null)) {  outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,  ps.resourcePathString, ps.nativeLibraryPathString);  }  return true;
}

在deleteInstalledPackageLI方法中,分为两步去卸载应用:第一步删除/data/data下面的数据目录,并从PMS的内部数据结构上清除当前卸载的package信息;第二步就删除code和resource文件。我们先来看第一步:

[java] view plaincopy
private void removePackageDataLI(PackageSetting ps,  int[] allUserHandles, boolean[] perUserInstalled,  PackageRemovedInfo outInfo, int flags, boolean writeSettings) {  String packageName = ps.name;  removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);  final PackageSetting deletedPs;  synchronized (mPackages) {  deletedPs = mSettings.mPackages.get(packageName);  if (outInfo != null) {  outInfo.removedPackage = packageName;  outInfo.removedUsers = deletedPs != null  ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true)  : null;  }  }  if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {  removeDataDirsLI(packageName);  schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);  }

removePackageDataLI用于删除应用的/data/data数据目录,并且从PMS内部数据结构里面清除package的信息。首先调用removePackageLI从PMS内部的数据结构上删除要卸载的package信息:

[java] view plaincopy
void removePackageLI(PackageSetting ps, boolean chatty) {  synchronized (mPackages) {  mPackages.remove(ps.name);  if (ps.codePathString != null) {  mAppDirs.remove(ps.codePathString);  }  final PackageParser.Package pkg = ps.pkg;  if (pkg != null) {  cleanPackageDataStructuresLILPw(pkg, chatty);  }  }
}

cleanPackageDataStructuresLILPw用于将package的providers、services、receivers、activities等信息去PMS的全局数据结构上移除,这部分代码比较简单。如果没有设置DELETE_KEEP_DATA这个flag,就会首先调用removeDataDirsLI去删除/data/data下面的目录:

[java] view plaincopy
private int removeDataDirsLI(String packageName) {  int[] users = sUserManager.getUserIds();  int res = 0;  for (int user : users) {  int resInner = mInstaller.remove(packageName, user);  if (resInner < 0) {  res = resInner;  }  }  final File nativeLibraryFile = new File(mAppLibInstallDir, packageName);  NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);  if (!nativeLibraryFile.delete()) {  Slog.w(TAG, "Couldn't delete native library directory " + nativeLibraryFile.getPath());  }  return res;
}

这里首先调用installd的remove方法去删除/data/data下面的目录。然后去删除/data/app-lib下面的应用程序的library信息,但因为这里的nativeLibraryFile为/data/app-lib/packageName,和前面介绍的APK安装过程中的目录/data/app-lib/packageName-num不一样,所以实际上,这里并没有真正的去删除library目录。先来看installd的remove方法:

[cpp] view plaincopy
static int do_remove(char **arg, char reply[REPLY_MAX])
{  return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */
}  int uninstall(const char *pkgname, userid_t userid)
{  char pkgdir[PKG_PATH_MAX];  if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid))  return -1;  return delete_dir_contents(pkgdir, 1, NULL);
}  int delete_dir_contents(const char *pathname,  int also_delete_dir,  const char *ignore)
{  int res = 0;  DIR *d;  d = opendir(pathname);  if (d == NULL) {  ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno));  return -errno;  }  res = _delete_dir_contents(d, ignore);  closedir(d);  if (also_delete_dir) {  if (rmdir(pathname)) {  ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno));  res = -1;  }  }  return res;
}

create_pkg_path方法构造/data/data/packageName的文件路径名,然后调用delete_dir_contents来删除文件内容以及目录,前面介绍过,/data/data/packageName的文件其实都是符号链接,所以_delete_dir_contents的实现中都是调用unlinkat去删除这些符号链接。回到removePackageDataLI中,接着调用schedulePackageCleaning来安排清理动作:

[java] view plaincopy
void schedulePackageCleaning(String packageName, int userId, boolean andCode) {  mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,  userId, andCode ? 1 : 0, packageName));
}

这里向PackageHandler发送START_CLEANING_PACKAGE消息,PMS会调用ContainService的函数去删除/storage/sdcard0/Android/data和/storage/sdcard0/Android/media下面与package相关的文件,有兴趣可以去看一下这部分的code。接着来看removePackageDataLI方法:

[java] view plaincopy
synchronized (mPackages) {  if (deletedPs != null) {  if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {  if (outInfo != null) {  outInfo.removedAppId = mSettings.removePackageLPw(packageName);  }  if (deletedPs != null) {  updatePermissionsLPw(deletedPs.name, null, 0);  if (deletedPs.sharedUser != null) {  // remove permissions associated with package  mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);  }  }  clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);  }  }  if (writeSettings) {  mSettings.writeLPr();  }
}
if (outInfo != null) {  removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);
}

这里首先从Settings中删除PackageSettings的信息:

[java] view plaincopy
int removePackageLPw(String name) {  final PackageSetting p = mPackages.get(name);  if (p != null) {  mPackages.remove(name);  if (p.sharedUser != null) {  p.sharedUser.removePackage(p);  if (p.sharedUser.packages.size() == 0) {  mSharedUsers.remove(p.sharedUser.name);  removeUserIdLPw(p.sharedUser.userId);  return p.sharedUser.userId;  }  } else {  removeUserIdLPw(p.appId);  return p.appId;  }  }  return -1;
}

removePackageLPw首先从mPackages这个map中删除PackageSettings信息,如果不存在sharedUser,则从mUserIds这个数组中删除对应的Package UID信息;如果存在sharedUser,则首先检查这个sharedUser是否所有的package都已经被卸载了,如果都被卸载了,这个sharedUser也就可以删除。然后removePackageDataLI调用updatePermissionsLPw去检查mPermissionTrees和mPermissions两个数组中的权限是否是被删除的Package提供,如果有,则删除。Settings的updateSharedUserPermsLPw方法用于清除sharedUser不用的gid信息,防止权限泄露:

[java] view plaincopy
void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {  SharedUserSetting sus = deletedPs.sharedUser;  for (String eachPerm : deletedPs.pkg.requestedPermissions) {  boolean used = false;  if (!sus.grantedPermissions.contains(eachPerm)) {  continue;  }  for (PackageSetting pkg:sus.packages) {  if (pkg.pkg != null &&  !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&  pkg.pkg.requestedPermissions.contains(eachPerm)) {  used = true;  break;  }  }  if (!used) {  sus.grantedPermissions.remove(eachPerm);  }  }  int newGids[] = globalGids;  for (String eachPerm : sus.grantedPermissions) {  BasePermission bp = mPermissions.get(eachPerm);  if (bp != null) {  newGids = PackageManagerService.appendInts(newGids, bp.gids);  }  }  sus.gids = newGids;
}

循环的从要被卸载的Package所在的sharedUser组中找被申请的权限是否还被同一组的其它package使用,如果没有使用者,就从sharedUser的grantedPermissions删除。clearPackagePreferredActivitiesLPw与AMS相关,我们留到以后再来介绍。在removePackageDataLI方法最好调用Settings.writeLPr()方法将改动的信息写到Package.xml中。到这里,我们前面所说的deleteInstalledPackageLI方法中的第一步已经完成,来看第二部分:

[java] view plaincopyif (deleteCodeAndResources && (outInfo != null)) {  outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,  ps.resourcePathString, ps.nativeLibraryPathString);  }  private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath,  String nativeLibraryPath) {  final boolean isInAsec;  if (installOnSd(flags)) {  isInAsec = true;  } else if (installForwardLocked(flags)  && !fullCodePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {  isInAsec = true;  } else {  isInAsec = false;  }  if (isInAsec) {  return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath,  installOnSd(flags), installForwardLocked(flags));  } else {  return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);  }
}

这里根据安装目录的不同,分别构造FileInstallArgs和AsecInstallArgs来完成code和resource资源的清除。这里我们主要介绍卸载内部存储空间上面的APK,来看FileInstallArgs的doPostDeleteLI方法:

[java] view plaincopy
boolean doPostDeleteLI(boolean delete) {  cleanUpResourcesLI();  return true;
}  void cleanUpResourcesLI() {  String sourceDir = getCodePath();  if (cleanUp()) {  int retCode = mInstaller.rmdex(sourceDir);  if (retCode < 0) {  Slog.w(TAG, "Couldn't remove dex file for package: "  +  " at location "  + sourceDir + ", retcode=" + retCode);  // we don't consider this to be a failure of the core package deletion  }  }
}

cleanUpResourcesLI方法中首先调用cleanUp方法去删除code、resource以及library文件:

[java] view plaincopy
private boolean cleanUp() {  boolean ret = true;  String sourceDir = getCodePath();  String publicSourceDir = getResourcePath();  if (sourceDir != null) {  File sourceFile = new File(sourceDir);  if (!sourceFile.exists()) {  Slog.w(TAG, "Package source " + sourceDir + " does not exist.");  ret = false;  }  sourceFile.delete();  }  if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {  final File publicSourceFile = new File(publicSourceDir);  if (!publicSourceFile.exists()) {  Slog.w(TAG, "Package public source " + publicSourceFile + " does not exist.");  }  if (publicSourceFile.exists()) {  publicSourceFile.delete();  }  }  if (libraryPath != null) {  File nativeLibraryFile = new File(libraryPath);  NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);  if (!nativeLibraryFile.delete()) {  Slog.w(TAG, "Couldn't delete native library directory " + libraryPath);  }  }  return ret;
}

然后cleanUpResourcesLI调用installd的rmdex方法去删除存在/data/dalvik-cache文件:

[cpp] view plaincopy
static int do_rm_dex(char **arg, char reply[REPLY_MAX])
{  return rm_dex(arg[0]); /* pkgname */
}  int rm_dex(const char *path)
{  char dex_path[PKG_PATH_MAX];  if (validate_apk_path(path)) return -1;  if (create_cache_path(dex_path, path)) return -1;  ALOGV("unlink %s\n", dex_path);  if (unlink(dex_path) < 0) {  ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));  return -1;  } else {  return 0;  }
}

create_cache_path依据path构造/data/dalvik-cache下的文件目录,调用unlink去删除文件。到这里卸载APK的deletePackageAsUser函数就已经分析完了。这时会通过observer把卸载结果返回给UninstallAppProgre

转载于:https://my.oschina.net/ldhy/blog/390794

Android PackageManagerService分析三:卸载APK相关推荐

  1. Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

    摘要:上一节讲解了PKMS的 权限扫描,扫描/system/etc/permissions中的xml,存入相应的结构体中,供之后权限管理使用. 这一节主要来讲讲APK的扫描. 阅读本文大约需要花费15 ...

  2. Android Telephony分析(三) ---- RILJ详解

    前言 本文主要讲解RILJ工作原理,以便更好地分析代码,分析业务的流程.  这里说的RILJ指的是RIL.java (frameworks\opt\telephony\src\java\com\And ...

  3. Android PackageManagerService分析一:PMS的启动

    从这一章开始,我们来分析Android的PackageManagerService,后面简称PMS.PMS用来管理所有的package信息,包括安装.卸载.更新以及解析AndroidManifest. ...

  4. android协议分析,对一个apk的协议分析

    过年的时候看见一个apk,因为肺炎在屋头宅的发霉,拿出来玩玩,小白图个乐,大佬乎喷. Apk信息 这个app的协议是明文,只有一个sign签名. 1APK的协议 长链接的心跳 返回值 {"c ...

  5. Android Telephony分析(六) ---- 接口扩展(实践篇)

    本文将结合前面五篇文章所讲解的知识,综合起来,实现一个接口扩展的功能.  如果还没有阅读过前面五篇文章的内容,请先阅读:  <Android Telephony分析(一) - Phone详解 & ...

  6. Android apk 分析工具:APK Analyzer

    前言 Android studio 2.2 版本后自带有一个分析工具:APK Analyzer.这是一个专门分析 apk 的神器,主要作用如下: 1. (重要) 直观的看到 apk 中各个文件的大小 ...

  7. 如何在Android模拟器上安装和卸载APK程序包

    如何在Android模拟器上安装和卸载APK程序包 Android, 程序包, APK, 卸载, 模拟 一,安装APK          安装APK当然首先是要有模拟器,和要安装的APK包,这个不多废 ...

  8. Android性能优化——使用 APK Analyzer 分析你的 APK

    Android Studio 2.2包含了APK Analyzer,通过它我们能够直观地看到APK的组成.使用APK Analyzer不仅能够减少你花在debug上的时间,而且还能减少你的APK大小. ...

  9. Android 系统(42)---Android7.0 PowerManagerService亮灭屏分析(三)

    Android7.0 PowerManagerService亮灭屏分析(三) 在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在D ...

最新文章

  1. get_headers()请求https报错解决思路
  2. Ubuntu16安装CUDA9.0+Anaconda+Tensorflow1.8GPU(2018.08.20最新)
  3. redis如何解决秒杀超卖java_Spring Boot + redis解决商品秒杀库存超卖,看这篇文章就够了...
  4. Python Django 一对一多表查询关联表字段
  5. linux下php扩展curl的安装
  6. Java agent初探
  7. Mysql+Navicat for Mysql
  8. java bean spring_JavaBean和Spring bean傻傻分不清楚
  9. cc笔记_robotium_01
  10. AJAX学习笔记(基本使用,请求参数传递,获取服务端响应,错误处理,低版本IE浏览器缓存问题及解决)
  11. P1144 最短路计数
  12. 编程必备基础知识-计算机组成原理-01概述篇-笔记
  13. S7-1200做智能IO设备
  14. C语言基础丨(六)程序结构——顺序结构【1】
  15. 笔记本java稳定wifi信号_笔记本wifi网速不稳定的解决方法
  16. Google浏览器任务栏图标变白
  17. Kubernetes Events介绍(下)
  18. 怎么看Mac电脑的序列号,Mac序列号是多少
  19. H3C光模块相关命令和检测方法
  20. 软件项目外包找我(附案例)

热门文章

  1. FileSystemObject ADO WScript.Shell
  2. C++读取XML树的建立和遍历
  3. Jenkins 内部服务器遭访问且被部署密币挖机
  4. Google Update Service 被曝提权 0day,谷歌拒绝修复
  5. CVE-2020-0601漏洞详细分析
  6. 转发:关于数据权限设计的思考
  7. Transformer原理解析——一种Open AI和DeepMind都在用的神经网络架构
  8. python基础学习--字符串和文件数据处理--附代码
  9. 浅谈Java中的栈和堆
  10. 十万个为什么之为什么大家都说dubbo