likes
comments
collection
share

如何应对Android面试官 -> PKMS 安装与签名校验

作者站长头像
站长
· 阅读数 29

前言


如何应对Android面试官 -> PKMS 安装与签名校验

本章主要围绕以上几个知识点进行讲解,最后会手写一个 权限申请框架;

PKMS


PackageManagerService 的作用:

  1. PackageManagerService(简称 PKMS),是 Android 系统中核心服务之一,负责应用程序的安装,卸载,信息查询,等工作;
  2. 解析AndroidNanifest.xml清单文件,解析清单文件中的所有节点信息;
  3. 扫描.apk文件,安装系统应用,安装本地应用等;
  4. 管理本地应用,主要有, 安装,卸载,应用信息查询 等;

PKMS 概述来说:Android 系统启动时,会启动(应用程序管理服务器 PKMS ),此服务负责扫描系统中特定的目录,寻找里面的APK格式的文件,并对这些文件进行解析,然后得到应用程序相关信息,最后完成应用程序的安装PKMS在安装应用过程中, 会全面解析应用程序的 AndroidManifest.xml 文件, 来得到 Activity, Service, BroadcastReceiver, ContextProvider 等信息, 在结合 PKMS 服务就可以在 Android OS 中正常的使用应用程序了,在 Android 系统中, 系统启动时由 SystemServer 启动 PKMS 服务, 启动该服务后会执行应用程序的安装过程;

PKMS 的角色位置,我们来看一张图

如何应对Android面试官 -> PKMS 安装与签名校验

客户端可通过 Context.getPackageManager() 获得 ApplicationPackageManager 对象, 而 mPM 指向的是 Proxy 代理,当调用到 mPM. 方法后,将会调用到 IPackageManager 的 Proxy 代理方法,然后通过Binder 机制中的 mRemote 与服务端 PackageManagerService 通信 并调用到 PackageManagerService 的方法;

PKMS 启动过程分析


启动过程,依然从 SystemServer 的 run 看起,我们进入这个 run 方法的 startBootstrapServices() 方法,这个方法主要用来启动引导服务;

private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    // 第一步:创建安装器,并启动安装器
    Installer installer = mSystemServiceManager.startService(Installer.class);
    
    // 第二步:获取设置是否加密「例如设置了密码」
    String cryptState = VoldProperties.decrypt().orElse("");
    
    // 第三步:调用 PKMS 的 main 方法,实例化 PKMS
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
        domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
        mOnlyCore);
    
    // 第四步:如果设备没有加密,操作它,管理 A/B OAT dexopting
    if (!mOnlyCore) {
        boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                false);
        if (!disableOtaDexopt) {
            try {
                // 
                OtaDexoptService.main(mSystemContext, mPackageManagerService);
            } catch (Throwable e) {
                reportWtf("starting OtaDexOptService", e);
            } finally {
                Watchdog.getInstance().resumeWatchingCurrentThread("moveab");
                t.traceEnd();
            }
        }
    }
    
    // 第五步:如果设备没有加密,执行performDexOptUpdate 完成dex优化
    // 构造PackageDexOptimizer及DexManager类,处理dex优化;
    mPackageManagerService.updatePackagesIfNeeded();
    
    // 第六步:执行performStrim 完成磁盘优化
    mPackageManagerService.performFstrimIfNeeded();
    
    // 第七步:PKMS准备就绪
    mPackageManagerService.systemReady();
}

重点一在第五步,手机开机慢的主要原因:

手机开机,BootRoom 拉起BootLoader程序,BootLoader 会拉起 Linux 内核驱动,驱动会初始化init进程,init进程会创建zygote进程,zygote进程会fork出它的第大儿子 系统服务进程「system server」系统服务进程会初始化 PKMS,PKMS 在构造方法中 就会扫描手机上的所有应用,手机上应用越多,时间越长,扫描完之后 还会进行 dex 优化;

重点二在第三步,我们进入第三步的 main 方法看下:

public static PackageManagerService main(Context context, Installer installer,
        @NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
        boolean onlyCore) {
    // 省略部分代码
    
    // 实例化 PackageManagerService
    PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
        Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
        Build.VERSION.INCREMENTAL);
    // 将 PMS 注册到 SystemServer 中
    ServiceManager.addService("package", m);
    // 
    final PackageManagerNative pmn = m.new PackageManagerNative();
    ServiceManager.addService("package_native", pmn);
}

这里主要是调用 PackageManagerService 的构造方法,进行实例化,并初始化相关信息,这里主要分为 5 个阶段;

public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
        final String buildFingerprint, final boolean isEngBuild,
        final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
    // 省略部分代码
    
    // 阶段1 BOOT_PROGRESS_PMS_START
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
        SystemClock.uptimeMillis());
    mMetrics = injector.getDisplayMetrics();
    mInstaller = injector.getInstaller();
    mPermissionManager = injector.getPermissionManagerServiceInternal();
    mSettings = injector.getSettings();
    // 这里的目的是:如果想安装系统应用,必须得到系统厂商的签名,这些 UID 是由 Linux 来管理的,系统应用在安装的时候,权限校验就能通过;
    mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.se", SE_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    // 构造PackageDexOptimizer及DexManager类,处理dex优化;
    mPackageDexOptimizer = injector.getPackageDexOptimizer();
    mDexManager = injector.getDexManager();
    // ART虚拟机管理服务
    mArtManagerService = injector.getArtManagerService();
    mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
    mViewCompiler = injector.getViewCompiler();
    // 权限变化监听器
    mProtectedPackages = new ProtectedPackages(mContext);
    mApexManager = injector.getApexManager();
}
  1. BOOT_PROGRESS_PMS_START,PKMS 的构造方法中进行;
  • mMetrics = new DisplayMetrics();初始化屏幕相关类;
  • mInstaller = installer;赋值安装器;
  • mPermissionManager = injector.getPermissionManagerServiceInternal();创建PermissionManager 进行权限管理;
  • 创建 settings 保存安装包信息;

这个 Settings 比较关键,我们进入这个 Settings 的构造方法看下;

Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
        LegacyPermissionDataProvider permissionDataProvider,
        @NonNull DomainVerificationManagerInternal domainVerificationManager,
        @NonNull PackageManagerTracedLock lock)  {
    // 读取 dat/system/packages.xml 主要是为了读取系统应用并加载
    mSettingsFilename = new File(mSystemDir, "packages.xml");
    // 备份读取,为了加载过程中被打断之后可以继续加载 
    mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
    mPackageListFilename = new File(mSystemDir, "packages.list");
}

packages.xml中 存放着所有的系统权限 以及 安装在手机上的每一个应用程序信息。每次开机都会执行这个 PKMS 的构造方法,进行应用的安装处理,如果手机上安装的应用很多很多,开机时间也会相应的变长;

  1. EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START;扫描系统的 apk 应用;

这个阶段就是开始扫描系统的 apk 应用,

public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
        final String buildFingerprint, final boolean isEngBuild,
        final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
    // 阶段2:扫描系统的 apk 应用     
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
        startTime);
    // 开始扫描
    for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
        final ScanPartition partition = mDirsToScanAsSystem.get(i);
        if (partition.getOverlayFolder() == null) {
            continue;
        }
        // 
        scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
                systemScanFlags | partition.scanFlag, 0,
                packageParser, executorService);
    }
    // 扫描 framework 
    File frameworkDir = new File(Environment.getRootDirectory(), "framework");
    scanDirTracedLI(frameworkDir, systemParseFlags,
        systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
        packageParser, executorService);
    
    // 
    for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
        final ScanPartition partition = mDirsToScanAsSystem.get(i);
        if (partition.getPrivAppFolder() != null) {
            scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
                    systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
                    packageParser, executorService);
        }
        scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
                systemScanFlags | partition.scanFlag, 0,
                packageParser, executorService);
    }
}

扫描系统的应用,主要扫描下面几个目录的 apk:

private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) {
    switch (partition.type) {
        // 
        case PackagePartitions.PARTITION_SYSTEM:
            return 0;
        // vendor/overlay
        case PackagePartitions.PARTITION_VENDOR:
            return SCAN_AS_VENDOR;
        // odm/overlay
        case PackagePartitions.PARTITION_ODM:
            return SCAN_AS_ODM;
        // oem/overlay
        case PackagePartitions.PARTITION_OEM:
            return SCAN_AS_OEM;
        // product/overlay
        case PackagePartitions.PARTITION_PRODUCT:
            return SCAN_AS_PRODUCT;
        // 
        case PackagePartitions.PARTITION_SYSTEM_EXT:
            return SCAN_AS_SYSTEM_EXT;
        default:
            throw new IllegalStateException("Unable to determine scan flag for "
                    + partition.getFolder());
    }
}
  1. BOOT_PROGRESS_PMS_DATA_SCAN_START,扫描data目录下的apk,这里主要是安装的第三方应用;
mAppInstallDir = new File(Environment.getDataDirectory(), "app");

if (!mOnlyCore) {
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
            SystemClock.uptimeMillis());
    scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
            packageParser, executorService);
    // 移除通过OTA删除的更新系统应用程序的禁用package设置
    // 如果更新不再存在,则完全删除该应用。否则,撤消其系统权限       
    for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
        final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
        final AndroidPackage pkg = mPackages.get(packageName);
        final String msg;

        // remove from the disabled system list; do this first so any future
        // scans of this package are performed without this state
        mSettings.removeDisabledSystemPackageLPw(packageName);
        // ...
    }
    
    // 确保期望在userdata分区上显示的所有系统应用程序实际显示
    // 如果从未出现过,需要回滚以恢复系统版本
    for (int i = 0; i < mExpectingBetter.size(); i++) {
        final String packageName = mExpectingBetter.keyAt(i);
        if (!mPackages.containsKey(packageName)) {
            final File scanFile = mExpectingBetter.valueAt(i);
            // ...
            mSettings.enableSystemPackageLPw(packageName);
            
            // 扫描 APK
            try {
                final AndroidPackage newPkg = scanPackageTracedLI(
                        scanFile, reparseFlags, rescanFlags, 0, null);
                // We rescanned a stub, add it to the list of stubbed system packages
                if (newPkg.isStub()) {
                    stubSystemApps.add(packageName);
                }
            } catch (PackageManagerException e) {
                Slog.e(TAG, "Failed to parse original system package: "
                        + e.getMessage());
            }
        }
    }
    // 解压缩并安装任何存根系统应用程序。必须最后执行此操作以确保替换或禁用所有存根
    installSystemStubPackages(stubSystemApps, scanFlags);
    // 获取storage manager包名
    mStorageManagerPackage = getStorageManagerPackageName();
    // 解决受保护的action过滤器。只允许setup wizard(开机向导)为这些action设置高优先级过滤器
    mSetupWizardPackage = getSetupWizardPackageNameImpl();
    // 更新客户端以确保持有正确的共享库路径
    updateAllSharedLibrariesLocked(null, null, Collections.unmodifiableMap(mPackages));
    // 读取并更新要保留的package的上次使用时间
    mPackageUsage.read(packageSettings);
    mCompilerStats.read();
}

处理 data 目录的应用信息,及时更新,祛除不必要的数据;

  1. BOOT_PROGRESS_PMS_SCAN_END OTA 升级后首次启动要清除不必要的缓存数据,权限等默认项更新后要清理相关数据,将阶段2 阶段3扫描后的数据更新到 packages.xml 文件中;
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
        SystemClock.uptimeMillis());
  1. BOOT_PROGRESS_PMS_READY GC 内存回收
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis());
// PermissionController 主持 缺陷许可证的授予和角色管理,所以这是核心系统的一个关键部分。
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
updateInstantAppInstallerLocked(null);
// 打开应用之后,及时回收处理
Runtime.getRuntime().gc();
// 上面的初始扫描在持有mPackage锁的同时对installd进行了多次调用
mInstaller.setWarnIfHeld(mPackages);

APK 的扫描


PKMS的构造函数中调用了 scanDirTracedLI 方法 来扫描某个目录的 apk 文件;

private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
        long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
    try {
        scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

从这里执行 APK 的扫描操作,我们进入这个 scanDirLI 看下;

private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
        PackageParser2 packageParser, ExecutorService executorService) {
    final File[] files = scanDir.listFiles();
    if (ArrayUtils.isEmpty(files)) {
        return;
    }

    ParallelPackageParser parallelPackageParser =
            new ParallelPackageParser(packageParser, executorService);

    int fileCount = 0;
    for (File file : files) {
        final boolean isPackage = (isApkFile(file) || file.isDirectory())
                && !PackageInstallerService.isStageName(file.getName());
        if (!isPackage) {
            // Ignore entries which are not packages
            continue;
        }
        // 收集apk文件 交给submit方法处理
        parallelPackageParser.submit(file, parseFlags);
        fileCount++;
    }
}

这里主要是 收集 apk 文件 交给 submit 方法处理;

public void submit(File scanFile, int parseFlags) {
    mExecutorService.submit(() -> {
        ParseResult pr = new ParseResult();
        try {
            pr.scanFile = scanFile;
            pr.parsedPackage = parsePackage(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();
            mInterruptedInThread = Thread.currentThread().getName();
        }
    });
}

submit 把 PackageParser 封装好的信息交给 parsePackage 方法;

public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    if (useCaches && mCacher != null) {
        ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
        if (parsed != null) {
            return parsed;
        }
    }

    long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
    ParseInput input = mSharedResult.get().reset();
    // 解析 apk 文件
    ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);

    ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
    return parsed;
}

解析 apk 文件

public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
        int flags)
        throws PackageParserException {
    if (packageFile.isDirectory()) {
        return parseClusterPackage(input, packageFile, flags);
    } else {
        // 解析
        return parseMonolithicPackage(input, packageFile, flags);
    }
}

我们进入 parseMonolithicPackage 这个方法看下:

private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
        int flags) throws PackageParserException {
    
    final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
    try {
        // 通过 parseBaseApk 解析 apk 中的清单文件
        final ParseResult<ParsingPackage> result = parseBaseApk(input,
                apkFile,
                apkFile.getCanonicalPath(),
                assetLoader, flags);
        if (result.isError()) {
            return input.error(result);
        }

        return input.success(result.getResult()
                .setUse32BitAbi(lite.isUse32bitAbi()));
    } catch (IOException e) {
        return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to get path: " + apkFile, e);
    } finally {
        IoUtils.closeQuietly(assetLoader);
    }
}

通过 parseBaseApk 解析 apk 中的清单文件;

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
        String codePath, SplitAssetLoader assetLoader, int flags)
        throws PackageParserException {
    final String apkPath = apkFile.getAbsolutePath();

    String volumeUuid = null;
    if (apkPath.startsWith(MNT_EXPAND)) {
        final int end = apkPath.indexOf('/', MNT_EXPAND.length());
        volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
    }

    if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

    final AssetManager assets = assetLoader.getBaseAssetManager();
    final int cookie = assets.findCookieForPath(apkPath);
    if (cookie == 0) {
        return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                "Failed adding asset path: " + apkPath);
    }
    // 解析 apk 中的清单文件
    try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
            ANDROID_MANIFEST_FILENAME)) {
        final Resources res = new Resources(assets, mDisplayMetrics, null);
        // 获取应用的相关信息,并存储起来
        ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
                parser, flags);
        if (result.isError()) {
            return input.error(result.getErrorCode(),
                    apkPath + " (at " + parser.getPositionDescription() + "): "
                            + result.getErrorMessage());
        }

        final ParsingPackage pkg = result.getResult();
        if (assets.containsAllocatedTable()) {
            final ParseResult<?> deferResult = input.deferError(
                    "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
                            + " the resources.arsc of installed APKs to be stored uncompressed"
                            + " and aligned on a 4-byte boundary",
                    DeferredError.RESOURCES_ARSC_COMPRESSED);
            if (deferResult.isError()) {
                return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
                        deferResult.getErrorMessage());
            }
        }

        ApkAssets apkAssets = assetLoader.getBaseApkAssets();
        boolean definesOverlayable = false;
        try {
            definesOverlayable = apkAssets.definesOverlayable();
        } catch (IOException ignored) {
            // Will fail if there's no packages in the ApkAssets, which can be treated as false
        }

        if (definesOverlayable) {
            SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
            int size = packageNames.size();
            for (int index = 0; index < size; index++) {
                String packageName = packageNames.valueAt(index);
                Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
                if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
                    for (String overlayable : overlayableToActor.keySet()) {
                        pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
                    }
                }
            }
        }

        pkg.setVolumeUuid(volumeUuid);

        if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
            pkg.setSigningDetails(getSigningDetails(pkg, false));
        } else {
            pkg.setSigningDetails(SigningDetails.UNKNOWN);
        }

        return input.success(pkg);
    } catch (Exception e) {
        return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to read manifest from " + apkPath, e);
    }
}

通过 XmlResourceParser parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME) 解析 apk 中的清单文件;

通过 parseBaseApk 获取应用的相关信息,并存储起来;

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
        String codePath, Resources res, XmlResourceParser parser, int flags)
        throws XmlPullParserException, IOException {
    final String splitName;
    final String pkgName;

    ParseResult<Pair<String, String>> packageSplitResult =
            ApkLiteParseUtils.parsePackageSplitNames(input, parser);
    if (packageSplitResult.isError()) {
        return input.error(packageSplitResult);
    }

    Pair<String, String> packageSplit = packageSplitResult.getResult();
    pkgName = packageSplit.first;
    splitName = packageSplit.second;

    if (!TextUtils.isEmpty(splitName)) {
        return input.error(
                PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                "Expected base APK, but found split " + splitName
        );
    }

    final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
    try {
        final boolean isCoreApp =
                parser.getAttributeBooleanValue(null, "coreApp", false);
        // 获取清单文件中的标签 例如「application」「permission」「package」「manifest」
        final ParsingPackage pkg = mCallback.startParsingPackage(
                pkgName, apkPath, codePath, manifestArray, isCoreApp);
        // 获取application标签中的中的「activity」「service」「receiver」「provider」       
        final ParseResult<ParsingPackage> result =
                parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
        if (result.isError()) {
            return result;
        }

        return input.success(pkg);
    } finally {
        manifestArray.recycle();
    }
}

startParsingPackage 获取清单文件中的标签 例如「application」「permission」「package」「manifest」

parseBaseApkTags 获取application标签中的中的「activity」「service」「receiver」「provider」;

所以,静态广播是什么时候注册的?Activity的启动模式是在什么时候获取的呢? 答案就显而易见了;

开机的时候,在 PKMS 的构造方法中 会扫描所有的 apk,包括系统 apk 和应用 apk,并解析它们的清单文件,获取到 receiver 的时候就会注册,获取到 activity 的时候 一并获取了 launchMode;

扫描外部apk直接通过接口,获取 Package 对象,PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);

安装 APK


用户点击 xxx.apk 文件进行安装,从「开始安装」到「完成安装」到过程;这里分为两个步骤,一个是复制流程,一个是安装流程;

复制流程

点击xxx.apk执行onClick事件,通过Intent 发起startActivity 跳转到 PackageInstallerActivity 这个界面,这个界面是以 Dialog 形式展示,存在「安装」和「取消」事件,进入「安装」事件看下;

从 PackageInstallerActivity 的 bindUI 发起流程,会调用 startInstall 方法进行整个的安装流程;

private void bindUi() {
    mAlert.setIcon(mAppSnippet.icon);
    mAlert.setTitle(mAppSnippet.label);
    mAlert.setView(R.layout.install_content_view);
    mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
            (ignored, ignored2) -> {
                if (mOk.isEnabled()) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                    } else {
                        // 开始安装
                        startInstall();
                    }
                }
            }, null);
    mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
            (ignored, ignored2) -> {
                // Cancel and finish
                setResult(RESULT_CANCELED);
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, false);
                }
                finish();
            }, null);
    setupAlert();

    mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
    mOk.setEnabled(false);

    if (!mOk.isInTouchMode()) {
        mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
    }
}

调用 startInstall 发起安装

private void startInstall() {
    // Start subactivity to actually install the application
    Intent newIntent = new Intent();
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    newIntent.setData(mPackageURI);
    newIntent.setClass(this, InstallInstalling.class);
    String installerPackageName = getIntent().getStringExtra(
            Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
    }
    if (installerPackageName != null) {
        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                installerPackageName);
    }
    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
        newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
    }
    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
    startActivity(newIntent);
    finish();
}

跳转到 安装中 InstallInstalling.class 界面,在 onResume 执行安装逻辑

protected void onResume() {
    super.onResume();

    // This is the first onResume in a single life of the activity
    if (mInstallingTask == null) {
        PackageInstaller installer = getPackageManager().getPackageInstaller();
        PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

        if (sessionInfo != null && !sessionInfo.isActive()) {
            mInstallingTask = new InstallingAsyncTask();
            // 执行 execute 方法进行安装
            mInstallingTask.execute();
        } else {
            // we will receive a broadcast when the install is finished
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        }
    }
}

mInstallingTask.execute(); 执行 execute 方法进行安装;这里是一个 AsyncTask,要处理 doInBackground 和 onPostExecute

private final class InstallingAsyncTask extends AsyncTask<Void, Void,
        PackageInstaller.Session> {
    volatile boolean isDone;

    @Override
    protected PackageInstaller.Session doInBackground(Void... params) {
        PackageInstaller.Session session;
        try {
            session = getPackageManager().getPackageInstaller().openSession(mSessionId);
        } catch (IOException e) {
            synchronized (this) {
                isDone = true;
                notifyAll();
            }
            return null;
        }

        session.setStagingProgress(0);

        try {
            File file = new File(mPackageURI.getPath());

            try (InputStream in = new FileInputStream(file)) {
                long sizeBytes = file.length();
                try (OutputStream out = session
                        .openWrite("PackageInstaller", 0, sizeBytes)) {
                    byte[] buffer = new byte[1024 * 1024];
                    while (true) {
                        int numRead = in.read(buffer);

                        if (numRead == -1) {
                            session.fsync(out);
                            break;
                        }

                        if (isCancelled()) {
                            session.close();
                            break;
                        }

                        out.write(buffer, 0, numRead);
                        if (sizeBytes > 0) {
                            float fraction = ((float) numRead / (float) sizeBytes);
                            session.addProgress(fraction);
                        }
                    }
                }
            }

            return session;
        } catch (IOException | SecurityException e) {
            Log.e(LOG_TAG, "Could not write package", e);

            session.close();

            return null;
        } finally {
            synchronized (this) {
                isDone = true;
                notifyAll();
            }
        }
    }

    @Override
    protected void onPostExecute(PackageInstaller.Session session) {
        if (session != null) {
            Intent broadcastIntent = new Intent(BROADCAST_ACTION);
            broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            broadcastIntent.setPackage(getPackageName());
            broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    InstallInstalling.this,
                    mInstallId,
                    broadcastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);

            session.commit(pendingIntent.getIntentSender());
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        } else {
            getPackageManager().getPackageInstaller().abandonSession(mSessionId);

            if (!isCancelled()) {
                launchFailure(PackageInstaller.STATUS_FAILURE,
                        PackageManager.INSTALL_FAILED_INVALID_APK, null);
            }
        }
    }
}

doInBackground方法中:通过IO流 将apk文件写入session中;

onPostExecute方法中:通过 session.commit 方法,跨进程操作,将 session 交给 PKMS,最终走到 PKMS 的 installStage 方法;

void installStage(InstallParams params) {
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;

    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
            System.identityHashCode(msg.obj));
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
            System.identityHashCode(msg.obj));

    mHandler.sendMessage(msg);
}

msg.what == INIT_COPY; handleMessage 的这个 case 我们进入看下:

class PackageHandler extends Handler {
    
    void doHandleMessage(Message msg) {
        
        switch (msg.what) {
            case INIT_COPY: {
                HandlerParams params = (HandlerParams) msg.obj;
                if (params != null) {
                    params.startCopy();
                }
                break;
            }
        }
    }
}

INIT_COPY 中最终执行的是 HandlerParams 的 startCopy 方法,将 apk 复制到 /data/app 目录下;

最终走到 PKMS 的 handleStartCopy() 方法 和 handleReturnCode 方法;

public void handleStartCopy() {
    if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
        mRet = INSTALL_SUCCEEDED;
        return;
    }
    PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
            mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);

    // For staged session, there is a delay between its verification and install. Device
    // state can change within this delay and hence we need to re-verify certain conditions.
    boolean isStaged = (installFlags & INSTALL_STAGED) != 0;
    if (isStaged) {
        mRet = verifyReplacingVersionCode(
                pkgLite, requiredInstalledVersionCode, installFlags);
        if (mRet != INSTALL_SUCCEEDED) {
            return;
        }
    }

    mRet = overrideInstallLocation(pkgLite);
}

主要是为了创建 InstallArgs,在 handleReturnCode 中执行 copyApk 方法;

void handleReturnCode() {
    processPendingInstall();
}

private void processPendingInstall() {
    // 这里创建的是 FileInstallArgs
    InstallArgs args = createInstallArgs(this);
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        mRet = args.copyApk();
    }
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        F2fsUtils.releaseCompressedBlocks(
                mContext.getContentResolver(), new File(args.getCodePath()));
    }
    if (mParentInstallParams != null) {
        mParentInstallParams.tryProcessInstallRequest(args, mRet);
    } else {
        PackageInstalledInfo res = createPackageInstalledInfo(mRet);
        processInstallRequestsAsync(
                res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                Collections.singletonList(new InstallRequest(args, res)));
    }
}

handleReturnCode 执行到 args.copyApk() 的时候,才真正的将 apk 文件复制到了 /data/app 目录下;

int copyApk() {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
    try {
        return doCopyApk();
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

private int doCopyApk() {
    if (origin.staged) {
        if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
        codeFile = origin.file;
        return PackageManager.INSTALL_SUCCEEDED;
    }

    try {
        final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
        final File tempDir =
                mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
        codeFile = tempDir;
    } catch (IOException e) {
        Slog.w(TAG, "Failed to create copy file: " + e);
        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
    }

    int ret = PackageManagerServiceUtils.copyPackage(
            origin.file.getAbsolutePath(), codeFile);
    if (ret != PackageManager.INSTALL_SUCCEEDED) {
        Slog.e(TAG, "Failed to copy package");
        return ret;
    }

    final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
    final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
    NativeLibraryHelper.Handle handle = null;
    try {
        handle = NativeLibraryHelper.Handle.create(codeFile);
        ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                abiOverride, isIncremental);
    } catch (IOException e) {
        Slog.e(TAG, "Copying native libraries failed", e);
        ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    } finally {
        IoUtils.closeQuietly(handle);
    }

    return ret;
}

通过 IO 流 执行 copy 操作;这也就能解释为什么 apk 安装包可以删除的原因了;

扫描流程大致可以理解为:

  1. 在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象;
  2. 扫描APK,解析AndroidManifest.xml文件,得到清单文件各个标签内容;
  3. 解析清单文件的信息由 Package 保存,从该类的成员变量可看出,和 Android 四大组件相关 的信息分别由 activites、receivers、providers、services 保存,由于一个 APK 可声明多个组件,因此 activites 和 receivers 等均声明为 ArrayList;

安装流程

安装流程也是有 PKMS 发起的,它发起的起点就是 handleReturnCode 的回调中调用了processPendingInstall 方法

void handleReturnCode() {
    processPendingInstall();
}

private void processPendingInstall() {
    InstallArgs args = createInstallArgs(this);
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        mRet = args.copyApk();
    }
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        F2fsUtils.releaseCompressedBlocks(
                mContext.getContentResolver(), new File(args.getCodePath()));
    }
    if (mParentInstallParams != null) {
        mParentInstallParams.tryProcessInstallRequest(args, mRet);
    } else {
        PackageInstalledInfo res = createPackageInstalledInfo(mRet);
        // 执行安装逻辑
        processInstallRequestsAsync(
                res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                Collections.singletonList(new InstallRequest(args, res)));
    }
}

通过调用 processInstallRequestsAsync 执行安装逻辑

private void processInstallRequestsAsync(boolean success,
        List<InstallRequest> installRequests) {
    
    mHandler.post(() -> {
        // 省略部分代码
        if (success) {
            // 省略部分代码
            synchronized (mInstallLock) {
                installPackagesTracedLI(apkInstallRequests);
            }
        }
    })    
}

这里直接调用 installPackagesTracedLI 进行安装;

private void installPackagesTracedLI(List<InstallRequest> requests) {
    try {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
        installPackagesLI(requests);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

这里直接调用 installPackagesLI 方法进行 apk 扫描以及安装

private void installPackagesLI(List<InstallRequest> requests) {
    //
    // 省略部分代码
    final ScanResult result = scanPackageTracedLI(
        prepareResult.packageToScan, prepareResult.parseFlags,
        prepareResult.scanFlags, System.currentTimeMillis(),
        request.args.user, request.args.abiOverride);
    
    // 构建安装请求,然后调用 executePostCommitSteps 进行安装
    CommitRequest commitRequest = null;
    synchronized (mLock) {
        Map<String, ReconciledPackage> reconciledPackages;
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
            reconciledPackages = reconcilePackagesLocked(
                    reconcileRequest, mSettings.getKeySetManagerService(), mInjector);
        } catch (ReconcileFailure e) {
            for (InstallRequest request : requests) {
                request.installResult.setError("Reconciliation failed...", e);
            }
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
            commitRequest = new CommitRequest(reconciledPackages,
                    mUserManager.getUserIds());
            commitPackagesLocked(commitRequest);
            success = true;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
    executePostCommitSteps(commitRequest);
}

构建安装请求,然后调用 executePostCommitSteps 进行安装;

private void executePostCommitSteps(CommitRequest commitRequest) {
    // 省略部分代码
    final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
    for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
        final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
                        & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
        final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
        final String packageName = pkg.getPackageName();
        final String codePath = pkg.getPath();
        final boolean onIncremental = mIncrementalManager != null
                && isIncrementalPath(codePath);
        if (onIncremental) {
            IncrementalStorage storage = mIncrementalManager.openStorage(codePath);
            if (storage == null) {
                throw new IllegalArgumentException(
                        "Install: null storage for incremental package " + packageName);
            }
            incrementalStorages.add(storage);
        }
        prepareAppDataAfterInstallLIF(pkg);
        
        // 省略部分代码
    }
    
    // 省略部分代码
}

这里调用 prepareAppDataAfterInstallLIF 最终调用到 prepareAppDataLeaf 中的 batch.createAppData

private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch,
        @NonNull AndroidPackage pkg, int userId, int flags) {
    return batch.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo,
        targetSdkVersion).whenComplete((ceDataInode, e) -> {
            
            if (e != null) {
                
                destroyAppDataLeafLIF(pkg, userId, flags);
                try {
                    ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId,
                            flags, appId, seInfo, pkg.getTargetSdkVersion());
                    
                } catch (InstallerException e2) {
                    
                }
            } else if (e != null) {
               
            }
            if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
                mArtManagerService.prepareAppProfiles(pkg, userId,
                    /* updateReferenceProfileContent= */ false);
            }

            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
                synchronized (mLock) {
                    if (ps != null) {
                        ps.setCeDataInode(ceDataInode, userId);
                    }
                }
            }
            
            prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
        });
}

Batch 的 createAppData 最终调用到的是其 execute 方法;

public synchronized void execute(@NonNull Installer installer) throws InstallerException {
    if (mExecuted) throw new IllegalStateException();
    mExecuted = true;

    final int size = mArgs.size();
    for (int i = 0; i < size; i += CREATE_APP_DATA_BATCH_SIZE) {
        final CreateAppDataArgs[] args = new CreateAppDataArgs[Math.min(size - i,
                CREATE_APP_DATA_BATCH_SIZE)];
        for (int j = 0; j < args.length; j++) {
            args[j] = mArgs.get(i + j);
        }
        final CreateAppDataResult[] results = installer.createAppDataBatched(args);
        for (int j = 0; j < args.length; j++) {
            final CreateAppDataResult result = results[j];
            final CompletableFuture<Long> future = mFutures.get(i + j);
            if (result.exceptionCode == 0) {
                future.complete(result.ceDataInode);
            } else {
                future.completeExceptionally(
                        new InstallerException(result.exceptionMessage));
            }
        }
    }
}

通过 installer.createAppDataBatched(args) 方法进行 app 的创建;

public @NonNull CreateAppDataResult[] createAppDataBatched(@NonNull CreateAppDataArgs[] args)
        throws InstallerException {
    if (!checkBeforeRemote()) {
        final CreateAppDataResult[] results = new CreateAppDataResult[args.length];
        Arrays.fill(results, buildPlaceholderCreateAppDataResult());
        return results;
    }
    try {
        // 进入 linux 底层的安装逻辑
        return mInstalld.createAppDataBatched(args);
    } catch (Exception e) {
        throw InstallerException.from(e);
    }
}

最终 mInstalld.createAppDataBatched(args); 进入 linux 底层的安装逻辑,底层的安装逻辑我也不太清楚,只能到这里了;

权限扫描


PKMS 的构造方法中会调用到 SystemConfig systemConfig = SystemConfig.getInstance(); 方法,此方法会执行权限的扫描/system/etc/permissions

如何应对Android面试官 -> PKMS 安装与签名校验

SytestemConfig 的构造方法中 会执行权限的读取操作;

如何应对Android面试官 -> PKMS 安装与签名校验

readPermissions 和扫描到的AndroidManifest文件中声明的权限会和这里进行一个匹配,匹配上了给予权限;

如何应对Android面试官 -> PKMS 安装与签名校验

好了,本章的讲解就到这里吧,静默安装和权限申请框架我们放在了下一章;

下一章预告


PMS 权限管理

欢迎三连


来都来了,点个关注点个赞吧,你的支持是我最大的动力~~