PackageInstaller源码分析(二)

来源:互联网 发布:淘宝平铺白底图怎么弄 编辑:程序博客网 时间:2024/04/29 16:32

  上篇PackageInstaller源码分析(一)对PackageInstaller源码分析了一半,本篇接着分析,分别分析对于App更新和新App安装的处理。

一、 新App的安装

  新App的安装从installNewPackageLI()开始,我们查看其源码。

private void installNewPackageLI(PackageParser.Package pkg,        int parseFlags, int scanFlags, UserHandle user,        String installerPackageName, PackageInstalledInfo res) {    ````    boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();    synchronized(mPackages) {        if (mSettings.mRenamedPackages.containsKey(pkgName)) {            // A package with the same name is already installed, though            // it has been renamed to an older name.  The package we            // are trying to install should be installed as an update to            // the existing one, but that has not been requested, so bail.            res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName                    + " without first uninstalling package running as "                    + mSettings.mRenamedPackages.get(pkgName));            return;        }        if (mPackages.containsKey(pkgName)) {            // Don't allow installation over an existing package with the same name.            res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName                    + " without first uninstalling.");            return;        }    }    ````    }

  这段代码首先判断了是否建立了App的data数据目录。而后,对于要安装的app的包名若已存在的情况作了处理。对于包名已经存在,但是并非更新的情况,抛出异常。

  紧接着,程序调用scanPackageLI()重新获取apk的Package结构,这个函数比较复杂,就是从apk中提取有用信息,这个和PackageInstaller源码分析(一)中PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile)应该是非常类似的,后面再选择一个分析。而后,调用updateSettingsLI()将安装的App写入设置,最后判断App是否完整安装,若没有完整安装,则调用deletePackageLI()将未完整安装的App删除,包括建立的data目录和cache目录。关于这个,在此处不做分析,这些操作和卸载App一定的相似性。以下将分析安装app细节的核心函数updateSettingsLI()。

private void installNewPackageLI(PackageParser.Package pkg,        int parseFlags, int scanFlags, UserHandle user,        String installerPackageName, PackageInstalledInfo res) {    ````    try {         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,                 System.currentTimeMillis(), user);         updateSettingsLI(newPackage, installerPackageName, null, null, res);         // delete the partially installed application. the data directory will have to be         // restored if it was already existing         if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {             // remove package from internal structures.  Note that we want deletePackageX to             // delete the package data and cache directories that it created in             // scanPackageLocked, unless those directories existed before we even tried to             // install.             deletePackageLI(pkgName, UserHandle.ALL, false, null, null,                     dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,                             res.removedInfo, true);         }     } catch (PackageManagerException e) {         res.setError("Package couldn't be installed in " + pkg.codePath, e);     }}

  查看updateSettingsLI()源码,对mPackages的同步处理中,调用了Setting的writeLPr()方法。

private void updateSettingsLI(PackageParser.Package newPackage,         String installerPackageName,        int[] allUsers, boolean[] perUserInstalled,        PackageInstalledInfo res) {      String pkgName = newPackage.packageName;      synchronized (mPackages) {          //write settings. the installStatus will be incomplete at this stage.          //note that the new package setting would have already been          //added to mPackages. It hasn't been persisted yet.          mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);          mSettings.writeLPr();      }       ````    }

  Setting的writeLPr()方法非常重要,于是我们查看其源码。writrLpr其实是更新三个文件,分别是:/data/system/packages.xml,/data/system/packages.list以及/data/system/users/0/package-restrictions.xml,我们把这三个代码分别提取出来,然后在说明一下这三个文件的作用。

void writeLPr() {    ```   try {      FileOutputStream fstr = new FileOutputStream(mSettingsFilename);      BufferedOutputStream str = new BufferedOutputStream(fstr);      //XmlSerializer serializer = XmlUtils.serializerInstance();      XmlSerializer serializer = new FastXmlSerializer();      serializer.setOutput(str, "utf-8");      serializer.startDocument(null, true);      serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);      serializer.startTag(null, "packages");      serializer.startTag(null, "last-platform-version");      serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform));      serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform));      serializer.attribute(null, "fingerprint", mFingerprint);      ````       mBackupSettingsFilename.delete();  FileUtils.setPermissions(mSettingsFilename.toString(),          FileUtils.S_IRUSR|FileUtils.S_IWUSR          |FileUtils.S_IRGRP|FileUtils.S_IWGRP,          -1, -1);     ````}

  上面的代码更新了/data/system/packages.xml,并把文件权限设置成了660。

这里写图片描述

这里写图片描述

  接着更新/data/system/packages.list,并把文件权限置为640。

void writeLPr() {    ````                JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);    final File writeTarget = journal.chooseForWrite();    fstr = new FileOutputStream(writeTarget);    str = new BufferedOutputStream(fstr);    try {        FileUtils.setPermissions(fstr.getFD(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);        StringBuilder sb = new StringBuilder();        for (final PackageSetting pkg : mPackages.values()) {            if (pkg.pkg == null || pkg.pkg.applicationInfo == null) {                Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");                continue;            }      ````}

这里写图片描述

这里写图片描述

  最后调用writeAllUsersPackageRestrictionsLPr()更新/data/system/users/0/package-restrictions.xml文件。

void writeLPr() {    ````    writeAllUsersPackageRestrictionsLPr();   return;}

这里写图片描述

  writeLPr()函数的工作做完了,就是更新了三个文件。下面简单说名义下三个文件的内容或者用途。和App权限关系最为密切的是/data/system/packages.xml文件。这个文件中包含所有Android设备中定义的权限,包括Android系统定义的权限以及App自定的权限。然后定义了子标签,这里面包括了申请的权限信息,签名信息等。还定义了app共享进程或者资源的一些信息。而所有的App证书中公钥的信息记录在了子标签。这些信息应该在API调用检查权限的时候用到。

  /data/system/packages.list记录的是App的简要信息,具体用在哪里不知道,但里面对应条目的内容主要是:pkgName,userId,debugFlag,dataPath,seinfo(seinfo label for the app (assigned at install time)),gids。根据源码里面的注释,这个文件应该在system/core/run-as/run-as.c,system/core/sdcard/sdcard.c以及external/libselinux/src/android.c:pack_info_init()中用到。

  /data/system/users/0/package-restrictions.xml文件作用不熟,Google了一下,好像和系统默认程序以及隐藏的组件(可以实现App隐藏,和setComponentEnabled()Api有关)有关,不再次过多阐述,但是值得研究一下。

  到这里,新App的安装流程基本走完了,于是总结一下。

  • installNewPackage()首先对待安装的App的包名进行了校验,如果包名在设备中已经存在,则抛出异常;
  • 接着调用scanPackageLI()解析了apk的结构,并且存在了Package类结构中。
  • 然后,通过updateSettingsLI()函数调用writeLPr()函数更新了三个重要文件,/data/system/packages.xml,/data/system/packages.list以和/data/system/users/0/package-restrictions.xml。其中/data/system/packages.xml包含了设备中所有定义的权限以及App申请的权限。
  • 最后,完成三个文件更新后,判断App是否完整安装,若没有,则调用deletePackageLI()删除app对应的data,cache目录等。

二、 App的更新

  App的更新操作从replacePackageLI()函数开始,查看replacePackageLI()函数源码。

private void replacePackageLI(PackageParser.Package pkg,        int parseFlags, int scanFlags, UserHandle user,        String installerPackageName, PackageInstalledInfo res) {    ````     synchronized(mPackages) {        oldPackage = mPackages.get(pkgName);        if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);        PackageSetting ps = mSettings.mPackages.get(pkgName);        if (ps == null || !ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {            // default to original signature matching            if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)                != PackageManager.SIGNATURE_MATCH) {                res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,                        "New package has a different signature: " + pkgName);                return;            }        } else {            if(!checkUpgradeKeySetLP(ps, pkg)) {                res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,                        "New package not signed by keys specified by upgrade-keysets: "                        + pkgName);                return;            }        }        // In case of rollback, remember per-user/profile install state        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;        }    }    ````} 

  这段代码对程序升级过程中的证书进行了验证,同样地,和前面PackageInstaller源码分析(一)中描述得一样,程序更新应该也有两种方式,即是否使用keyset来更新。对于这一细节的讨论,后面如果有分析App安装过程的强烈需求再讨论。

  紧接着,就更新是系统App更新还是普通App更新做了不同的处理。

private void replacePackageLI(PackageParser.Package pkg,        int parseFlags, int scanFlags, UserHandle user,        String installerPackageName, PackageInstalledInfo res) {    `````    if (sysPkg) {       replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,               user, allUsers, perUserInstalled, installerPackageName, res);   } else {       replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,               user, allUsers, perUserInstalled, installerPackageName, res);   }    ````}

  对系统App更新,调用replaceSystemPackageLI()函数。

private void replaceSystemPackageLI(PackageParser.Package deletedPackage,        PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,        int[] allUsers, boolean[] perUserInstalled,        String installerPackageName, PackageInstalledInfo res) {    ````    killApplication(packageName, oldPkg.applicationInfo.uid, "replace sys pkg");    ```}

  程序首先调用了killApplilcation()函数,其主要工作就是调用ActivityManager来杀死需要把被替换的App进程。

private void replaceSystemPackageLI(PackageParser.Package deletedPackage,        PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,        int[] allUsers, boolean[] perUserInstalled,        String installerPackageName, PackageInstalledInfo res) {    ````    removePackageLI(oldPkgSetting, true);    ````    deleteCodeCacheDirsLI(packageName);    ````}

  然后,程序前后调用removePackageLI()和deleteCodeCacheDirsLI()删除和待更新App相关的数据目录和缓存数据。

  如果前面的调用过程都成功,则和前面安装新App一样,调用updateSettingsLI()函数把App相关信息写入设置,包括更新/data/system/packages.xml,/data/system/packages.list以及/data/system/users/0/package-restrictions.xml这三个文件。
  反之,如果前面的调用过程失败,即更新系统App失败,则删除带安装的apk文件,然后恢复之前被删除的待更新App,最后,和前面安装新App一样,调用writeLPr()重新将旧的App信息更新到设置当中,这段代码如下。

private void replaceSystemPackageLI(PackageParser.Package deletedPackage,        PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,        int[] allUsers, boolean[] perUserInstalled,        String installerPackageName, PackageInstalledInfo res) {    `````   if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {      // Re installation failed. Restore old information      // Remove new pkg information      if (newPackage != null) {          removeInstalledPackageLI(newPackage, true);      }      // Add back the old system package      try {          scanPackageLI(oldPkg, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);      } catch (PackageManagerException e) {          Slog.e(TAG, "Failed to restore original package: " + e.getMessage());      }      // Restore the old system information in Settings      synchronized (mPackages) {          if (disabledSystem) {              mSettings.enableSystemPackageLPw(packageName);          }          if (updatedSettings) {              mSettings.setInstallerPackageName(packageName,                      oldPkgSetting.installerPackageName);          }          mSettings.writeLPr();      }  }}

  看完对系统App的更新,再看对非系统App的更新,其代码逻辑应该和非系统App有很大的相似性,对非系统App的更新从replaceNonSystemPackageLI()函数开始。

private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,        PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,        int[] allUsers, boolean[] perUserInstalled,        String installerPackageName, PackageInstalledInfo res) {    ````   if (!deletePackageLI(pkgName, null, true, null, null, PackageManager.DELETE_KEEP_DATA,          res.removedInfo, true)) {      // If the existing package wasn't successfully deleted      res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");      deletedPkg = false;  } else {      // Successfully deleted the old package; proceed with replace.      // If deleted package lived in a container, give users a chance to      // relinquish resources before killing.      if (isForwardLocked(deletedPackage) || isExternal(deletedPackage)) {          if (DEBUG_INSTALL) {              Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");          }          final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };          final ArrayList<String> pkgList = new ArrayList<String>(1);          pkgList.add(deletedPackage.applicationInfo.packageName);          sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);      }      deleteCodeCacheDirsLI(pkgName);      try {          final PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,                  scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);          updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);          updatedSettings = true;      } catch (PackageManagerException e) {          res.setError("Package couldn't be installed in " + pkg.codePath, e);      }  }  ````}

  程序首先调用deletePackageLI()删除带更新的App的包信息,如果删除失败,则抛出异常。否则,调用deletePackageLI()删除旧的App相关的缓存信息,然后调用updateSettingsLI()将新App的相关信息接入设置文件。当然,和前面一样,也更新了/data/system/packages.xml,/data/system/packages.list以及/data/system/users/0/package-restrictions.xml这三个文件。

  如果更新不成功,和更新系统App一样,也要首先删除新的App的apk文件以及其他相关信息。然后恢复之前删除的旧的App包括旧App的data和cache等。最后,还是调用writeLPr()重新把旧的App信息重新更新到/data/system/packages.xml,/data/system/packages.list以及/data/system/users/0/package-restrictions.xml这三个文件。

private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,        PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,        int[] allUsers, boolean[] perUserInstalled,        String installerPackageName, PackageInstalledInfo res) {    `````     if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {        // remove package from internal structures.  Note that we want deletePackageX to        // delete the package data and cache directories that it created in        // scanPackageLocked, unless those directories existed before we even tried to        // install.        if(updatedSettings) {            if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);            deletePackageLI(                    pkgName, null, true, allUsers, perUserInstalled,                    PackageManager.DELETE_KEEP_DATA,                            res.removedInfo, true);        }        // Since we failed to install the new package we need to restore the old        // package that we deleted.        if (deletedPkg) {            if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);            File restoreFile = new File(deletedPackage.codePath);            // Parse old package            boolean oldOnSd = isExternal(deletedPackage);            int oldParseFlags  = mDefParseFlags | PackageParser.PARSE_CHATTY |                    (isForwardLocked(deletedPackage) ? PackageParser.PARSE_FORWARD_LOCK : 0) |                    (oldOnSd ? PackageParser.PARSE_ON_SDCARD : 0);            int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;            try {                scanPackageLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null);            } catch (PackageManagerException e) {                Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "                        + e.getMessage());                return;            }            // Restore of old package succeeded. Update permissions.            // writer            synchronized (mPackages) {                updatePermissionsLPw(deletedPackage.packageName, deletedPackage,                        UPDATE_PERMISSIONS_ALL);                // can downgrade to reader                mSettings.writeLPr();            }            Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");        }    `````}

  总之,系统App更新和非系统App更新是非常相似的,对于里面跟多函数的具体分析本篇没有贴出来,多半是个权限关系不大。

三、 总结

  在一开始分析PackageInstaller的时候,我们就明确了我们的研究目标。就是在App安装过程中的权限获得过程。App的权限是在安装期间授予的,并且,我们知道Android的权限机制是基于Linux的文件权限基础的。也就是说,在platform.xml文件中,是定义了权限和Linux底层的group组的对应关系的。只有对应Linux组的成员可以使用对应的Android权限。所以,通过分析App安装流程我们想一下App是怎么实现了获得权限的。其重点还是在writeLPr()函数,里面每次安装App都会更新三个文件/data/system/packages.xml,/data/system/packages.list以及/data/system/users/0/package-restrictions.xml。packages.xml包含App申请和定义的权限,packages.list包含了App属于的groupID。所以从packages.list就可以看到,当App的gid对应的permission时(这个在platform.xml中有定义),其就自然或得了对应的权限。

  整个分析过程就这样了,第一次分析Android源码,还一起分析了三个功能。中间还有不少细节有点模糊,后面再继续慢慢消化!

0 0
原创粉丝点击