系统应用的AndroidManifest.xml是在什么时候被解析的

调试应用,一般两种方式把apk通过adb push到指定目录或者通过adb install 。
做系统应用开发一般使用adb push到 指定目录进行调试,然后kill进程,等待应用再被拉起,但是会遇到一个问题,把APK通过adb push到设备后重启之前AndroidManifest.xml的修改不生效。

为什么会出现这个现象,就需要了解AndroidManifest.xml啥时候被加载的。
APK的安装,离不开PMS (PackageManagerService)
先上图


解析AndroidManifest流程.png

首先分析PMS的启动

PMS (PackageManagerService)启动过程

  • SystemServer --> main() --> startBootstrapServices() --> PackageManagerService.main()
    /**
     * The main entry point from zygote.<--源头
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }
....
....
    private void run() {
    ...
    ...
    // Start services.
        try {
            t.traceBegin("StartServices");
            startBootstrapServices(t);
            startCoreServices(t);
            startOtherServices(t);
            startApexServices(t);
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            t.traceEnd(); // StartServices
        }
    }
    ...
    
    private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
      ...
      ...
       //启动installer服务,从名字可以看出此服务与安装apk相关,里面主要是管理apk的各种信息,AMS和PMS启动时都需要这个参数
       t.traceBegin("StartInstaller");
       Installer installer = mSystemServiceManager.startService(Installer.class);
       t.traceEnd();
      //这里是启动AMS
        // Activity manager runs the show.
        t.traceBegin("StartActivityManager");
        //这里有备注可能以后会换地方
        // TODO: Might need to move after migration to WM.
        ActivityTaskManagerService atm = mSystemServiceManager.startService(
                ActivityTaskManagerService.Lifecycle.class).getService();
        mActivityManagerService = ActivityManagerService.Lifecycle.startService(
                mSystemServiceManager, atm);
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        mWindowManagerGlobalLock = atm.getGlobalLock();
        t.traceEnd();
      ...
      ...
        // Only run "core" apps if we're encrypting the device.
        //启动min-framework 显示密码输入界面,仅启动 coreApp, 在AndroidManifest.xml中声明。
        //此时启动的 APP 在 tmpfs 临时分区,所以,所有app都是原始安装状态,不包含任何用户使用产生的数据。
        String cryptState = VoldProperties.decrypt().orElse("");
        if (ENCRYPTING_STATE.equals(cryptState)) {
            Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
            mOnlyCore = true;
        } else if (ENCRYPTED_STATE.equals(cryptState)) {
            Slog.w(TAG, "Device encrypted - only parsing core apps");
            mOnlyCore = true;
        }
        ....
        ....
        t.traceBegin("StartDomainVerificationService");
        //这个服务作为参数传进PMS,主要是为了做一些校验工作
        DomainVerificationService domainVerificationService = new DomainVerificationService(
                mSystemContext, SystemConfig.getInstance(), platformCompat);
        mSystemServiceManager.startService(domainVerificationService);
        t.traceEnd();
      ...
      ...
      //启动PMS
       t.traceBegin("StartPackageManagerService");
        try {
            Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
            Pair<PackageManagerService, IPackageManager> pmsPair = PackageManagerService.main(
                    mSystemContext, installer, domainVerificationService,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
            mPackageManagerService = pmsPair.first;
            iPackageManager = pmsPair.second;
        } finally {
            Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
        }
    ...
    ...
    }
......
.....
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        ...
        ...
        WindowManagerService wm = null;
        ...
        ...
            //启动WMS
            t.traceBegin("StartWindowManagerService");
            // WMS needs sensor service ready
            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
            t.traceEnd();

            t.traceBegin("SetWindowManagerService");
            mActivityManagerService.setWindowManager(wm);
            t.traceEnd();

            t.traceBegin("WindowManagerServiceOnInitReady");
            wm.onInitReady();
            t.traceEnd();
      .....
      .....
    }

注意此处开始Android的10到13版本各有变化,其中11到13属于重构逐渐迭代过程

Android T(13) SDK32版本的过程以后分析

PackageManagerService --> main()

Android Q(10) SDK29版本的过程
  • PackageManagerService -> main() --> new PackageManagerService()
    在这个巨大无比的构造方法中摘出我们需要关心的两个方法
    -->scanDirTracedLI()
    -->installSystemStubPackages()
    public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();

        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }
    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
            ....
            ....
             scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);
          /**省略了很多目录 PRODUCT_OVERLAY_DIR,PRODUCT_SERVICES_OVERLAY_DIR 等等*/
          ....
          ....
            // Find base frameworks (resource packages without code).
            //扫描安装“system\framework”目录下的jar包
            scanDirTracedLI(frameworkDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_NO_DEX
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRIVILEGED,
                    0);
          ....
          ....
           // Collect privileged system packages. /system/priv-app
            final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            scanDirTracedLI(privilegedAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRIVILEGED,
                    0);
           /**省略了很多目录 Environment.getRootDirectory()+"app" == /system/app,
            Environment.getVendorDirectory()+"priv-app"== /vendor/piv-app 
            Environment.getVendorDirectory()+"app"== /vendor/app
            Environment.getOdmDirectory()+"priv-app"== /omd/piv-app 
            Environment.getOdmDirectory()+"app"== /omd/app 
            Environment.getOemDirectory()+"app"== /oed/piv-app 
            Environment.getProductDirectory()+"priv-app"== /product/piv-app
            Environment.getProductDirectory()+"app"== /product/app
            Environment.getProductServicesDirectory()+"priv-app"== //product_services/priv-app 
            Environment.getProductServicesDirectory()+"app"== //product_services/app **/
          ....
          ....
           // Stub packages must either be replaced with full versions in the /data
            // partition or be disabled.
            final List<String> stubSystemApps = new ArrayList<>();
            if (!mOnlyCore) {
                // do this first before mucking with mPackages for the "expecting better" case
                //mPackages是在方法scanDirTracedLI中赋值的
                final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
                while (pkgIterator.hasNext()) {
                    final PackageParser.Package pkg = pkgIterator.next();
                    if (pkg.isStub) {
                        stubSystemApps.add(pkg.packageName);
                    }
                }
            ....
            ....
            }
           ....
           ....
           if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                //sAppInstallDir这个目录是 Environment.getDataDirectory()+ "app" == /data/app
                //扫描三方安装目录
                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
                ....
                ....
                // Uncompress and install any stubbed system applications.
                // This must be done last to ensure all stubs are replaced or disabled.
                //安装系统应用
                installSystemStubPackages(stubSystemApps, scanFlags); 
            }
            ....
            ....
            // Now that we know all of the shared libraries, update all clients to have
            // the correct library paths.
            //更新所有应用库的路径
            updateAllSharedLibrariesLocked(null, Collections.unmodifiableMap(mPackages));
            ....
            ...
            for (String pkgName : deferPackages) {
            ....
                  //最后会调到 mInstaller.createAppData, 
                  //通过Binder调用到 进程installd,最终调用installd的createAppData()
                  prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags, true /* maybeMigrateAppData */);
            ....
            }
    }

方法scanDirTracedLI()

--> scanDirLI()此方法中又有两个值得关注的方法
-->parallelPackageParser.submit(file, parseFlags)
-->scanPackageChildLI()

 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);
        }
    }
 private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
        ....
        ....
       try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback)) {
            // Submit files for parsing in parallel
            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文件,主要是对AndroidManifest.xml进行解析,解析后所有的信息放在Package对象中
                parallelPackageParser.submit(file, parseFlags);
                fileCount++;
            }
            // Process results one by one
            for (; fileCount > 0; fileCount--) {
                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                ....
                ....
                        scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,currentTime, null);
            ....
            }
       }
          

        ....
 }
  • 首先看parallelPackageParser.submit方法
    ParallelPackageParser --> parallelPackageParser.submit(file, parseFlags) --> parsePackage()
    /**
     * Submits the file for parsing
     * @param scanFile file to scan
     * @param parseFlags parse falgs
     */
    public void submit(File scanFile, int parseFlags) {
        mService.submit(() -> {
            ParseResult pr = new ParseResult();
            ...
            try {
                ....
                ....
                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) {
                ....
            }
        });
    }
    @VisibleForTesting
    protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
            int parseFlags) throws PackageParser.PackageParserException {
        return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
    }

继续跟进,这个过程中注意是对apk资源的加载和AndroidManifest.xml的解析
PackageParser -> parsePackage() 其中两个方法最终都会调用到 parseBaseApk()
-->parseBaseApk()-->parseBaseApkCommon() 中包括解析四大组件,权限等等方法
-->parseBaseApplication() 具体往里跟进一下
--->parsePermission()
....

       public Package parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
        if (packageFile.isDirectory()) {
            return parseClusterPackage(packageFile, flags);
        } else {
            return parseMonolithicPackage(packageFile, flags);
        }
    }
    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
        //因为AndroidManifest.xml 文件解析节点很多很庞大,这一步是对其进行一个初步解析,
        //获取应用目录的PackageLite对象,这个对象分开保存了目录下的核心应用以及非核心应用
        //主要用于对apk资源的加载
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
    ....
    ....
            //对apk资源的加载
            final AssetManager assets = assetLoader.getBaseAssetManager();
    ....
                //对核心应用解析 parseBaseApk
            final Package pkg = parseBaseApk(baseApk, assets, flags);
    ....
    ....
                for (int i = 0; i < num; i++) {
                    //对非核心apk资源的加载
                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
                    //对非核心应用的处理
                    parseSplitApk(pkg, i, splitAssets, flags);
                }
    }

    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
        //对AndroidManifest.xml 文件进行初步解析,主要用于对apk资源的加载
        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
        ....
         final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
        ....
            //对核心应用解析 ,assetLoader.getBaseAssetManager()是对apk资源的加载
            final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
    }
    
    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
        ....
        ....
        //获得一个 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);
        ....
        ....
    }
    private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
    ....
    ....
    return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
    }
    private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
            IOException {
            .....
            ....
            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 (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                          return null;
                      }
                  } 
                  .....
                  .....
                  // //如果标签是"permission"
                  else if (tagName.equals(TAG_PERMISSION)) {
                      if (!parsePermission(pkg, res, parser, outError)) {
                          return null;
                      }
            }
            }
    .....
    ....
    }
    //在该方法中可以看出四大组件的解析过程
    private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
    ....
    ....
        if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
                        owner.baseHardwareAccelerated);
                ....
                }

                ....

            } else if (tagName.equals("receiver")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                        true, false);
                .....
                ....

            } else if (tagName.equals("service")) {
                Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
                .....

            } else if (tagName.equals("provider")) {
                Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
                .....

            }
    }
  • 在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象,下一步就是将该 Package 加入到系统中,接下来是方法scanPackageChildLI()
    scanPackageChildLI() --> addForInitLI()
    主要进行安装包校验、签名检查、apk更新等操作
/**
     *  Scans a package and returns the newly parsed package.
     *  @throws PackageManagerException on a parse error.
     */
    @GuardedBy({"mInstallLock", "mPackages"})
    private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
            final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
            @Nullable UserHandle user){
        if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
            if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
                scanFlags |= SCAN_CHECK_ONLY;
            }
        } else {
            scanFlags &= ~SCAN_CHECK_ONLY;
        }

        //在addForInitLI()中,进行安装包校验、签名检查、apk更新等操作,把Package加入系统
        // 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);
            addForInitLI(childPackage, parseFlags, scanFlags,
                    currentTime, user);
        }


        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
            return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
        }

        return scannedPkg;
    }

以上是scanDirTracedLI()方法的流程,简单总结一下就是扫描系统存放apk目录解析AndroidManifest.xml文件生成Package,然后进行校验、签名检查、apk更新等操作,把Package加入系统;

接下来是方法 installSystemStubPackages(),

主要功能是对压缩编译后的apk.gz进行解压安装,为了优化system分区空间,对app进行压缩编译
installSystemStubPackages()--> installStubPackageLI(pkg, 0, scanFlags) 中有两个关键方法
-->decompressPackage()
-->scanPackageTracedLI()

    private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
            @ScanFlags int scanFlags) {
        ....
        ....
        installStubPackageLI(pkg, 0, scanFlags);
    }

    private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg,
            @ParseFlags int parseFlags, @ScanFlags int scanFlags)
                    throws PackageManagerException {
        ....
        // uncompress the binary to its eventual destination on /data
        //解压到data目录
        final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath);
        ...
        ...
         try {
            return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
        } 
    ....
    }
  • 首先看方法decompressPackage() --> PackageManagerServiceUtils -->decompressFile()
    private File decompressPackage(String packageName, String codePath) {
        //过滤是否有压缩编译的,gz文件
        final File[] compressedFiles = getCompressedFiles(codePath);
        if (compressedFiles == null || compressedFiles.length == 0) {
            .....
            return null;
        }
        //这个Environment.getDataAppDirectory是/data/app目录,也就是说解压安装压缩编译后的路径是和通过install安装的分区目录是一样的
        final File dstCodePath =
                getNextCodePath(Environment.getDataAppDirectory(null), packageName);
        int ret = PackageManager.INSTALL_SUCCEEDED;
        try {
            Os.mkdir(dstCodePath.getAbsolutePath(), 0755);
            Os.chmod(dstCodePath.getAbsolutePath(), 0755);
            for (File srcFile : compressedFiles) {
                final String srcFileName = srcFile.getName();
                final String dstFileName = srcFileName.substring(
                        0, srcFileName.length() - COMPRESSED_EXTENSION.length());
                final File dstFile = new File(dstCodePath, dstFileName);
                ret = decompressFile(srcFile, dstFile);
                .....
            }
        }
        .....
        .....
        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
            NativeLibraryHelper.Handle handle = null;
            try {
                handle = NativeLibraryHelper.Handle.create(dstCodePath);
                //释放apk中的so文件到本地目录
                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                        null /*abiOverride*/);
            }
            .....
        }
        ....
        return dstCodePath;
    }

PackageManagerServiceUtils -->decompressFile()
解压压缩文件

    public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
        if (DEBUG_COMPRESSION) {
            Slog.i(TAG, "Decompress file"
                    + "; src: " + srcFile.getAbsolutePath()
                    + ", dst: " + dstFile.getAbsolutePath());
        }
        try (
                InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
                OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
        ) {
            FileUtils.copy(fileIn, fileOut);
            Os.chmod(dstFile.getAbsolutePath(), 0644);
            return PackageManager.INSTALL_SUCCEEDED;
        } catch (IOException e) {
            logCriticalInfo(Log.ERROR, "Failed to decompress file"
                    + "; src: " + srcFile.getAbsolutePath()
                    + ", dst: " + dstFile.getAbsolutePath());
        }
        return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    }
  • 再看installStubPackageLI() --> scanPackageTracedLI()--> scanPackageLI()
    接下来跟上面分析的扫描系统指定目录对apk的处理是一样的
    -->PackageParser-->pp.parsePackage()先解析
    -->scanPackageChildLI()再进行校验、签名检查、apk更新等操作,把Package加入系统;
    private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
      .....
      return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
      .....
    }
    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        ....
        PackageParser pp = new PackageParser();
        ....
        pkg = pp.parsePackage(scanFile, parseFlags);
        ....
        return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
    }

到这里已经我们已经分析到了系统应用AndroidManifest.xml被解析过程,这些都是再PMS的构造方法中执行的,而PMS是再SystemServer中被启动的,所以想要在系统目录下/system、//vendor等等目录下的apk的AndroidManifest.xml生效,需要重启系统,或者重启SystemServer进程。

另外关于系统应用安装?
安装的原理就是把通过进程间通信通过PMS完成对apk复制到指定目录/data/app、/system/app、/system/priv-app等,对齐进行解析,的操作,系统应用本身就在指定目录,所以在PMS启动后完成解析就是安装完成了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容