Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android PakageManagerService啟動流程分析

android PakageManagerService啟動流程分析

編輯:關於Android編程

1.PakageManagerService概述

PakageManagerService是android系統中一個核心的服務,它負責系統中Package的管理,應該程序的安裝、卸載等。後面PakageManagerService簡稱PMS。

2.SystemServer啟動PackageManagerService

我之前的ATA文章有說到,SystemServer進程是Zygote孵化出的第一個進程,該進程主要的工作是啟動android系統服務進程,其中包括PackageManagerService服務,SystemServer啟動PMS關鍵源碼如下:


    private void startBootstrapServices() {
        //...
         //調用PMS的main函數
         mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
         //判斷本次是否為初次啟動,當Zygote或者SystemServer退出時,init會再次啟動它們,所以這裡
         //的firstBoot指的是開機後的第一次啟動
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();
      //...
    } 

關鍵點

PMS的main函數,該函數是PKM的核心。

3.PMS的main方法

PackageManagerService的主要功能是,掃描Android系統中幾個目標文件夾的APK,建立對應的數據結構來管理Package信息、四大組件信息、權限信息等各種信息。例如PKMS解析APK包中的AndroidMainfest.xml,並根據其中聲明的Activity標簽來創建對應的對象並加以保管。PMS的main方法的代碼如下:

 public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        //new 一個PackageManagerService對象
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        //PKM注冊到ServiceManager上。ServiceManager相當於安卓系統服務的DNS服務器
        ServiceManager.addService("package", m);
        return m;
 }

該方法看似很簡單,只有幾行代碼,然而執行事件卻比較長,這是因為PMS在其構造函數中做了很多的“重體力活”,這也是android啟動速度慢的主要因素之一。安裝的應用越多,系統啟動開機時間越長。
PMS構造函數的主要工作流程

  • 掃描目標文件夾之前的准備工作。
  • 掃描目標文件夾。
  • 掃描之後的工作。

4.PMS的前期准備工作

4.1探究Setting


public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis());

        if (mSdkVersion <= 0) {
            Slog.w(TAG, "**** ro.build.version.sdk not set!");
        }

        mContext = context;
        //是否在工廠測試模式下,假定為false
        mFactoryTest = factoryTest;
        mOnlyCore = onlyCore;
        //如果此系統是“eng”版,掃描Package後,不對package做dex優化
        mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
        //用於存儲與顯示屏相關的一些屬性,例如屏幕的寬高分辨率等。
        mMetrics = new DisplayMetrics();
        mSettings = new Settings(mPackages);
        //第一個參數是字符串“android.uid.system”;第二個是SYSTEM_UID,其值為1000,
        //第三個是FLAG_SYSTEM標志,用於標識系統Package。
        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);

4.1.1Android系統中UID/GID

UID為用戶ID的縮寫,GID為用戶組ID的縮寫,這兩個概念均與Linux系統中進程的權限管理有關。一般來說,每一個進程都有一個對應的UID,表示該進程屬於哪個用戶,不同用戶有不同權限。一個進程也可分屬不同的用戶組,每個用戶組都有對應的權限。
在android系統中,系統定義的UID/GID在Process.java文件中,關鍵源碼如下所示


    /**
     * Defines the UID/GID under which system code runs.
     */
    public static final int SYSTEM_UID = 1000;

    /**
     * Defines the UID/GID under which the telephony code runs.
     */
    public static final int PHONE_UID = 1001;

    /**
     * Defines the UID/GID for the user shell.
     * @hide
     */
    public static final int SHELL_UID = 2000;

    /**
     * Defines the UID/GID for the log group.
     * @hide
     */
    public static final int LOG_UID = 1007;

    /**
     * Defines the UID/GID for the WIFI supplicant process.
     * @hide
     */
    public static final int WIFI_UID = 1010;

    /**
     * Defines the UID/GID for the mediaserver process.
     * @hide
     */
    public static final int MEDIA_UID = 1013;

    /**
     * Defines the UID/GID for the DRM process.
     * @hide
     */
    public static final int DRM_UID = 1019;

    /**
     * Defines the UID/GID for the group that controls VPN services.
     * @hide
     */
    public static final int VPN_UID = 1016;

    /**
     * Defines the UID/GID for the NFC service process.
     * @hide
     */
    public static final int NFC_UID = 1027;

    /**
     * Defines the UID/GID for the Bluetooth service process.
     * @hide
     */
    public static final int BLUETOOTH_UID = 1002;

    /**
     * Defines the GID for the group that allows write access to the internal media storage.
     * @hide
     */
    public static final int MEDIA_RW_GID = 1023;

    /**
     * Access to installed package details
     * @hide
     */
    public static final int PACKAGE_INFO_GID = 1032;

    /**
     * Defines the UID/GID for the shared RELRO file updater process.
     * @hide
     */
    public static final int SHARED_RELRO_UID = 1037;

    /**
     * Defines the start of a range of UIDs (and GIDs), going from this
     * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
     * to applications.
     */
    public static final int FIRST_APPLICATION_UID = 10000;

    /**
     * Last of application-specific UIDs starting at
     * {@link #FIRST_APPLICATION_UID}.
     */
    public static final int LAST_APPLICATION_UID = 19999;

    /**
     * First uid used for fully isolated sandboxed processes (with no permissions of their own)
     * @hide
     */
    public static final int FIRST_ISOLATED_UID = 99000;

    /**
     * Last uid used for fully isolated sandboxed processes (with no permissions of their own)
     * @hide
     */
    public static final int LAST_ISOLATED_UID = 99999;
4.1.2 探究SharedUserSetting

Setting中有一個mShareUsers成員,該成員存儲的是字符串變量name與SharedUserSetting健值對。


SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
        //mSharedUsers是一個HashMap.key為字符串,值為ShareUserSetting對象
        SharedUserSetting s = mSharedUsers.get(name);
        if (s != null) {
            if (s.userId == uid) {
                return s;
            }
           //...
                       return null;
        }
        創建一個SharedUserSetting對象,並設置為userid為uid
        s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
        s.userId = uid;
        if (addUserIdLPw(uid, s, name)) {
            mSharedUsers.put(name, s);
            return s;
        }
        return null;
    }

例如在SystemUI.apk的AndroidManifest.xml文件中,有關鍵代碼:


        ....

在該標簽中,聲明了一個android:sharedUserId的屬性,其值為“android.uid.system”。sharedUserId和UID有關,它的作用是

  • 兩個或者多個聲明了同一種sharedUserid的APK可共享彼此的數據,並且可運行在同一進程中。
  • 通過聲明特定的sharedUserId,該APK所在的進程將被賦予指定UID。

例如SystemUI聲明了system的uid,運行SystemUI的進程就可享有system用戶所對應的權限了,實際上就是將該進程的UID設置為system的uid了

接下來分析addUserIdLPw的功能,它主要就是將SharedUserSettings對象保存到對應的數組中,代碼如下

private boolean addUserIdLPw(int uid, Object obj, Object name) {
        //uid不能超出限制,Android對uid進行歸納,系統APK所在進程小於10000
        //應用APK所在進程的uid從10000開始
        if (uid > Process.LAST_APPLICATION_UID) {
            return false;
        }
        //FIRST_APPLICATION_UID = 10000,屬於應用APK
        if (uid >= Process.FIRST_APPLICATION_UID) {
            int N = mUserIds.size();
            //計算索引,其值是uid和FIRST_APPLICATION_UID的差
            final int index = uid - Process.FIRST_APPLICATION_UID;
            while (index >= N) {
                mUserIds.add(null);
                N++;
            }
            //如果索引位置不為空,返回
            if (mUserIds.get(index) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate user id: " + uid
                        + " name=" + name);
                return false;
            }
            //mUserIds保存應用Package的uid,obj是SharedUserSettings
            mUserIds.set(index, obj);
        } else {
            if (mOtherUserIds.get(uid) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate shared id: " + uid
                                + " name=" + name);
                return false;
            }
            mOtherUserIds.put(uid, obj);
        }
        return true;
    }

4.2 XML文件掃描

接下來是掃描系統目錄下與系統權限相關的xml文件,將其存放到PKM中,關鍵源碼如下:

        // 獲取系統相關的權限,它主要是解析系統目錄下xml文件,獲得設備相關的權限
        SystemConfig systemConfig = SystemConfig.getInstance();
        mGlobalGids = systemConfig.getGlobalGids();
        mSystemPermissions = systemConfig.getSystemPermissions();
        mAvailableFeatures = systemConfig.getAvailableFeatures();

        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            //創建一個ThreadHandler對象,實際就是創建一個帶消息隊列循環處理的線程,
            //該線程的工作是:程序的安裝和卸載等。
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            //以ThreadHandler線程的消息循環(Looper對象)作為參數new一個
            //PackageHandler,因此該Handler的handlemessage方法將運行在此線程上
            mHandler = new PackageHandler(mHandlerThread.getLooper());
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
            // /data目錄
            File dataDir = Environment.getDataDirectory();
            // /data/data目錄
            mAppDataDir = new File(dataDir, "data");
            // /data/app目錄
            mAppInstallDir = new File(dataDir, "app");
            // /data/app-lib目錄
            mAppLib32InstallDir = new File(dataDir, "app-lib");
            // /data/app-asec目錄            
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            // /data/user目錄            
            mUserAppDataDir = new File(dataDir, "user");
            // /data/app-private目錄            
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
            //new一個UserManager對象,目前沒有什麼作用,但其前途不可限量。
            //google設想,未來手機將支持多個User,每個User安裝自己的應用
            //該功能為android智能手機推向企業用戶打下基礎
            sUserManager = new UserManagerService(context, this,
                    mInstallLock, mPackages);

            // 獲取系統相關的權限,它主要是解析系統目錄下xml文件,獲得設備相關的權限
            ArrayMap permConfig
                    = systemConfig.getPermissions();
            for (int i=0; i libConfig = systemConfig.getSharedLibraries();
            for (int i=0; i

進一步我們再觀察SystemConfig是如何解析系統權限xml文件的,在SystemConfig的構造函數中,它會去分別讀取etc目錄下的sysconfig,permissions,sysconfig目錄下的文件。

        SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), false);
        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), false);
        // Only read features from OEM config
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), true);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), true);
    }

我們看看到底這些目錄下放著什麼樣的文件,例如/etc/permissions目錄下的文件如下圖:
Screenshot_2016_10_10_15_35_50

我們再打開第一個文件來探究,沒錯,這個文件代表藍牙權限,表示該設備支持藍牙。具體代碼如下




    


總結一下PMS的前期工作,其實就是掃描並解析XML文件,將其中的信息保存到特定的數據結構中。

5.PMS掃描Package

第二個階段的工作主要是掃描系統中的APK,由於需要逐個掃描apk文件,因此手機上安裝的程序越多,PKM的工作量越大,系統啟動速度越慢,也就是開機時間越長。

5.1系統庫的dex優化

以下的代碼主要是對系統庫BOOTCLASSPATH指定,或platform.xml定義,或者/system/frameworks目錄下的jar
和apk包進行一次檢查,該dex優化的優化.dex優化後會在相應的目錄生成.odex文件。/system/frameworks如下圖:
關鍵源碼如下:

            // Set flag to monitor and not change apk file paths when
            // scanning install directories.
            //定義掃描參數
            final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
            //用於存儲已經dex優化過或者不需要優化的包
            final ArraySet alreadyDexOpted = new ArraySet();

            /**
             * Add everything in the in the boot class path to the
             * list of process files because dexopt will have been run
             * if necessary during zygote startup.
             */
             //獲取java啟動類庫的路徑,在init.rc文件中通過BOOTCLASSPATH環境變量輸出
             //主要是/system/framework/下的系統jar包
            final String bootClassPath = System.getenv("BOOTCLASSPATH");
            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
            if (bootClassPath != null) {
                String[] bootClassPathElements = splitString(bootClassPath, ':');
                //循環遍歷/system/framework/下的系統jar包的絕對路徑,添加到alreadyDexOpted
                for (String element : bootClassPathElements) {
                    alreadyDexOpted.add(element);
                }
            } 
            if (mSharedLibraries.size() > 0) {
                //...
                 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                                alreadyDexOpted.add(lib);
                                //dex優化
                                mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);        

                }
            }

            //...

            File frameworkDir = new File(Environment.getRootDirectory(), "framework");
            //framework-res.apk定義了系統常用的資源,還有幾個重要的Activity,如長按Power鍵彈出選擇框
            //不需要dex優化
            alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");

            alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");

            //掃描framework/下的apk或者jar進行dex優化
            String[] frameworkFiles = frameworkDir.list();
            if (frameworkFiles != null) {
                // TODO: We could compile these only for the most preferred ABI. We should
                // first double check that the dex files for these commands are not referenced
                // by other system apps.
                for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                    for (int i=0; i<frameworkfiles.length; dexoptneeded="" file="" if="" int="" libpath="new" path="libPath.getPath();" pre="" string="">

5.2掃描系統的APK

對apk或者jar進行dex優化後,現在PKM進入了重點階段,掃描系統的APK,每一個APK對應一個Package對象,主要是掃描APK的AndroidManifest.xml,解析application標簽及其子標簽actvity、service、recever等,也就是android的四大組件,解析後將它們保存到Package對應的數據結構中,最後將它們注冊到PKM中,要掃描以下幾個目錄:

  • /system/frameworks:該目錄下的文件都是系統庫,例如service.jar、framework.jar、framework-res.apk。不過只掃描framework-res.apk文件
</frameworkfiles.length;>
  • /system/app:該目錄下全是默認的系統應用和廠商特定的APK文件,例如Buletooth.apk、和SystemUI.apk等
<frameworkfiles.length; dexoptneeded="" file="" if="" int="" libpath="new" path="libPath.getPath();" pre="" string="">

解析AndroidManifest.xml關鍵的源碼如下:

</frameworkfiles.length;>
private Package parseBaseApk(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)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); //解析 if (tagName.equals("application")) { if (foundApp) { if (RIGID_PARSER) { outError[0] = " has more than one "; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, " has more than one "); XmlUtils.skipCurrentTag(parser); continue; } } foundApp = true; //解析及其子標簽 if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) { return null; } //解析 } else if (tagName.equals("overlay")) { } else if (tagName.equals("key-sets")) { } 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-permission-sdk-m") || tagName.equals("uses-permission-sdk-23")) { } else if (tagName.equals("uses-configuration")) { } else if (tagName.equals("uses-feature")) { } else if (tagName.equals("feature-group")) { } else if (tagName.equals("uses-sdk")) { } else if (tagName.equals("supports-screens")) { } 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("supports-input")) { } else if (tagName.equals("eat-comment")) { } else if (RIGID_PARSER) { } else { } } } 

具體看一下怎麼解析application標簽下的四大組件的,依次解析activity,receiver,service,provider,其中可以發現,receiver被當成activity來解析了,PKM通過PackageParser類將解析後的四大組件保存到對應數據結構中,也就是存放到PackageParser的activities,receivers,providers,services對象中。關鍵源碼如下:


private boolean parseBaseApplication(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError){
            //...
 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            //activity標簽
            if (tagName.equals("activity")) {
                //解析activity標簽
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                        owner.baseHardwareAccelerated);
                //添加activity到owner.activities中
                owner.activities.add(a);
            //receiver標簽
            } else if (tagName.equals("receiver")) {
                //解析receiver標簽,receiver其實被當成Activity來解析了。
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
            //添加activity到owner.activities中
                owner.receivers.add(a);
            //service標簽
            } else if (tagName.equals("service")) {
                //解析service標簽
                Service s = parseService(owner, res, parser, attrs, flags, outError);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
            //添加service到owner.services中
                owner.services.add(s);
            //provider標簽
            } else if (tagName.equals("provider")) {
                Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
                if (p == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.providers.add(p);

            } else if (tagName.equals("activity-alias")) {
            } else if (parser.getName().equals("meta-data")) {

            } else if (tagName.equals("uses-library")) {


            } else if (tagName.equals("uses-package")) {
            }
        }            
}

在PackageParser掃描完一個APK後,此時系統已經根據APK中的AndroidMainifest.xml,創建了一個Package對象,下一步是將該Package加入到系統中。此時調用scanPackageDirtyLI方法,scanPackageDirtyLI首先會對packageName為“android”的apk做單獨的處理,該apk其實就是framework-res.apk,它包含了幾個常見的activity

  • ChooserActivity:當startActivity有多個Acitvity符合時,系統會彈出此Acitivity,由用戶選擇合適的應用來處理
  • ShutDownActivity:關機前彈出的選擇對話框
  • RingtonePickerAcitivity:鈴聲選擇Activity
<frameworkfiles.length; dexoptneeded="" file="" if="" int="" libpath="new" path="libPath.getPath();" pre="" string=""></frameworkfiles.length;>

scanPackageDirtyLI關鍵代碼如下:

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        final File scanFile = new File(pkg.codePath);
        if (pkg.applicationInfo.getCodePath() == null ||
                pkg.applicationInfo.getResourcePath() == null) {
            // Bail out. The resource and code paths haven't been set.
            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                    "Code and resource paths haven't been set correctly");
        }

        if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        } else {
            // Only allow system apps to be flagged as core apps.
            pkg.coreApp = false;
        }

        if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
        }

        if (mCustomResolverComponentName != null &&
                mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
            setUpCustomResolverActivity(pkg);
        }

        if (pkg.packageName.equals("android")) {
            synchronized (mPackages) {
                if (mAndroidApplication != null) {
                   //...  
                }

                //保存該package信息
                mPlatformPackage = pkg;
                pkg.mVersionCode = mSdkVersion;
                //保存該package的ApplicationInfo
                mAndroidApplication = pkg.applicationInfo;

                if (!mResolverReplaced) {
                    //mResolveActivity為ChooserActivity信息的ActivityInfo
                    mResolveActivity.applicationInfo = mAndroidApplication;
                    mResolveActivity.name = ResolverActivity.class.getName();
                    mResolveActivity.packageName = mAndroidApplication.packageName;
                    mResolveActivity.processName = "system:ui";
                    mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                    mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
                    mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
                    mResolveActivity.exported = true;
                    mResolveActivity.enabled = true;
                    //mResolveInfo用於存儲系統解析Intent後得到的結果信息,在從PKM查詢滿足某個Intent的
                    //Activity時,返回的就是ResolveInfo,再根據ResolveInfo的activityInfo的信息得到                         //Activity
                    mResolveInfo.activityInfo = mResolveActivity;
                    mResolveInfo.priority = 0;
                    mResolveInfo.preferredOrder = 0;
                    mResolveInfo.match = 0;
                    mResolveComponentName = new ComponentName(
                            mAndroidApplication.packageName, mResolveActivity.name);
                }
            }
        }
 }

5.4掃描系統和非系統apk總結

PKM在這個過程中工作任務非常繁重,要創建很多的對象,所以它是一個耗時耗內存的操作,從流程來看,PKM在這個過程中無非是掃描XML或者APK文件,但是其中涉及的數據結構及它們的關系較為復雜。

“android“該Package與系統有非常重要的作用,這裡保存特殊處理保存該Package的信息,主要是為了提高運行過程中的效率,例如ChooserActivity使用的地方非常多。 接下裡scanPackageDirtyLI方法會對系統其它的Package做處理,關鍵源碼如下:

//mPackages用於保存系統內的所有Package,以packageName為key
 if (mPackages.containsKey(pkg.packageName)
                || mSharedLibraries.containsKey(pkg.packageName)) {
           //...
                   }

        if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
            if (mExpectingBetter.containsKey(pkg.packageName)) {
                logCriticalInfo(Log.WARN,
                        "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
            } else {
                PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
                if (known != null) {
                    if (DEBUG_PACKAGE_SCANNING) {
                        Log.d(TAG, "Examining " + pkg.codePath
                                + " and requiring known paths " + known.codePathString
                                + " & " + known.resourcePathString);
                    }
                    if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
                            || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
                        throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
                                "Application package " + pkg.packageName
                                + " found at " + pkg.applicationInfo.getCodePath()
                                + " but expected at " + known.codePathString + "; ignoring.");
                    }
                }
            }
        }

        // Initialize package source and resource directories
        File destCodeFile = new File(pkg.applicationInfo.getCodePath());
        File destResourceFile = new File(pkg.applicationInfo.getResourcePath());

        SharedUserSetting suid = null;
        PackageSetting pkgSetting = null;

        if (!isSystemApp(pkg)) {
            // Only system apps can use these features.
            pkg.mOriginalPackages = null;
            pkg.mRealPackage = null;
            pkg.mAdoptPermissions = null;
        }
        //...
        final String pkgName = pkg.packageName;

        final long scanFileTime = scanFile.lastModified();
        final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;
        //確定運行該package的進程名,一般用package作為進程名
        pkg.applicationInfo.processName = fixProcessName(
                pkg.applicationInfo.packageName,
                pkg.applicationInfo.processName,
                pkg.applicationInfo.uid);

        File dataPath;
        if (mPlatformPackage == pkg) {
            // The system package is special.
            dataPath = new File(Environment.getDataDirectory(), "system");

            pkg.applicationInfo.dataDir = dataPath.getPath();

        } else {
            // This is a normal package, need to make its data directory.
            //該函數返回data/data/packageName
            dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
                    UserHandle.USER_OWNER, pkg.packageName);

            boolean uidError = false;
            if (dataPath.exists()) {
                //..
            } else {
                if (DEBUG_PACKAGE_SCANNING) {
                    if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
                        Log.v(TAG, "Want this data dir: " + dataPath);
                }
                //該方法調用installer發送install命令,其實就是在/data/data/目錄下建立packageName目錄
                //然後為系統所有的user安裝此apk
                int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
                        pkg.applicationInfo.seinfo);
                //安裝錯誤
                if (ret < 0) {
                    // Error from installer
                    throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                            "Unable to create data dirs [errorCode=" + ret + "]");
                }

            }

            pkgSetting.uidError = uidError;
        }

        final String path = scanFile.getPath();
        final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
        //在/data/data/pageName/lib下建立和CPU類型對應的目錄,例如ARM平台的事arm/,MIP平台的事mips/
        if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
            derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);

            //系統package的native庫統一放在/system/lib下,
            //所以系統不會提取系統package目錄apk包中的native庫
            if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
                    pkg.applicationInfo.primaryCpuAbi == null) {
                setBundledAppAbisAndRoots(pkg, pkgSetting);
                setNativeLibraryPaths(pkg);
            }

        } else {
            if ((scanFlags & SCAN_MOVE) != 0) {
            //
            setNativeLibraryPaths(pkg);
        }
        //...
        if ((scanFlags & SCAN_NO_DEX) == 0) {
            //對該APK做dex優化
            int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
                    forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */);
      //如果該apk已經存在,要先殺掉該APK的進程
         if ((scanFlags & SCAN_REPLACING) != 0) {
            killApplication(pkg.applicationInfo.packageName,
                        pkg.applicationInfo.uid, "replace pkg");
        }
        //在此之前,四大組件信息都是Package對象的私有的,在這裡把它們注冊到PKM內部的財產管理對象中。
        //這樣,PKMS就可對外提供統一的組件信息。
        synchronized (mPackages) {
                ...
            //注冊該Package中的provider到PKM的mProviders上
            int N = pkg.providers.size();
            StringBuilder r = null;
            int i;
            for (i=0; i

5.3掃描非系統apk

在PackageManagerService構造函數掃描完系統apk後,接下來就是掃描非系統apk,這些apk在/data/app或者/data/app-private中。如下圖: 下面是關鍵源碼,scanDirLI已經在前面分析過了。跟系統apk的調用過程差不多。

        if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);
        }

5.4掃描結果保存到文件中

在PackageManagerService構造函數收尾階段,PMS將前面收集的信息再整理一次,將已安裝的apk信息寫到package.xml、pacakage.list和package-stopped.xml中

            //整理更新Permisssion的信息
            updatePermissionsLPw(null, null, updateFlags);
            //...

            //將信息寫到package.xml,pacakage.list和package-stopped.xml中
            mSettings.writeLPr();

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());

            mRequiredVerifierPackage = getRequiredVerifierLPr();
            mRequiredInstallerPackage = getRequiredInstallerLPr();

        } // synchronized (mPackages)
        } // synchronized (mInstallLock)

        //gc
        Runtime.getRuntime().gc();
  • packages.xml:系統對程序安裝,卸載和更新等操作時會更新該文件,PMS掃描完目標文件夾後創建該文件,保存了Package相關的信息。
  • packages.list:保存著系統中所有的非系統自帶的APK信息,程序安裝,卸載和更新會更新該文件。
  • packages-stoped.xml:保存系統中被用戶強制停止的Package的信息。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved