Android PackageManagerService加载apk流程

        近日研习古书《菜根谭》,提到“闲时吃紧,忙里偷闲”,大意为君子闲时要有吃紧的心思,忙处要有悠闲的趣味。

        前阵子忙完了一个底层驱动的feature的任务,这两天稍闲,遂准备老生常谈,总结一下开机时apk的加载。

        至于Android系统从开机到如何启动到PMS,不做赘述,想了解的朋友可以参看我的另外一篇博文:http://blog.csdn.net/dkbdkbdkb/article/details/51799791 关于zygote启动的,里边有SystemServer的相关启动部分。

1)开启PMS的代码来自SystemServer.java的initAndLoop方法。

public void initAndLoop() {
          ......
pm = PackageManagerService.main(context, installer,
                    factoryTest != SystemServer.FACTORY_TEST_OFF,
                     onlyCore);

          ......
}

2) 调用到了PMS的main方法,跟进。

    public static final IPackageManager main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore); 
        ServiceManager.addService("package", m);     
        return m;
    }

3) 接下来我们看new一个PMS实例做了哪些具体工作。

File vendorAppDir = new File(Environment.getRootDirectory(), "/vendor/app");
mVendorInstallObserver = new AppDirObserver(
    vendorAppDir.getPath(), OBSERVER_EVENTS, true, false, false);
mVendorInstallObserver.startWatching();
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
/// M: [ALPS00098646] Mtprof tool
addBootEvent(new String("Android:PMS_scan_done:" + vendorAppDir.getPath().toString()));

核心的流程就是调用scanDirLi方法,扫描了许多系统app存放的目录,诸如system/app, data/app,vendor/app等。。。

4)scanDirLi实现:

.......
       for (i=0; i<files.length; i++) {
            File file = new File(dir, files[i]);
            if (!isPackageFilename(files[i])) {
                // Ignore entries which are not apk's
                continue;
            }     

            /** M: [ALPS00118168] Add PMS scan package time log @{ */
            startScanTime = SystemClock.uptimeMillis();
            Log.d(TAG,"scan package: " + file.toString() + " , start at: " + startScanTime + "ms.");
            PackageParser.Package pkg = scanPackageLI(file,
                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
            endScanTime = SystemClock.uptimeMillis();
            Log.d(TAG,"scan package: " + file.toString() + " , end at: " + endScanTime + "ms. elapsed time = " + (endScanTime - startScanTime) + "ms.");
            /** @} */

            // Don't mess around with apps in system partition.
            if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                    mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
                // Delete the apk
                Slog.w(TAG, "Cleaning up failed install of " + file);
                file.delete();
            }     
        }     
.........

该方法首先检测传入的Dir的路径下的file是否为apk文件,如果是的话,调用scanPackageLi继续往下走。

5)scanPackageLi

private PackageParser.Package scanPackageLI(File scanFile,
            int parseFlags, int scanMode, long currentTime, UserHandle user) {
        mLastScanError = PackageManager.INSTALL_SUCCEEDED;
        String scanPath = scanFile.getPath();
        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
        parseFlags |= mDefParseFlags;
        PackageParser pp = new PackageParser(scanPath);
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        final PackageParser.Package pkg = <span style="color:#ff0000;">pp.parsePackage</span>(scanFile,
                scanPath, mMetrics, parseFlags);
        if (pkg == null) {
            mLastScanError = pp.getParseError();
            return null; 
        }     
        .......
        PackageParser.Package scannedPkg = <span style="color:#ff0000;">scanPackageLI</span>(pkg, parseFlags, scanMode
                | SCAN_UPDATE_SIGNATURE, currentTime, user);

        /*
         * If the system app should be overridden by a previously installed
         * data, hide the system app now and let the /data/app scan pick it up
         * again.
         */
        if (shouldHideSystemApp) {
            synchronized (mPackages) {
                /*
                 * We have to grant systems permissions before we hide, because
                 * grantPermissions will assume the package update is trying to
                 * expand its permissions.
                 */
                grantPermissionsLPw(pkg, true);
                mSettings.disableSystemPackageLPw(pkg.packageName);
            }
        }

        return scannedPkg;
    }

红色字体的两个方法是主线。

6)我们先看第一条线:parsePackage

public Package parsePackage(File sourceFile, String destCodePath,
         DisplayMetrics metrics, int flags) {
                  ......
        mArchiveSourcePath = sourceFile.getPath();

                  ......
        XmlResourceParser parser = null;
        AssetManager assmgr = null;
        Resources res = null;
        boolean assetError = true;
        try {
            assmgr = new AssetManager();
            int cookie = assmgr.addAssetPath(mArchiveSourcePath);
            if (cookie != 0) {
                res = new Resources(assmgr, metrics, null);
                assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                        Build.VERSION.RESOURCES_SDK_INT);
                parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
                assetError = false;
            } else {
                Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
            }
        } catch (Exception e) {
            Slog.w(TAG, "Unable to read AndroidManifest.xml of "
                    + mArchiveSourcePath, e);
        }

             ......
        String[] errorText = new String[1];
        Package pkg = null;
        Exception errorException = null;
        try {
            // XXXX todo: need to figure out correct configuration.
            pkg = parsePackage(res, parser, flags, errorText);
        } catch (Exception e) {
            errorException = e;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
        }

            ......
        parser.close();
        assmgr.close();

        // Set code and resource paths
        pkg.mPath = destCodePath;
        pkg.mScanPath = mArchiveSourcePath;
        //pkg.applicationInfo.sourceDir = destCodePath;
        //pkg.applicationInfo.publicSourceDir = destRes;
        pkg.mSignatures = null;

        return pkg;
    }

每一个Apk文件都是一个归档文件,它里面包含了Android应用程序的配置文件AndroidManifest.xml,这里主要就是要对这个配置文件就行解析了,从Apk归档文件中得到这个配置文件后,就调用另一外版本的parsePackage函数对这个应用程序进行解析了:

public class PackageParser {  
    ......  
  
    private Package parsePackage(  
            Resources res, XmlResourceParser parser, int flags, String[] outError)  
            throws XmlPullParserException, IOException {  
        ......  
  
        String pkgName = parsePackageName(parser, attrs, flags, outError);  
          
        ......  
  
        final Package pkg = new Package(pkgName);  
  
        ......  
  
        int type;  
  
        ......  
          
        TypedArray sa = res.obtainAttributes(attrs,  
            com.android.internal.R.styleable.AndroidManifest);  
  
        ......  
  
        while ((type=parser.next()) != parser.END_DOCUMENT  
            && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {  
                if (type == parser.END_TAG || type == parser.TEXT) {  
                    continue;  
                }  
  
                String tagName = parser.getName();  
                if (tagName.equals("application")) {  
                    ......  
  
                    if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {  
                        return null;  
                    }  
                } else if (tagName.equals("permission-group")) {  
                    ......  
                } else if (tagName.equals("permission")) {  
                    ......  
                } else if (tagName.equals("permission-tree")) {  
                    ......  
                } else if (tagName.equals("uses-permission")) {  
                    ......  
                } else if (tagName.equals("uses-configuration")) {  
                    ......  
                } else if (tagName.equals("uses-feature")) {  
                    ......  
                } else if (tagName.equals("uses-sdk")) {  
                    ......  
                } else if (tagName.equals("supports-screens")) {  
                    ......  
                } else if (tagName.equals("protected-broadcast")) {  
                    ......  
                } else if (tagName.equals("instrumentation")) {  
                    ......  
                } else if (tagName.equals("original-package")) {  
                    ......  
                } else if (tagName.equals("adopt-permissions")) {  
                    ......  
                } else if (tagName.equals("uses-gl-texture")) {  
                    ......  
                } else if (tagName.equals("compatible-screens")) {  
                    ......  
                } else if (tagName.equals("eat-comment")) {  
                    ......  
                } else if (RIGID_PARSER) {  
                    ......  
                } else {  
                    ......  
                }  
        }  
  
        ......  
  
        return pkg;  
    }  
  
    ......  
}

这里就是对AndroidManifest.xml文件中的各个标签进行解析了,这里我们只简单看一下application标签的解析,这是通过调用parseApplication函数来进行的。

7)PackageParser.parseApplication

public class PackageParser {  
    ......  
  
    private boolean parseApplication(Package owner, Resources res,  
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)  
            throws XmlPullParserException, IOException {  
        final ApplicationInfo ai = owner.applicationInfo;  
        final String pkgName = owner.applicationInfo.packageName;  
  
        TypedArray sa = res.obtainAttributes(attrs,  
            com.android.internal.R.styleable.AndroidManifestApplication);  
  
        ......  
  
        int type;  
        while ((type=parser.next()) != parser.END_DOCUMENT  
            && (type != parser.END_TAG || parser.getDepth() > innerDepth)) {  
                if (type == parser.END_TAG || type == parser.TEXT) {  
                    continue;  
                }  
          
                String tagName = parser.getName();  
                if (tagName.equals("activity")) {  
                    Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false);  
                    ......  
  
                    owner.activities.add(a);  
  
                } else if (tagName.equals("receiver")) {  
                    Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true);  
                    ......  
  
                    owner.receivers.add(a);  
                } else if (tagName.equals("service")) {  
                    Service s = parseService(owner, res, parser, attrs, flags, outError);  
                    ......  
  
                    owner.services.add(s);  
                } else if (tagName.equals("provider")) {  
                    Provider p = parseProvider(owner, res, parser, attrs, flags, outError);  
                    ......  
  
                    owner.providers.add(p);  
                } else if (tagName.equals("activity-alias")) {  
                    Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);  
                    ......  
  
                    owner.activities.add(a);  
                } else if (parser.getName().equals("meta-data")) {  
                    ......  
                } else if (tagName.equals("uses-library")) {  
                    ......  
                } else if (tagName.equals("uses-package")) {  
                    ......  
                } else {  
                    ......  
                }  
        }  
  
        return true;  
    }  
  
    ......  
}

这里就是对AndroidManifest.xml文件中的application标签进行解析了,我们常用到的标签就有activity、service、receiver和provider,这里解析完成后,一层层返回到5)中,调用另一个版本的scanPackageLI函数把来解析后得到的应用程序信息保存下来。

8)PackageManagerService.scanPackageLI

class PackageManagerService extends IPackageManager.Stub {  
    ......  
  
    // Keys are String (package name), values are Package.  This also serves  
    // as the lock for the global state.  Methods that must be called with  
    // this lock held have the prefix "LP".  
    final HashMap<String, PackageParser.Package> mPackages =  
        new HashMap<String, PackageParser.Package>();  
  
    ......  
  
    // All available activities, for your resolving pleasure.  
    final ActivityIntentResolver mActivities =  
    new ActivityIntentResolver();  
  
    // All available receivers, for your resolving pleasure.  
    final ActivityIntentResolver mReceivers =  
        new ActivityIntentResolver();  
  
    // All available services, for your resolving pleasure.  
    final ServiceIntentResolver mServices = new ServiceIntentResolver();  
  
    // Keys are String (provider class name), values are Provider.  
    final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent =  
        new HashMap<ComponentName, PackageParser.Provider>();  
  
    ......  
  
    private PackageParser.Package scanPackageLI(PackageParser.Package pkg,  
            int parseFlags, int scanMode, long currentTime) {  
        ......  
  
        synchronized (mPackages) {  
            ......  
  
            // Add the new setting to mPackages  
            mPackages.put(pkg.applicationInfo.packageName, pkg);  
  
            ......  
  
            int N = pkg.providers.size();  
            int i;  
            for (i=0; i<N; i++) {  
                PackageParser.Provider p = pkg.providers.get(i);  
                p.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                    p.info.processName, pkg.applicationInfo.uid);  
                mProvidersByComponent.put(new ComponentName(p.info.packageName,  
                    p.info.name), p);  
  
                ......  
            }  
  
            N = pkg.services.size();  
            for (i=0; i<N; i++) {  
                PackageParser.Service s = pkg.services.get(i);  
                s.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                    s.info.processName, pkg.applicationInfo.uid);  
                mServices.addService(s);  
  
                ......  
            }  
  
            N = pkg.receivers.size();  
            r = null;  
            for (i=0; i<N; i++) {  
                PackageParser.Activity a = pkg.receivers.get(i);  
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                    a.info.processName, pkg.applicationInfo.uid);  
                mReceivers.addActivity(a, "receiver");  
                  
                ......  
            }  
  
            N = pkg.activities.size();  
            for (i=0; i<N; i++) {  
                PackageParser.Activity a = pkg.activities.get(i);  
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                    a.info.processName, pkg.applicationInfo.uid);  
                mActivities.addActivity(a, "activity");  
                  
                ......  
            }  
  
            ......  
        }  
  
        ......  
  
        return pkg;  
    }  
  
    ......  
}

这个函数主要就是把前面解析应用程序得到的package、provider、service、receiver和activity等信息保存在PackageManagerService服务中了。

        


        这仅仅是相当于静默安装完成,至于呈现给用户看的app图标还需要HOME去提取信息去显示。


    原文作者:凯文的内存
    原文地址: https://blog.csdn.net/dkbdkbdkb/article/details/52153017
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞