美文网首页
Android PackageManagerService--0

Android PackageManagerService--0

作者: DarcyZhou | 来源:发表于2023-11-18 10:02 被阅读0次

    本文转载自:Android PackageManagerService总结(三) APK扫描流程

    本文基于Android 10源码分析

    前言

      PackageManagerService(简称PKMS),是Android系统中核心服务之一,管理着所有与package相关的工作,常见的比如安装、卸载应用, 信息查询等工作,主要完成以下核心功能:

    1. 解析AndroidManifest.xml清单文件,解析清单文件中的所有节点信息;

    2. 扫描本地文件,主要针对apk,主要是系统应用、本地安装应用等;

    3. 管理本地apk,主要包括安装、删除等等;

    4. 管理设备上安装的所有应用程序,并在系统启动时加载应用程序;

    5. 根据请求的Intent匹配到对应的Activity、Provider、Service,提供包含包名和Component的信息对象;

    6. 调用需要权限的系统函数时,检查程序是否具备相应权限从而保证系统安全;

    7. 提供应用程序的安装、卸载的接口。

    本篇文章重点介绍一下apk的扫描过程。

    1.扫描流程

      在上一篇文章介绍了PKMS的构造函数中调用了scanDirTracedLI方法来扫描某个目录的apk文件,在Android 10上扫描的目录有:

    /vendor/overlay
    /product/overlay
    /product_services/overlay
    /odm/overlay
    /oem/overlay
    /system/framework
    /system/priv-app
    /system/app
    /vendor/priv-app
    /vendor/app
    /odm/priv-app
    /odm/app
    /oem/app
    /oem/priv-app
    /product/priv-app
    /product/app
    /product_services/priv-app
    /product_services/app
    /product_services/priv-app
    

    (1)这里就从scanDirTracedLI()方法为入口来分析:

    // frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
        private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
            try {
                scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        }
    

    (2)接着调用到PKMS的scanDirLI方法
    scanDirLI()中使用了ParallelPackageParser的对象,ParallelPackageParser是一个队列,保存手机所有系统的apk,然后从这些队列里面取出apk,再调用PackageParser 解析进行解析。

    // frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
        private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
            final File[] files = scanDir.listFiles();
            ...
            //parallelPackageParser是一个队列,收集系统apk文件,
            //然后从这个队列里面一个个取出apk调用PackageParser解析
            try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                    mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                    mParallelPackageParserCallback)) {
                // Submit files for parsing in parallel
                int fileCount = 0;
                for (File file : files) {
                    //是Apk文件,或者是目录
                    final boolean isPackage = (isApkFile(file) || file.isDirectory())
                            && !PackageInstallerService.isStageName(file.getName());
                    //过滤掉非apk文件,如果不是则跳过继续扫描
                    if (!isPackage) {
                        // Ignore entries which are not packages
                        continue;
                    }
                    //把APK信息存入parallelPackageParser中的对象mQueue,
                    //PackageParser()函数赋给了队列中的pkg成员
                    parallelPackageParser.submit(file, parseFlags);
                    fileCount++;
                }
    
                // Process results one by one
                //从parallelPackageParser中取出队列apk的信息
                for (; fileCount > 0; fileCount--) {
                    ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                    Throwable throwable = parseResult.throwable;
                    int errorCode = PackageManager.INSTALL_SUCCEEDED;
    
                    if (throwable == null) {
                        // TODO(toddke): move lower in the scan chain
                        // Static shared libraries have synthetic package names
                        if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                            renameStaticSharedLibraryPackage(parseResult.pkg);
                        }
                        //调用scanPackageChildLI方法扫描一个特定的apk文件
                        //该类的实例代表一个APK文件,所以它就是和apk文件对应的数据结构。
                        try {
                            scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
                                    currentTime, null);
                        } catch (PackageManagerException e) {
                            errorCode = e.error;
                            Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
                        }
                    } else if (throwable instanceof PackageParser.PackageParserException) {
                        PackageParser.PackageParserException e = (PackageParser.PackageParserException)
                                throwable;
                        errorCode = e.error;
                        Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
                    } else {
                        throw new IllegalStateException("Unexpected exception occurred while parsing "
                                + parseResult.scanFile, throwable);
                    }
    
                    // Delete invalid userdata apps
                    //如果是非系统apk并且解析失败
                    if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
                            errorCode != PackageManager.INSTALL_SUCCEEDED) {
                        logCriticalInfo(Log.WARN,
                                "Deleting invalid package at " + parseResult.scanFile);
                        //非系统Package扫描失败,删除文件
                        removeCodePathLI(parseResult.scanFile);
                    }
                }
            }
        }
    

    (3)时序图

    PKMS03.png

    1.1 parallelPackageParser.submit()

      继续分析parallelPackageParser.submit(file, parseFlags)这个方法,把扫描路径中的APK等内容,放入队列mQueue,并把parsePackage()赋给ParseResult,用于后面的调用。

    // frameworks/base/services/core/java/com/android/server/pm/ParallelPackageParser.java
    public void submit(File scanFile, int parseFlags) {
        mService.submit(() -> {
            ParseResult pr = new ParseResult();
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
            try {
                PackageParser pp = new PackageParser();
                pp.setSeparateProcesses(mSeparateProcesses);
                pp.setOnlyCoreApps(mOnlyCore);
                pp.setDisplayMetrics(mMetrics);
                pp.setCacheDir(mCacheDir);
                pp.setCallback(mPackageParserCallback);
                pr.scanFile = scanFile;
                //这个方法获取PackageParser.Package对象
                pr.pkg = parsePackage(pp, scanFile, parseFlags);
            } catch (Throwable e) {
                pr.throwable = e;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            try {
                // 放入队列
                mQueue.put(pr);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                // Propagate result to callers of take().
                // This is helpful to prevent main thread from getting stuck waiting on
                // ParallelPackageParser to finish in case of interruption
                mInterruptedInThread = Thread.currentThread().getName();
            }
        });
    }
    

    (1)通过parsePackage进行apk解析,如果传入的packageFile是目录,调用parseClusterPackage()解析,如果传入的是APK文件,就调用parseMonolithicPackage()解析。

    // frameworks/base/core/java/android/content/pm/PackageParser.java
     public Package parsePackage(File packageFile, int flags, boolean useCaches)
                throws PackageParserExceparseClusterPackageption {
            ....
            if (packageFile.isDirectory()) {
                // 解析目录
                parsed = parseClusterPackage(packageFile, flags);
            } else {
                // 解析APK
                parsed = parseMonolithicPackage(packageFile, flags);
            }
            ....
    }
    

    (2)先来看看parseClusterPackage()方法

      作用:解析给定目录中包含的所有apk,将它们视为单个包。这还可以执行完整性检查,比如需要相同的包名和版本代码、单个基本APK和唯一的拆分名称。

      首先通过parseClusterPackageLite()对目录下的apk文件进行初步分析,主要区别是核心应用还是非核心应用。核心应用只有一个,非核心应用可以没有,或者多个,非核心应用的作用主要用来保存资源和代码。然后对核心应用调用parseBaseApk分析并生成Package。对非核心应用调用parseSplitApk,分析结果放在前面的Package对象中。

    // frameworks/base/core/java/android/content/pm/PackageParser.java
    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
        //获取应用目录的PackageLite对象,这个对象分开保存了目录下的核心应用以及非核心应用的名称
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
        //如果lite中没有核心应用,退出
        if (mOnlyCoreApps && !lite.coreApp) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + packageDir);
        }
    
        // Build the split dependency tree.
        //构建分割的依赖项树
        SparseArray<int[]> splitDependencies = null;
        final SplitAssetLoader assetLoader;
        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
            try {
                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
            }
        } else {
            assetLoader = new DefaultSplitAssetLoader(lite, flags);
        }
    
        try {
            final AssetManager assets = assetLoader.getBaseAssetManager();
            final File baseApk = new File(lite.baseCodePath);
            //对核心应用解析
            final Package pkg = parseBaseApk(baseApk, assets, flags);
            if (pkg == null) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                        "Failed to parse base APK: " + baseApk);
            }
    
            if (!ArrayUtils.isEmpty(lite.splitNames)) {
                final int num = lite.splitNames.length;
                pkg.splitNames = lite.splitNames;
                pkg.splitCodePaths = lite.splitCodePaths;
                pkg.splitRevisionCodes = lite.splitRevisionCodes;
                pkg.splitFlags = new int[num];
                pkg.splitPrivateFlags = new int[num];
                pkg.applicationInfo.splitNames = pkg.splitNames;
                pkg.applicationInfo.splitDependencies = splitDependencies;
                pkg.applicationInfo.splitClassLoaderNames = new String[num];
    
                for (int i = 0; i < num; i++) {
                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
                    //对非核心应用的处理
                    parseSplitApk(pkg, i, splitAssets, flags);
                }
            }
    
            pkg.setCodePath(packageDir.getCanonicalPath());
            pkg.setUse32bitAbi(lite.use32bitAbi);
            return pkg;
        } catch (IOException e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to get path: " + lite.baseCodePath, e);
        } finally {
            IoUtils.closeQuietly(assetLoader);
        }
    }
    

    (3)再看parseMonolithicPackage(),它的作用是解析给定的APK文件,将其作为单个单块包处理。
    最终也是调用parseBaseApk()进行解析,我们接下来看下parseBaseApk():

    // frameworks/base/core/java/android/content/pm/PackageParser.java
        private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
                int flags) throws PackageParserException {
            ParseResult<PackageParser.PackageLite> liteResult =
                    ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
            if (liteResult.isError()) {
                return input.error(liteResult);
            }
    
            final PackageParser.PackageLite lite = liteResult.getResult();
            if (mOnlyCoreApps && !lite.coreApp) {
                return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                        "Not a coreApp: " + apkFile);
            }
    
            final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
            try {
                // 对核心应用解析
                ParseResult<ParsingPackage> result = parseBaseApk(input,
                        apkFile,
                        apkFile.getCanonicalPath(),
                        assetLoader.getBaseAssetManager(), flags);
                if (result.isError()) {
                    return input.error(result);
                }
    
                return input.success(result.getResult()
                        .setUse32BitAbi(lite.use32bitAbi));
            } catch (IOException e) {
                return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                        "Failed to get path: " + apkFile, e);
            } finally {
                IoUtils.closeQuietly(assetLoader);
            }
        }
    

    (4)parseBaseApk()主要是对AndroidManifest.xml进行解析,解析后所有的信息放在Package对象中:

    // frameworks/base/core/java/android/content/pm/PackageParser.java
    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();
        ...
        XmlResourceParser parser = null;
        ...
            final int cookie = assets.findCookieForPath(apkPath);
            if (cookie == 0) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                        "Failed adding asset path: " + apkPath);
            }
            //获得一个XML资源解析对象,该对象解析的是APK中的AndroidManifest.xml文件。
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
            final Resources res = new Resources(assets, mMetrics, null);
    
            final String[] outError = new String[1];
            //再调用重载函数parseBaseApk()最终到parseBaseApkCommon(),
            //解析AndroidManifest.xml后得到一个Package对象
            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
            ...
            pkg.setVolumeUuid(volumeUuid);
            pkg.setApplicationVolumeUuid(volumeUuid);
            pkg.setBaseCodePath(apkPath);
            pkg.setSigningDetails(SigningDetails.UNKNOWN);
    
            return pkg;
      ...
    }
    

    (5)从AndroidManifest.xml中获取标签名,解析标签中的各个item的内容,存入Package对象中,例如获取标签"application"、"permission"。

    // frameworks/base/core/java/android/content/pm/PackageParser.java
    private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
            IOException {
          TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifest);
          //拿到AndroidManifest.xml 中的sharedUserId, 一般情况下有“android.uid.system”等信息
          String str = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
    
          while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
          //从AndroidManifest.xml中获取标签名
          String tagName = parser.getName();
          //如果读到AndroidManifest.xml中的tag是"application",执行parseBaseApplication()进行解析
          if (tagName.equals(TAG_APPLICATION)) {
                if (foundApp) {
                ...
                }
                foundApp = true;
                //解析"application"的信息,赋值给pkg
                if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                    return null;
                }
                ...
          //如果标签是"permission"
          else if (tagName.equals(TAG_PERMISSION)) {
                 //进行"permission"的解析
                if (!parsePermission(pkg, res, parser, outError)) {
                    return null;
                }
                  ....
            } 
            }
      }
    }
    

    (6)上面解析AndroidManifest.xml,会得到"application"、"overlay"、"permission"、"uses-permission"等信息,下面就针对"application"进行展开分析一下,进入parseBaseApplication()函数。

    // frameworks/base/core/java/android/content/pm/PackageParser.java
    private boolean parseBaseApplication(Package owner, Resources res,
                XmlResourceParser parser, int flags, String[] outError)
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
        //获取"application"子标签的标签内容
        String tagName = parser.getName();
            //如果标签是"activity"
            if (tagName.equals("activity")) {
                //解析Activity的信息,把activity加入Package对象
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                hasActivityOrder |= (a.order != 0);
                owner.activities.add(a);
    
            } else if (tagName.equals("receiver")) {
                //如果标签是"receiver",获取receiver信息,加入Package对象
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                        true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                hasReceiverOrder |= (a.order != 0);
                owner.receivers.add(a);
    
            }else if (tagName.equals("service")) {
                //如果标签是"service",获取service信息,加入Package对象
                Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                hasServiceOrder |= (s.order != 0);
                owner.services.add(s);
    
            }else if (tagName.equals("provider")) {
                //如果标签是"provider",获取provider信息,加入Package对象
                Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
                if (p == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                owner.providers.add(p);
            }
        ...
      }
    }
    

    关于如何使用 XmlPullParser来解析xml文件,请参考: Android XML文件结构 和 用XmlPullParser 来解析xml文件

      在PackageParser扫描完一个APK后,此时系统已经根据该APK中AndroidManifest.xml,创建了一个完整的 Package对象。

    1.2 scanPackageChildLI()

      回到《1.扫描流程》中,scanDirLI()方法调用到scanPackageChildLI()。调用addForInitLI()在platform初始化时,把Package内容加入到内部数据结构。

    // frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
    private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
        final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
        @Nullable UserHandle user)
                throws PackageManagerException {
        ...
        // Scan the parent
        PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
                scanFlags, currentTime, user);
    
        // Scan the children
        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
        for (int i = 0; i < childCount; i++) {
            PackageParser.Package childPackage = pkg.childPackages.get(i);
            //在平台初始化期间向内部数据结构添加新包,
            //在platform初始化时,把Package内容加入到内部数据结构。
            addForInitLI(childPackage, parseFlags, scanFlags,
                    currentTime, user);
        }
    
        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
            return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
        }
    }
    

    (1)在addForInitLI()中,进行安装包校验、签名检查、apk更新等操作,把Package加入系统

    // frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
    private PackageParser.Package addForInitLI(PackageParser.Package pkg,
            @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
            @Nullable UserHandle user)
                    throws PackageManagerException {
        // 判断系统应用是否需要更新
        synchronized (mPackages) {
            // 更新子应用
            if (isSystemPkgUpdated) {
                ...
                }
            if (isSystemPkgBetter) {
                // 更新安装包到system分区中
                synchronized (mPackages) {
                    // just remove the loaded entries from package lists
                    mPackages.remove(pkgSetting.name);
                }
                ...
                // 创建安装参数InstallArgs
                final InstallArgs args = createInstallArgsForExisting(
                        pkgSetting.codePathString,
                        pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
                args.cleanUpResourcesLI();
                synchronized (mPackages) {
                    mSettings.enableSystemPackageLPw(pkgSetting.name);
                }
            }
            // 安装包校验
            collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
        ...
        try (PackageFreezer freezer = freezePackage(pkg.packageName,
                            "scanPackageInternalLI")) {
                 // 如果两个apk签名不匹配,则调用deletePackageLIF方法清除apk文件及其数据
                deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
            }
            ...
            // 更新系统apk程序
            InstallArgs args = createInstallArgsForExisting(
                    pkgSetting.codePathString,
                    pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
            synchronized (mInstallLock) {
                args.cleanUpResourcesLI();
            }
      }
        // 如果新安装的系统APP会被旧的APP数据覆盖,所以需要隐藏隐藏系统应用程序,
        //并重新扫描data/app目录
        if (shouldHideSystemApp) {
            synchronized (mPackages) {
                mSettings.disableSystemPackageLPw(pkg.packageName, true);
            }
        }
    }
    

    2.总结

      回顾一下整个APK的扫描过程:

    1. 按照core app > system app > other app优先级扫描APK,解析AndroidManifest.xml文件,得到各个标签内容;

    2. 解析XML文件得到的信息由Package保存。从该类的成员变量可看出,和 Android 四大组件相关的信息分别由 activites、receivers、providers、services 保存。由于一个APK可声明多个组件,因此activites和receivers等均声明为ArrayList。

    3. 在PackageParser扫描完一个APK后,此时系统已经根据该APK中AndroidManifest.xml,创建了一个完整的 Package对象,下一步就是将该Package加入到系统中;

    4. 非系统Package扫描失败,删除文件。

    相关文章

      网友评论

          本文标题:Android PackageManagerService--0

          本文链接:https://www.haomeiwen.com/subject/mzvvvdtx.html