Android-0.PMS簡介及VirtualApp中安裝App全流程

1.Package Manager簡介

包指的是Apk、jar和so文件等等,它們被加載到Android內存中,由一個包轉變成可執行的代碼,這就需要一個機制來進行包的加載、解析、管理等操作,這就是包管理機制。包管理機制由許多類一起組成,其中核心爲PackageManagerServicePMS),它負責對包進行管理。

PackageManager是一個抽象類,具體實現類爲ApplicationPackageManagerApplicationPackageManager中的方法會通過IPackageManagerPMS進行進程間通信,因此PackageManager所提供的功能最終是由PMS來實現的。

Package Manager的功能主要包含以下部分:

  1. 獲取一個應用程序的所有信息(ApplicationInfo
  2. 獲取ActivityProviderReceiverService四大組件的信息
  3. 權限處理,包括對系統和應用定義的PermissionPermission Group信息的增加、刪除、查詢和檢查
  4. 獲取包的信息,查詢包的UIDGID、包名、系統默認程序等信息
  5. 安裝、卸載APK

APK的安裝場景主要有以下幾種:

  1. 通過adb命令安裝:adb 命令包括adb push/install
  2. 用戶下載的Apk,通過系統安裝器packageinstaller安裝該Apk。

最終都交由PMS來進行處理。

2.PackageInstaller簡介

2.1 PackageInstaller初始化

代碼參考AOSP 中 API 23 (android 6.0.1)

packageinstaller: http://androidxref.com/6.0.1_r10/xref/packages/apps/PackageInstaller/
它的AndroidManifest.xml顯示入口爲PackageInstallerActivity

        <activity android:name=".PackageInstallerActivity"
               android:configChanges="orientation|keyboardHidden|screenSize"
               android:excludeFromRecents="true">

當我們調用PackageInstaller來安裝應用時會跳轉到PackageInstallerActivity,並調用PackageInstallerActivityonCreate方法, 這個方法部分功能爲:

  1. Intent獲取相關數據
  2. 分別對package協議file協議的Uri進行處理,得到包信息PackageInfo
  3. 對不同來源Apk進行處理(已知來源直接調用initiateInstall安裝,未知來源彈框提示)
    代碼如下:
protected void onCreate(Bundle icicle) {
		...		
		// 1.從Intent獲取相關數據
		 final Intent intent = getIntent();
        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
         	....
            mSessionId = sessionId;
            mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
            mOriginatingURI = null;
            mReferrerURI = null;
        } else {
            mSessionId = -1;
            mPackageURI = intent.getData();
            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
        }
        ...
     
 		final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
        final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
		// 是否爲未知來源的APK
        boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
 		...
 		// 2.分別對package協議和file協議的Uri進行處理,得到包信息PackageInfo
 		 final PackageUtil.AppSnippet as;
        if ("package".equals(mPackageURI.getScheme())) { // package協議
            mInstallFlowAnalytics.setFileUri(false);
        	
            mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
                        PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
            ....
        } else { // file協議
            mInstallFlowAnalytics.setFileUri(true);
            final File sourceFile = new File(mPackageURI.getPath());
            PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);

           	....
            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                    PackageManager.GET_PERMISSIONS, 0, 0, null,
                    new PackageUserState());
           ...
        }
        mInstallFlowAnalytics.setPackageInfoObtained();
        ...
	    // 3.對不同來源Apk進行處理
	      if (!requestFromUnknownSource) { // 已知來源的Apk直接安裝
            initiateInstall();
            return;
        }
        ....
        final boolean isManagedProfile = mUserManager.isManagedProfile();
        if (!unknownSourcesAllowedByAdmin
                || (!unknownSourcesAllowedByUser && isManagedProfile)) { // 禁止安裝,就彈出提示Dialog
            showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else if (!unknownSourcesAllowedByUser) { // 彈出詢問框
            // Ask user to enable setting first
            showDialogInner(DLG_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else { // 允許安裝未知來源的APK
            initiateInstall();
        }   

===>可以看到接着是調用了initiateInstall方法, 這個方法部分功能爲:

  1. 得到包名
  2. 根據包名獲取應用程序信息
  3. 調用startInstallConfirm初始化安裝確認界面

代碼如下:

  private void initiateInstall() {
        String pkgName = mPkgInfo.packageName; //1.得到包名
      	// 檢查設備上是否已有包含此名稱的包,但它已被重命名爲其他內容
        String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
            pkgName = oldName[0];
            mPkgInfo.packageName = pkgName;
            mPkgInfo.applicationInfo.packageName = pkgName;
        }
        // Check if package is already installed. display confirmation dialog if replacing pkg
        try {
            // 2.根據包名獲取應用程序信息
            mAppInfo = mPm.getApplicationInfo(pkgName,
                    PackageManager.GET_UNINSTALLED_PACKAGES);
            if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
                mAppInfo = null;
            }
        } catch (NameNotFoundException e) {
            mAppInfo = null;
        }

        mInstallFlowAnalytics.setReplace(mAppInfo != null);
        mInstallFlowAnalytics.setSystemApp(
                (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
		// 3. 初始化安裝確認界面
        startInstallConfirm();
    }

===>startInstallConfirm初始化並顯示安裝確認界面,就是我們平常安裝APK時出現的界面,界面上有確認和取消按鈕,並列出了安裝該APK需要訪問的系統權限,這個方法部分功能爲:

  1. 初始化界面相關代碼
  2. 界面列出安裝該APK需要訪問的系統權限
  3. 綁定監聽安裝按鈕的事件

代碼如下:

 private void startInstallConfirm() {
        // 1.初始化界面相關代碼
		...
		// 2.列出安裝該APK需要訪問的系統權限,並顯示
        AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
        final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
        if (mAppInfo != null) {
            msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                    ? R.string.install_confirm_question_update_system
                    : R.string.install_confirm_question_update;
            mScrollView = new CaffeinatedScrollView(this);
            mScrollView.setFillViewport(true);
            boolean newPermissionsFound = false;
            if (!supportsRuntimePermissions) {
                newPermissionsFound =
                        (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
                mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
                if (newPermissionsFound) {
                    permVisible = true;
                    mScrollView.addView(perms.getPermissionsView(
                            AppSecurityPermissions.WHICH_NEW));
                }
            }
            ....
        } else  {
            findViewById(R.id.tabscontainer).setVisibility(View.GONE);
            findViewById(R.id.divider).setVisibility(View.VISIBLE);
        }
        ....
        mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
       ....
        mInstallConfirm.setVisibility(View.VISIBLE);
        mOk = (Button)findViewById(R.id.ok_button); 
        mCancel = (Button)findViewById(R.id.cancel_button);
        mOk.setOnClickListener(this); // 3.綁定安裝按鈕事件
        mCancel.setOnClickListener(this);
        if (mScrollView == null) {
            // There is nothing to scroll view, so the ok button is immediately
            // set to install.
            mOk.setText(R.string.install);
            mOkCanInstall = true;
        } else {
            mScrollView.setFullScrollAction(new Runnable() {
                @Override
                public void run() {
                    mOk.setText(R.string.install);  // 安裝按鈕
                    mOkCanInstall = true;
                }
            });
        }
    }

簡單總結下PackageInstaller初始化:

  1. 分別對package協議file協議的Uri進行處理,得到mPkgInfo
  2. 對不同來源Apk進行處理(已知來源直接調用initiateInstall安裝,未知來源彈框提示)
  3. 如果允許安裝未知來源或者根據Intent判斷得出該APK不是未知來源,就會初始化安裝確認界面

2.2 PackageInstaller安裝APK

用戶點擊安裝確認界面的mOk 按鈕,就會觸發PackageInstallerActivityonClick方法,內部調用了startInstall方法,如下所示:

  public void onClick(View v) {
        if (v == mOk) {
            if (mOkCanInstall || mScrollView == null) {
                mInstallFlowAnalytics.setInstallButtonClicked();
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, true);
                    mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
                            PackageManager.INSTALL_SUCCEEDED);
                    finish();
                } else {
                    startInstall(); // 安裝函數
                }
            } else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        } else if(v == mCancel) {
           ...
        }
    }

===>startInstall 啓動了一個新的InstallAppProgress activity, 並把一些參數傳了過去,代碼如下:

 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, InstallAppProgress.class);
        //  省略傳入部分參數代碼
        ....
        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
        startActivity(newIntent);
        finish();
    }

===>InstallAppProgress 類的onCreate函數先取得傳入參數,再調用initView方法,代碼如下:

    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
       //  省略傳入部分參數代碼
		...
        initView();
    }

===>InstallAppProgress 類的initView方法,代碼如下:


    public void initView() {

        PackageManager pm = getPackageManager();
        try {
            PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName, 
                    PackageManager.GET_UNINSTALLED_PACKAGES);
            if(pi != null) {
                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
            }
        } catch (NameNotFoundException e) {
        }
        if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
            Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
        }
		....
        if ("package".equals(mPackageURI.getScheme())) { // package協議
            try {
                pm.installExistingPackage(mAppInfo.packageName);
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_SUCCEEDED);
            } catch (PackageManager.NameNotFoundException e) {
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_FAILED_INVALID_APK);
            }
        } else {
            pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                    installerPackageName, verificationParams, null);
        }
    }

===>精簡了一下,主要是這兩個方法:

  1. pm.installExistingPackage,實際上調用的就是ApplicationPackageManagerinstallExistingPackage,它是爲其他用戶安裝已安裝過的apk
  2. pm.installPackageWithVerificationAndEncryption,實際上調用的就是ApplicationPackageManagerinstallPackageWithVerificationAndEncryption,它是安裝一個apk

這裏我們只跟蹤ApplicationPackageManagerinstallPackageWithVerificationAndEncryption, 最終內部是調用了 mPM.installPackage, 代碼如下:

    public void installPackageWithVerificationAndEncryption(Uri packageURI,
            IPackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
                installerPackageName, verificationParams, encryptionParams);
    }
    --->installCommon方法的實現如下:
        private void installCommon(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        ...
        final String originPath = packageURI.getPath();
        try {
            mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                    verificationParams, null);
        } catch (RemoteException ignored) {
        }
    }

===> mPM的定義爲IPackageManager ,也就是IPackageManager.aidl,很明顯,它最終是調到了PMSinstallPackage方法。

 IPackageManager mPM

總結下PackageInstaller安裝APK,其實就是調到了PMSinstallPackage方法進行安裝。

3. PMS簡介

3.1 交互簡介

從源碼來分析:
ApplicationPackageManagerPackageManager的實現體,內部有成員變量IPackageManager mPM, ApplicationPackageManager是由ContextImpl.java初始化的,同時初始化的還有mPM變量

    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager(); // ApplicationPackageManager中的mPM
        if (pm != null) {
            // ApplicationPackageManager初始化
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }
        return null;
    }
    --->ApplicationPackageManager構造函數如下:
   ApplicationPackageManager(ContextImpl context,
                              IPackageManager pm) {
        mContext = context;
        mPM = pm;
    }
    ---> ActivityThread.getPackageManager如下:
        public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
    }

可以看出 mPM變量就是IPackageManager.Stub轉換的代理IPackageManagerProxy。參考 aidl自動生成類分析

public class PackageManagerService extends IPackageManager.Stub {
}

1.PackageManagerServiceIPackageManager在服務端的接口
2.ApplicationPackageManagermPM成員或者ActivityThread.getPackageManager,是IPackageManager在客戶端的代理接口,

3.2 PMS拷貝APK流程

PMSinstallPackage方法繼續查看源碼, 內部調用了installPackageAsUser方法

    public void installPackage(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, VerificationParams verificationParams,
            String packageAbiOverride) {
        installPackageAsUser(originPath, observer, installFlags, installerPackageName,
                verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
    }
    

===>installPackageAsUser方法,這個方法部分功能爲:

  1. 檢查調用者是否有安裝apk的權限
  2. 由於安裝程序是一個耗時的過程,所以使用了Handler消息傳遞機制,發送INIT_COPY消息

代碼如下:

    public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, VerificationParams verificationParams,
            String packageAbiOverride, int userId) {
        // 1.檢查調用者是否有安裝apk的權限
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
        ...

        UserHandle user;
        if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
            user = UserHandle.ALL;
        } else {
            user = new UserHandle(userId);
        }

        // Only system components can circumvent runtime permissions when installing.
        if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
                && mContext.checkCallingOrSelfPermission(Manifest.permission
                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
            throw new SecurityException("You need the "
                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
        }

        verificationParams.setInstallerUid(callingUid);

        final File originFile = new File(originPath);
        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);

        final Message msg = mHandler.obtainMessage(INIT_COPY);
        msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
                null, verificationParams, user, packageAbiOverride, null);
        // 2.由於安裝程序是一個耗時的過程,所以使用了Handler消息傳遞機制,發送INIT_COPY消息
        mHandler.sendMessage(msg);
    }
   ---->mHandler的定義:
   final PackageHandler mHandler;

處理INIT_COPY類型的消息,這個方法部分功能爲:

  1. connectToService用於標識是否綁定了DefaultContainerServiceDefaultContainerService是用於檢查和複製可移動文件的服務,這是一個比較耗時的操作,因此DefaultContainerService沒有和PMS運行在同一進程中,它運行在com.android.defcontainer進程,通過IMediaContainerServicePMS進行IPC通信
  2. 發送MCS_BOUND類型的消息,觸發處理第一個安裝請求

代碼如下所示:

        void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    int idx = mPendingInstalls.size();
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                    // mBound用於標識是否綁定了服務,默認值爲false
                    if (!mBound) {
                         //如果沒有綁定服務,重新綁定,connectToService方法內部如果綁定成功會將mBound置爲true
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            params.serviceError();
                             //綁定服務失敗則return
                            return;
                        } else {
                            //綁定服務成功,將請求添加到ArrayList類型的mPendingInstalls中,等待處理
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                      	 //已經綁定服務
                        mPendingInstalls.add(idx, params); // 加入待安裝List
                        if (idx == 0) {
                            mHandler.sendEmptyMessage(MCS_BOUND);// 發送MCS_BOUND
                        }
                    }
                    break;

處理MCS_BOUND類型的消息, 如果待安裝mPendingInstalls不爲空,這個方法部分功能爲:

  1. startCopy開始複製APK
  2. 如果APK安裝成功,刪除本次安裝請求, mPendingInstalls.remove(0);
  3. 如果沒有安裝請求了,發送MCS_UNBIND解綁服務的請求
  4. 如果還有其他的安裝請求,接着發送MCS_BOUND消息繼續處理剩餘的安裝請求

代碼如下所示:

   case MCS_BOUND: {
                    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
                    if (msg.obj != null) {
                        mContainerService = (IMediaContainerService) msg.obj;
                    }
                    if (mContainerService == null) {
                        if (!mBound) {
                        	...
                            //  綁定失敗,清空安裝請求隊列
                            mPendingInstalls.clear();
                        } else {
                            Slog.w(TAG, "Waiting to connect to media container service");
                        }
                    } else if (mPendingInstalls.size() > 0) {// 待安裝List不爲空
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            if (params.startCopy()) { // 1.開始複製APK
                                // We are done...  look for more work or to
                                // Delete pending install
                                 //2.如果APK安裝成功,刪除本次安裝請求
                                if (mPendingInstalls.size() > 0) {
                                    mPendingInstalls.remove(0);
                                }
                                if (mPendingInstalls.size() == 0) {
                                    if (mBound) {
										// 3.如果沒有安裝請求了,發送MCS_UNBIND解綁服務的請求
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                        // Unbind after a little delay, to avoid
                                        // continual thrashing.
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                     // 4.如果還有其他的安裝請求,接着發送MCS_BOUND消息繼續處理剩餘的安裝請求    
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                        }
                    } else {
                        // Should never happen ideally.
                        Slog.w(TAG, "Empty queue");
                    }
                    break;
                }

INIT_COPY的發送消息,我們可以知道InstallParamsHandlerParams真正實現體,代碼如下:

final Message msg = mHandler.obtainMessage(INIT_COPY);
        msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
                null, verificationParams, user, packageAbiOverride, null);
--->InstallParams

前面關鍵函數是HandlerParamsstartCopy,也就是InstallParamsstartCopy,調用startCopy方法時會先自動加1,如果次數大於4次就放棄這個安裝請求,代碼如下:

  final boolean startCopy() {
            boolean res;
            try {
				 // 1.startCopy方法嘗試的次數,超過了4次,就放棄這個安裝請求
                if (++mRetries > MAX_RETRIES) {
              		...
                    return false;
                } else {
                    handleStartCopy(); // 2.調用handleStartCopy
                    res = true;
                }
            } catch (RemoteException e) {
                ...
            }
            handleReturnCode();
            return res;
        }

===>handleStartCopy方法在InstallParams中實現,這個方法部分功能爲:

  1. 確定APK的安裝位置。onSd:安裝到SD卡, onInt:內部存儲即Data分區
  2. 判斷安裝的位置
  3. 根據InstallParams創建InstallArgs對象, InstallArgs 是一個抽象類,定義了APK的安裝邏輯,比如複製和重命名APK等,它有3個子類,都被定義在PMS中,如下圖所示。
    private InstallArgs createInstallArgs(InstallParams params) {
        if (params.move != null) {
            return new MoveInstallArgs(params);
        } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
            return new AsecInstallArgs(params);
        } else {
            return new FileInstallArgs(params);
        }
    }

在這裏插入圖片描述
FileInstallArgs用於處理安裝到非ASEC的存儲空間的APK,也就是內部存儲空間(Data分區)
AsecInstallArgs用於處理安裝到ASEC中(mnt/asec)即SD卡中的APK
MoveInstallArgs用於處理已安裝APK的移動的邏輯。
對APK進行檢查後就會調用InstallArgscopyApk方法進行安裝。

代碼如下:

        public void handleStartCopy() throws RemoteException {
			...
 			// 1.確定APK的安裝位置。onSd:安裝到SD卡, onInt:內部存儲即Data分區  
            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;

            PackageInfoLite pkgLite = null;

            if (onInt && onSd) {
                // APK不能同時安裝在SD卡和Data分區
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else {
                // 獲取APK的少量的信息
                pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                        packageAbiOverride);
              ...
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
            	// 2.判斷安裝的位置
                int loc = pkgLite.recommendedInstallLocation;
                if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                    ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                   ...
                } else {
                    // Override with defaults if needed.
                    loc = installLocationPolicy(pkgLite);
                    ...
                }
            }
			//3.根據InstallParams創建InstallArgs對象
            final InstallArgs args = createInstallArgs(this);
            mArgs = args;

            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 /*
                 * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
                 * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
                 */
                int userIdentifier = getUser().getIdentifier();
                if (userIdentifier == UserHandle.USER_ALL
                        && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
                    userIdentifier = UserHandle.USER_OWNER;
                }

                /*
                 * Determine if we have any installed package verifiers. If we
                 * do, then we'll defer to them to verify the packages.
                 */
                final int requiredUid = mRequiredVerifierPackage == null ? -1
                        : getPackageUid(mRequiredVerifierPackage, userIdentifier);
                if (!origin.existing && requiredUid != -1
                        && isVerificationEnabled(userIdentifier, installFlags)) {

                    final ComponentName requiredVerifierComponent = matchComponentForVerifier(
                            mRequiredVerifierPackage, receivers);
                    if (ret == PackageManager.INSTALL_SUCCEEDED
                            && mRequiredVerifierPackage != null) {
 						...
 						// 一堆檢查...
                    }
                } else {
                    /*
                     * No package verification is enabled, so immediately start
                     * the remote call to initiate copy using temporary file.
                     */
                    ret = args.copyApk(mContainerService, true);
                }
            }
            mRet = ret;
        }

===>InstallArgscopyApk方法, 不同的InstallArgs子類會有着不同的處理,這裏以FileInstallArgs爲例,這個方法部分功能爲:

  1. 創建臨時文件存儲目錄,如/data/app/vmdl123456.tmp,其中123456是安裝的sessionId
  2. 通過IMediaContainerService跨進程調用DefaultContainerServicecopyPackage方法,這個方法會在DefaultContainerService所在的進程中將APK複製到臨時存儲目錄,比如/data/app/vmdl 123456.tmp/base.apk

代碼如下:

 int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            if (origin.staged) {
            // 已經安裝過了
                if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                codeFile = origin.file;
                resourceFile = origin.file;
                return PackageManager.INSTALL_SUCCEEDED;
            }

            try {
            // 1.創建臨時文件存儲目錄
                final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);
                codeFile = tempDir;
                resourceFile = tempDir;
            } catch (IOException e) {
                Slog.w(TAG, "Failed to create copy file: " + e);
                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            }
			...
            int ret = PackageManager.INSTALL_SUCCEEDED;
            // 2.通過IMediaContainerService跨進程調用DefaultContainerService的copyPackage方法
            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "Failed to copy package");
                return ret;
            }

            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);
            } catch (IOException e) {
                Slog.e(TAG, "Copying native libraries failed", e);
                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            } finally {
                IoUtils.closeQuietly(handle);
            }
            return ret;
        }

===>DefaultContainerServicecopyPackage,最終調用FileInputStream寫入,代碼流程如下:

        public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
            ...
                final File packageFile = new File(packagePath);
                pkg = PackageParser.parsePackageLite(packageFile, 0);
                return copyPackageInner(pkg, target);
          ...
        }
        ---->
   private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
            throws IOException, RemoteException {
        copyFile(pkg.baseCodePath, target, "base.apk");
        if (!ArrayUtils.isEmpty(pkg.splitNames)) {
            for (int i = 0; i < pkg.splitNames.length; i++) {
                copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
            }
        }

        return PackageManager.INSTALL_SUCCEEDED;
    }
  ---->
    private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
            throws IOException, RemoteException {
        Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
        InputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(sourcePath);
            out = new ParcelFileDescriptor.AutoCloseOutputStream(
                    target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
            Streams.copy(in, out);
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
        }
    }

3.3 PMS安裝APK流程

我們回到APK的複製調用鏈的頭部方法:HandlerParamsstartCopy方法,在最後會調用InstallParamshandleReturnCode,代碼如下所示:

        void handleReturnCode() {
            // If mArgs is null, then MCS couldn't be reached. When it
            // reconnects, it will try again to install. At that point, this
            // will succeed.
            if (mArgs != null) {
                processPendingInstall(mArgs, mRet);
            }
        }

===>processPendingInstall,這個方法部分功能爲:

  1. args.doPreInstall安裝前處理,檢查APK的狀態,在安裝前確保安裝環境的可靠,如果不可靠會清除複製的APK文件
  2. installPackageLI安裝流程
  3. args.doPostInstall處理安裝後的收尾操作,如果安裝不成功,刪除掉安裝相關的目錄與文件

代碼如下:

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        // Queue up an async operation since the package installation may take a little while.
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
                 // Result object to be returned
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.returnCode = currentStatus;
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = new PackageRemovedInfo();
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                	//  安裝前處理
                    args.doPreInstall(res.returnCode);
                    synchronized (mInstallLock) {
                        installPackageLI(args, res);// 安裝
                    }
                    // 安裝後收尾
                    args.doPostInstall(res.returnCode, res.uid);
                }

                ....
        });
    }

===>installPackageLI安裝流程,這個方法部分功能爲:

  1. 創建PackageParser解析APK
  2. 檢查APK是否存在,標誌位replace置爲true表示是替換安裝
  3. 如果Settings中保存有要安裝的APK的信息,說明此前安裝過該APK,則需要校驗APK的簽名信息,確保安全的進行替換
  4. 遍歷每個權限,對權限進行處理
  5. 將臨時文件重新命名,比如前面提到的/data/app/vmdl123456.tmp/base.apk,重命名爲/data/app/包名-1/base.apk。這個新命名的包名會帶上一個數字後綴1,每次升級一個已有的App,這個數字會不斷的累加,參考FileInstallArgsgetNextCodePath
  6. 替換安裝或安裝新的APK
  7. 更新應用程序所屬的用戶

代碼如下:

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
         ...
        PackageParser pp = new PackageParser();//1
        final PackageParser.Package pkg;
        try {
            // 1.創建PackageParser解析APK
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        }
		...
        // Get rid of all references to package scan path via parser.
        pp = null;
        String oldCodePath = null;
        boolean systemApp = false;
        synchronized (mPackages) {
            // 2. 檢查APK的package是已存在,替換已存在的,也就是更新模式
            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                String oldName = mSettings.mRenamedPackages.get(pkgName);
                if (pkg.mOriginalPackages != null
                        && pkg.mOriginalPackages.contains(oldName)
                        && mPackages.containsKey(oldName)) {
                    // This package is derived from an original package,
                    // and this device has been updating from that original
                    // name.  We must continue using the original name, so
                    // rename the new package here.
                    pkg.setPackageName(oldName);
                    pkgName = pkg.packageName;
                    replace = true;//設置標誌位表示是替換安裝
               
                }  
                ...
            }

            PackageSetting ps = mSettings.mPackages.get(pkgName);
            //3.查看Settings中是否存有要安裝的APK的信息,如果有就獲取簽名信息
            if (ps != null) {
                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);

                //檢查簽名的正確性
                if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
                    if (!checkUpgradeKeySetLP(ps, pkg)) {
                        res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                + pkg.packageName + " upgrade keys do not match the "
                                + "previously installed version");
                        return;
                    }
                }
                ...
            }

            // Check whether the newly-scanned package wants to define an already-defined perm
            // 4.遍歷每個權限,對權限進行處理
            int N = pkg.permissions.size();
            for (int i = N-1; i >= 0; i--) {
                PackageParser.Permission perm = pkg.permissions.get(i);
                BasePermission bp = mSettings.mPermissions.get(perm.info.name);
				...
            }

        }

        if (systemApp && onExternal) {
           // 系統APP不能在SD卡上替換安裝
            res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                    "Cannot install updates to system apps on sdcard");
            return;
        }
        ...
       // 5.重命名臨時文件
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }

        startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);

        if (replace) {
        	 //6.1 替換安裝   
            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                    installerPackageName, volumeUuid, res);
        } else {
        	// 6.2 安裝新的APK
            installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                    args.user, installerPackageName, volumeUuid, res);
        }
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
            	// 7.更新應用程序所屬的用戶
                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
            }
        }
    }

===>這裏我們以新安裝APK爲例,會調用PMSinstallNewPackageLIF方法,這個方法部分功能爲:

  1. 掃描APK,將APK的信息存儲在PackageParser.Package類型的newPackage中,一個Package的信息包含了1個base APK以及0個或者多個split APK
  2. 更新Settings信息,Settings用於保存所有包的動態設置。
  3. 安裝失敗則刪除APK

代碼如下:

 private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
            UserHandle user, String installerPackageName, String volumeUuid,
            PackageInstalledInfo res) {
		...
        try {
        //1. 掃描APK
            PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
                    System.currentTimeMillis(), user);
			 // 2.更新Settings信息
            updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
            // delete the partially installed application. the data directory will have to be
            // restored if it was already existing
            if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                //3.安裝失敗則刪除APK
                deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
                        dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
                                res.removedInfo, true);
            }

        } catch (PackageManagerException e) {
            res.setError("Package couldn't be installed in " + pkg.codePath, e);
        }
    }

===>updateSettingsLI中設置APK安裝成功,代碼如下:

    private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
            String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
            UserHandle user) {
        String pkgName = newPackage.packageName;
        synchronized (mPackages) {
            
            mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
            mSettings.writeLPr();
        }

        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);

        synchronized (mPackages) {
            updatePermissionsLPw(newPackage.packageName, newPackage,
                    UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
                            ? UPDATE_PERMISSIONS_ALL : 0));
            // For system-bundled packages, we assume that installing an upgraded version
            // of the package implies that the user actually wants to run that new code,
            // so we enable the package.
            PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                if (isSystemApp(newPackage)) {
                    // NB: implicit assumption that system package upgrades apply to all users
                    if (DEBUG_INSTALL) {
                        Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
                    }
                    if (res.origUsers != null) {
                        for (int userHandle : res.origUsers) {
                            ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
                                    userHandle, installerPackageName);
                        }
                    }
                    // Also convey the prior install/uninstall state
                    if (allUsers != null && perUserInstalled != null) {
                        for (int i = 0; i < allUsers.length; i++) {
                            if (DEBUG_INSTALL) {
                                Slog.d(TAG, "    user " + allUsers[i]
                                        + " => " + perUserInstalled[i]);
                            }
                            ps.setInstalled(perUserInstalled[i], allUsers[i]);
                        }
                        // these install state changes will be persisted in the
                        // upcoming call to mSettings.writeLPr().
                    }
                }
                // It's implied that when a user requests installation, they want the app to be
                // installed and enabled.
                int userId = user.getIdentifier();
                if (userId != UserHandle.USER_ALL) {
                    ps.setInstalled(true, userId);
                    ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
                }
            }
            res.name = pkgName;
            res.uid = newPackage.applicationInfo.uid;
            res.pkg = newPackage;
            // 安裝完成狀態
            mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
            mSettings.setInstallerPackageName(pkgName, installerPackageName);
            res.returnCode = PackageManager.INSTALL_SUCCEEDED;
            //to update install status
            mSettings.writeLPr();
        }

3.4 PMS創建流程

它和 AMS的創建是類似的,啓動代碼位於frameworks/base/services/java/com/android/server/SystemServer.java中,代碼如下:

//  zygote的main入口
 public static void main(String[] args) {
   new SystemServer().run();
 }

===> SystemServer().run的部分代碼:

 private void run() {
 // Initialize the system context.
 	createSystemContext();
	// Create the system service manager.
	mSystemServiceManager = new SystemServiceManager(mSystemContext);
	LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);

	// Start services.
	try {
 		 traceBeginAndSlog("StartServices");
        //啓動引導服務
        startBootstrapServices();//3
        //啓動核心服務
        startCoreServices();//4
        //啓動其他服務
        startOtherServices();//5
	}
 }

SystemServiceManager啓動了ActivityManagerServicePowerManagerServicePackageManagerService等服務。
startCoreServices方法中則啓動了DropBoxManagerServiceBatteryServiceUsageStatsServiceWebViewUpdateServic等服務。
startOtherServices方法中啓動了CameraServiceAlarmManagerServiceVrManagerService等服務。

這些服務的父類均爲SystemService

可以看出,官方把系統服務分爲了三種類型,分別是引導服務、核心服務和其他服務,PMS屬於引導服務

===>創建PMS的過程位於startBootstrapServices中,使用了PackageManagerService.main方法,代碼如下:

private void startBootstrapServices() {
		...
		// 創建PMS
        Slog.i(TAG, "Package Manager");
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();

===>PackageManagerService.main方法 ,這個方法部分功能爲:

  1. 創建PMS對象
  2. 將PMS註冊到ServiceManager中
    public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        // 1.創建PMS對象
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        // 2.將PMS註冊到ServiceManager中
        ServiceManager.addService("package", m);
        return m;
    }

==>接着看PMS的構造過程,PMS的構造方法大概有600多行,分爲5個階段,每個階段會打印出相應的EventLog,EventLog用於打印Android系統的事件日誌。

  1. BOOT_PROGRESS_PMS_START(開始階段)
  2. BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(掃描系統階段)
  3. BOOT_PROGRESS_PMS_DATA_SCAN_START(掃描Data分區階段)
  4. BOOT_PROGRESS_PMS_SCAN_END(掃描結束階段)
  5. BOOT_PROGRESS_PMS_READY(準備階段)

代碼如下:

3.4.1 開始階段

    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;
        mFactoryTest = factoryTest;
        mOnlyCore = onlyCore;
        mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
        // 用於存儲屏幕的相關信息
        mMetrics = new DisplayMetrics();
        // Settings用於保存所有包的動態設置
        mSettings = new Settings(mPackages);
        // 在Settings中添加多個默認的sharedUserId//1
        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);

		...

        mInstaller = installer;
        // 創建Dex優化工具類
        mPackageDexOptimizer = new PackageDexOptimizer(this);
        mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());

        mOnPermissionChangeListeners = new OnPermissionChangeListeners(
                FgThread.get().getLooper());

        getDefaultDisplayMetrics(context, mMetrics);
		// 得到全局系統配置信息
        SystemConfig systemConfig = SystemConfig.getInstance();
        // 獲取全局的groupId 
        mGlobalGids = systemConfig.getGlobalGids();
        // 獲取系統權限
        mSystemPermissions = systemConfig.getSystemPermissions();
        mAvailableFeatures = systemConfig.getAvailableFeatures();

        synchronized (mInstallLock) {
         // 安裝APK時需要的鎖,保護所有對installd的訪問。
        synchronized (mPackages) {//2
            // 創建後臺線程ServiceThread
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
             // 創建PackageHandler綁定到ServiceThread的消息隊列
            mHandler = new PackageHandler(mHandlerThread.getLooper());//3
             // 將PackageHandler添加到Watchdog的檢測集中
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);//4
			// 在Data分區創建一些目錄
            File dataDir = Environment.getDataDirectory();//5
            mAppDataDir = new File(dataDir, "data");
            mAppInstallDir = new File(dataDir, "app");
            mAppLib32InstallDir = new File(dataDir, "app-lib");
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mUserAppDataDir = new File(dataDir, "user");
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
			// 創建多用戶管理服務
            sUserManager = new UserManagerService(context, this,
                    mInstallLock, mPackages);

在開始階段中創建了很多PMS中的關鍵對象並賦值給PMS中的成員變量,下面簡單介紹這些成員變量。

mSettings :用於保存所有包的動態設置。註釋1處將系統進程的sharedUserId添加到Settings中,sharedUserId用於進程間共享數據,比如兩個App的之間的數據是不共享的,如果它們有了共同的sharedUserId,就可以運行在同一個進程中共享數據。

mInstallerInstaller繼承自SystemService,和PMSAMS一樣是系統的服務(雖然名稱不像是服務),PMS很多的操作都是由Installer來完成的,比如APK的安裝和卸載。在Installer內部,通過IInstalldinstalld進行Binder通信,由位於nativie層的installd來完成具體的操作。

systemConfig:用於得到全局系統配置信息。比如系統的權限就可以通過SystemConfig來獲取。

mPackageDexOptimizer : Dex優化的工具類。

mHandlerPackageHandler類型) :PackageHandler繼承自Handler,在註釋3處它綁定了後臺線程ServiceThread的消息隊列。PMS通過PackageHandler驅動APK的複製和安裝工作,參考前面的APK拷貝。
PackageHandler處理的消息隊列如果過於繁忙,有可能導致系統卡住, 因此在註釋4處將它添加到Watchdog的監測集中。
Watchdog主要有兩個用途,一個是定時檢測系統關鍵服務(AMSWMS等)是否可能發生死鎖,還有一個是定時檢測線程的消息隊列是否長時間處於工作狀態(可能阻塞等待了很長時間)。如果出現上述問題,Watchdog會將日誌保存起來,必要時還會殺掉自己所在的進程,也就是SystemServer進程。

sUserManagerUserManagerService類型) :多用戶管理服務。

3.4.2 掃描系統階段

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
			...
			// 打印掃描系統階段日誌
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);

			// 在/system中創建framework目錄
            File frameworkDir = new File(Environment.getRootDirectory(), "framework");

            // Gross hack for now: we know this file doesn't contain any
            // code, so don't dexopt it to avoid the resulting log spew.
            alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");

			 //掃描/vendor/overlay目錄下的文件
            File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
            scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

            //掃描/system/framework 目錄下的文件
            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED,
                    scanFlags | SCAN_NO_DEX, 0);

           //掃描 /system/priv-app 目錄下的文件
            final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

           //掃描/system/app 目錄下的文件
            final File systemAppDir = new File(Environment.getRootDirectory(), "app");
            scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

            // Collect all vendor packages.
            File vendorAppDir = new File("/vendor/app");
            try {
                vendorAppDir = vendorAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
             //掃描 /vendor/app 目錄下的文件
            scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

           //掃描/oem/app 目錄下的文件
            final File oemAppDir = new File(Environment.getOemDirectory(), "app");
            scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

            if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
            mInstaller.moveFiles();

              //這個列表代表有可能有升級包的系統App
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
            if (!mOnlyCore) {
                Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                while (psit.hasNext()) {
                    PackageSetting ps = psit.next();

                    /*
                     * If this is not a system app, it can't be a
                     * disable system app.
                     */
                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                        continue;
                    }

                  //這裏的mPackages的是PMS的成員變量,代表scanDirTracedLI方法掃描上面那些目錄得到的 
                    final PackageParser.Package scannedPkg = mPackages.get(ps.name);
                    if (scannedPkg != null) {
                        if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
                            //將這個系統App的PackageSetting從PMS的mPackages中移除
                            removePackageLI(ps, true);
                              //將升級包的路徑添加到mExpectingBetter列表中
                            mExpectingBetter.put(ps.name, ps.codePath);
                        }

                        continue;
                    }

                    if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                        psit.remove();
                        logCriticalInfo(Log.WARN, "System package " + ps.name
                                + " no longer exists; wiping its data");
                        removeDataDirsLI(null, ps.name);
                    } else {
                        final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
                        //這個系統App升級包信息在mDisabledSysPackages中,但是沒有發現這個升級包存在
                        if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
                            possiblyDeletedUpdatedSystemApps.add(ps.name);
                        }
                    }
                }
            }
            ...

/system可以稱作爲System分區,裏面主要存儲谷歌和其他廠商提供的Android系統相關文件和框架。Android系統架構分爲應用層、應用框架層、系統運行庫層(Native 層)、硬件抽象層(HAL層)和Linux內核層,除了Linux內核層在Boot分區,其他層的代碼都在System分區。下面列出 System分區的部分子目錄。

目錄 含義
system/app 存放系統App,包括了谷歌內置的App也有廠商或者運營商提供的App
system/framework 存放應用框架層的jar包
system/priv-app 存放特權App
system/lib 存放so文件
system/fonts 存放系統字體文件
system/media 存放系統的各種聲音,比如鈴聲、提示音,以及系統啓動播放的動畫

系統掃描階段的主要工作有以下3點:

  1. 創建/system的子目錄,比如/system/framework、/system/priv-app和/system/app等等
  2. 掃描系統文件,比如/system/framework、/system/app等等目錄下的文件
  3. 對掃描到的系統文件做後續處理

3.4.3 掃描Data分區階段

   // 打印掃描Data分區階段日誌
   EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                // 掃描/data/app目錄下的文件      
                scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
			    // 掃描/data/app-private目錄下的文件  
                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);
                // 掃描完Data分區後,處理possiblyDeletedUpdatedSystemApps列表
                for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
                    PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
                    // 從mSettings.mDisabledSysPackages變量中移除去此應用
                    mSettings.removeDisabledSystemPackageLPw(deletedAppName);

                    String msg;
                     //1:如果這個系統App的包信息不在PMS的變量mPackages中,說明是殘留的App信息,後續會刪除它的數據。
                    if (deletedPkg == null) {
                        msg = "Updated system package " + deletedAppName
                                + " no longer exists; wiping its data";
                        removeDataDirsLI(null, deletedAppName);
                    } else {
                    	//2:如果這個系統App在mPackages中,說明是存在於Data分區,不屬於系統App,那麼移除其系統權限。
                        msg = "Updated system app + " + deletedAppName
                                + " no longer present; removing system privileges for "
                                + deletedAppName;

                        deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;

                        PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                        deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
                    }
                    logCriticalInfo(Log.WARN, msg);
                }

                 //遍歷mExpectingBetter列表
                for (int i = 0; i < mExpectingBetter.size(); i++) {
                    final String packageName = mExpectingBetter.keyAt(i);
                    if (!mPackages.containsKey(packageName)) {
                        //得到系統App的升級包路徑
                        final File scanFile = mExpectingBetter.valueAt(i);

                        // 3:根據系統App所在的目錄設置掃描的解析參數
                        final int reparseFlags;
                        if (FileUtils.contains(privilegedAppDir, scanFile)) {
                            reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                    | PackageParser.PARSE_IS_SYSTEM_DIR
                                    | PackageParser.PARSE_IS_PRIVILEGED;
                        } else if (FileUtils.contains(systemAppDir, scanFile)) {
                            reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                    | PackageParser.PARSE_IS_SYSTEM_DIR;
                        } else if (FileUtils.contains(vendorAppDir, scanFile)) {
                            reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                    | PackageParser.PARSE_IS_SYSTEM_DIR;
                        } else if (FileUtils.contains(oemAppDir, scanFile)) {
                            reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                    | PackageParser.PARSE_IS_SYSTEM_DIR;
                        } else {
                            Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
                            continue;
                        }
						 //將packageName對應的包設置數據(PackageSetting)添加到mSettings的mPackages中
                        mSettings.enableSystemPackageLPw(packageName);

                        try {
                           //掃描系統App的升級包
                            scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
                        } catch (PackageManagerException e) {
                            Slog.e(TAG, "Failed to parse original system package: "
                                    + e.getMessage());
                        }
                    }
                }
            }
            mExpectingBetter.clear();

/data可以稱爲Data分區,它用來存儲所有用戶的個人數據和配置文件。下面列出Data分區部分子目錄:

目錄 含義
data/app 存儲用戶自己安裝的App
data/data 存儲所有已安裝的App數據的目錄,每個App都有自己單獨的子目錄
data/app-private App的私有存儲空間
data/app-lib 存儲所有App的Jni庫
data/system 存放系統配置文件
data/anr 用於存儲ANR發生時系統生成的traces.txt文件

3.4.4 掃描結束階段

//  打印掃描結束階段日誌
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            Slog.i(TAG, "Time to scan packages: "
                    + ((SystemClock.uptimeMillis()-startTime)/1000f)
                    + " seconds");

 // 如果當前平臺SDK版本和上次啓動時的SDK版本不同,重新更新APK的授權
            int updateFlags = UPDATE_PERMISSIONS_ALL;
            if (ver.sdkVersion != mSdkVersion) {
                Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
                        + mSdkVersion + "; regranting permissions for internal storage");
                updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
            }
            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
            ver.sdkVersion = mSdkVersion;

           //如果是第一次啓動,需要初始化所有用戶定義的默認首選App
            if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) {
                for (UserInfo user : sUserManager.getUsers(true)) {
                    mSettings.applyDefaultPreferredAppsLPw(this, user.id);
                    applyFactoryDefaultBrowserLPw(user.id);
                    primeDomainVerificationsLPw(user.id);
                }
            }

             //OTA後的第一次啓動,會清除代碼緩存目錄。
            if (mIsUpgrade && !onlyCore) {
                Slog.i(TAG, "Build fingerprint changed; clearing code caches");
                for (int i = 0; i < mSettings.mPackages.size(); i++) {
                    final PackageSetting ps = mSettings.mPackages.valueAt(i);
                    if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
                        deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
                    }
                }
                ver.fingerprint = Build.FINGERPRINT;
            }

            checkDefaultBrowser();

            // clear only after permissions and other defaults have been updated
            mExistingSystemPackages.clear();
            mPromoteSystemApps = false;

            // All the changes are done during package scanning.
            ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;

            // 把Settings的內容保存到packages.xml中
            mSettings.writeLPr();

掃描結束結束階段主要做了以下幾件事:

  1. 如果當前平臺SDK版本和上次啓動時的SDK版本不同,重新更新APK的授權。
  2. 如果是第一次啓動,需要初始化所有用戶定義的默認首選App。
  3. OTA升級後的第一次啓動,會清除代碼緩存目錄。
  4. 把Settings的內容保存到packages.xml中,這樣此後PMS再次創建時會讀到此前保存的Settings的內容。

3.4.5 準備階段

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

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

            mInstallerService = new PackageInstallerService(context, this);//1

            mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
            mIntentFilterVerifier = new IntentVerifierProxy(mContext,
                    mIntentFilterVerifierComponent);
                     } // synchronized (mPackages)
        } // synchronized (mInstallLock)

        // Now after opening every single application zip, make sure they
        // are all flushed.  Not really needed, but keeps things nice and
        // tidy.
        Runtime.getRuntime().gc();

        // Expose private service for system components to use.
        LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());//2

註釋1處創建PackageInstallerServicePackageInstallerService是用於管理安裝會話的服務,它會爲每次安裝過程分配一個SessionId
註釋2處將PackageManagerInternalImplPackageManager的本地服務)添加到LocalServices中,LocalServices用於存儲運行在當前的進程中的本地服務。

3.5 PMS解析APK

3.5.1 PackageParser引入

Android世界中有很多包,比如應用程序的APK,Android運行環境的JAR包(比如framework.jar)和組成Android系統的各種動態庫so等等,由於包的種類和數量繁多,就需要進行包管理,但是包管理需要在內存中進行,而這些包都是以靜態文件的形式存在的,就需要一個工具類將這些包轉換爲內存中的數據結構,這個工具就是包解析器PackageParser

再看下 前面的installPackageLI代碼:

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
         ...
        PackageParser pp = new PackageParser();//1
        final PackageParser.Package pkg;
        try {
            // 1.創建PackageParser解析APK
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);//2
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        }

可以看到安裝APK時,需要先在註釋1處創建PackageParser,然後在註釋2處調用PackageParserparsePackage方法來解析APK。

3.5.2 PackageParser解析APK

Android5.0引入了Split APK機制,這是爲了解決65536上限以及APK安裝包越來越大等問題。Split APK機制可以將一個APK,拆分成多個獨立APK。
在引入了Split APK機制後,APK有兩種分類:

  1. Single APK:安裝文件爲一個完整的APK,即base APK。Android稱其爲Monolithic
  2. Mutiple APK:安裝文件在一個文件目錄中,其內部有多個被拆分的APK,這些APK由一個 base APK和一個或多個split APK組成。Android稱其爲Cluster

===>先看 pp.parsePackage方法的實現,結合前面的分類,可以知道,如果要解析的packageFile是一個目錄,說明是Mutiple APK,就需要調用parseClusterPackage方法來解析,如果是Single APK則調用parseMonolithicPackage方法來解析,代碼如下:

    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
        if (packageFile.isDirectory()) {
            return parseClusterPackage(packageFile, flags);
        } else {
            return parseMonolithicPackage(packageFile, flags);
        }
    }

===>以相對複雜的parseClusterPackage繼續分析,代碼如下:

    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);//1

        if (mOnlyCoreApps && !lite.coreApp) {//2
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + packageDir);
        }

        final AssetManager assets = new AssetManager();
        try {
           // 把base和所有split都加入assets
            loadApkIntoAssetManager(assets, lite.baseCodePath, flags);

            if (!ArrayUtils.isEmpty(lite.splitCodePaths)) {
                for (String path : lite.splitCodePaths) {
                    loadApkIntoAssetManager(assets, path, flags);
                }
            }

            final File baseApk = new File(lite.baseCodePath);
            final Package pkg = parseBaseApk(baseApk, assets, flags);//3
            if (pkg == null) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                        "Failed to parse base APK: " + baseApk);
            }

            if (!ArrayUtils.isEmpty(lite.splitNames)) {
                final int num = lite.splitNames.length;//4
                pkg.splitNames = lite.splitNames;
                pkg.splitCodePaths = lite.splitCodePaths;
                pkg.splitRevisionCodes = lite.splitRevisionCodes;
                pkg.splitFlags = new int[num];
                pkg.splitPrivateFlags = new int[num];

                for (int i = 0; i < num; i++) {
                    parseSplitApk(pkg, i, assets, flags);//5
                }
            }

            pkg.codePath = packageDir.getAbsolutePath();
            return pkg;
        } finally {
            IoUtils.closeQuietly(assets);
        }
    }

註釋1處調用parseClusterPackageLite方法用於輕量級解析目錄文件,之所以要輕量級解析是因爲解析APK是一個複雜耗時的操作,這裏的邏輯並不需要APK所有的信息。parseClusterPackageLite方法內部會通過parseApkLite方法解析每個Mutiple APK,得到每個Mutiple APK對應的ApkLite(輕量級APK信息),然後再將這些ApkLite封裝爲一個PackageLite(輕量級包信息)並返回。

註釋2處,mOnlyCoreApps用來指示PackageParser是否只解析“核心”應用,“核心”應用指的是AndroidManifest中屬性coreApp值爲true,只解析“核心”應用是爲了創建一個極簡的啓動環境。mOnlyCoreApps在創建PMS時就一路傳遞過來,如果我們加密了設備,mOnlyCoreApps值就爲true

註釋3處的parseBaseApk方法用於解析base APK,註釋4處獲取split APK的數量,根據這個數量在註釋5處遍歷調用parseSplitApk來解析每個split APK

===>先來看下parseClusterPackageLite方法,它的調用流程如下:

    private static PackageLite parseClusterPackageLite(File packageDir, int flags)
            throws PackageParserException {
      	....
        final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
        for (File file : files) {
            if (isApkFile(file)) {
                final ApkLite lite = parseApkLite(file, flags);
                ...
    }
    --->ApkLite 的實現:
        public static class ApkLite {
        public final String codePath;
        public final String packageName;
        public final String splitName;
        public final int versionCode;
        public final int revisionCode;
        public final int installLocation;
        public final VerifierInfo[] verifiers;
        public final Signature[] signatures;
        public final boolean coreApp;
        public final boolean multiArch;
        public final boolean extractNativeLibs;

===>再來看下parseBaseApk方法,它的調用流程如下:

    private Package parseBaseApk(File apkFile, AssetManager assets, 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);//1
        }

        mParseError = PackageManager.INSTALL_SUCCEEDED;
        mArchiveSourcePath = apkFile.getAbsolutePath();

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

        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);

        Resources res = null;
        XmlResourceParser parser = null;
        try {
            res = new Resources(assets, mMetrics, null);
            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    Build.VERSION.RESOURCES_SDK_INT);
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            final String[] outError = new String[1];
            final Package pkg = parseBaseApk(res, parser, flags, outError);//2
            if (pkg == null) {
                throw new PackageParserException(mParseError,
                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
            }

            pkg.volumeUuid = volumeUuid; // 3 
            pkg.applicationInfo.volumeUuid = volumeUuid; // 4
            pkg.baseCodePath = apkPath;
            pkg.mSignatures = null;

            return pkg;

        } catch (PackageParserException e) {
            throw e;
        } catch (Exception e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to read manifest from " + apkPath, e);
        } finally {
            IoUtils.closeQuietly(parser);
        }
    }
---->

註釋1處,如果APK的路徑以/mnt/expand/開頭,就截取該路徑獲取volumeUuid
註釋3處用於以後標識這個解析後的Package
註釋4處的用於標識該App所在的存儲卷UUID
註釋2處又調用了parseBaseApk的重載方法,可以看出當前的parseBaseApk方法主要是爲了獲取和設置volumeUuid

===>繼續看下parseBaseApk重載方法,它的調用流程如下:

  1. 創建Package對象
  2. 從資源中提取自定義屬性集com.android.internal.R.styleable.AndroidManifest得到TypedArray ,並賦值給pkg
  3. 讀取APK的AndroidManifest中的coreApp的值
  4. 用來解析APK的AndroidManifest中的各個標籤,比如applicationpermissionuses-sdkfeature-group

解析application標籤的方法爲parseBaseApplication,有近500行代碼,而Application只是AndroidManifest衆多標籤的一個,這讓我們更加理解了爲什麼此前解析APK時要使用輕量級解析了,parseBaseApk方法主要的解析結構可以理解爲以下簡圖。
在這裏插入圖片描述
代碼如下:

  private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
         ...
     	// 1.創建Package對象
        final Package pkg = new Package(pkgName);
        boolean foundApp = false;
		// 2.從資源中提取自定義屬性集com.android.internal.R.styleable.AndroidManifest得到TypedArray 
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifest);
        pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
        pkg.baseRevisionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
       
        ...
        // 3.讀取APK的AndroidManifest中的coreApp的值
        pkg.coreApp = attrs.getAttributeBooleanValue(null, "coreApp", false);
		 //獲取資源後要回收
        sa.recycle();

        /* Set the global "forward lock" flag */
        if ((flags & PARSE_FORWARD_LOCK) != 0) {
            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
        }

        /* Set the global "on SD card" flag */
        if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
        }

        // Resource boolean are -1, so 1 means we don't know the value.
        int supportsSmallScreens = 1;
        int supportsNormalScreens = 1;
        int supportsLargeScreens = 1;
        int supportsXLargeScreens = 1;
        int resizeable = 1;
        int anyDensity = 1;
        
        int outerDepth = parser.getDepth();
        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")) {
                 ...

                foundApp = true;
                if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) { // 3
                    return null;
                }
            } else if (tagName.equals("overlay")) {
                pkg.mTrustedOverlay = trustedOverlay;

                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                pkg.mOverlayTarget = sa.getString(
                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
                pkg.mOverlayPriority = sa.getInt(
                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
                        -1);
                sa.recycle();

                if (pkg.mOverlayTarget == null) {
                    outError[0] = "<overlay> does not specify a target package";
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                }
                if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
                    outError[0] = "<overlay> priority must be between 0 and 9999";
                    mParseError =
                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                }
                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("key-sets")) {
              // 4.各種解析tagname....
              .....
		
        }

        if (!foundApp && pkg.instrumentation.size() == 0) {
            outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
        }

        final int NP = PackageParser.NEW_PERMISSIONS.length;
        StringBuilder implicitPerms = null;
        for (int ip=0; ip<NP; ip++) {
            final PackageParser.NewPermissionInfo npi
                    = PackageParser.NEW_PERMISSIONS[ip];
            if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
                break;
            }
            if (!pkg.requestedPermissions.contains(npi.name)) {
                if (implicitPerms == null) {
                    implicitPerms = new StringBuilder(128);
                    implicitPerms.append(pkg.packageName);
                    implicitPerms.append(": compat added ");
                } else {
                    implicitPerms.append(' ');
                }
                implicitPerms.append(npi.name);
                pkg.requestedPermissions.add(npi.name);
            }
        }
        if (implicitPerms != null) {
            Slog.i(TAG, implicitPerms.toString());
        }

        final int NS = PackageParser.SPLIT_PERMISSIONS.length;
        for (int is=0; is<NS; is++) {
            final PackageParser.SplitPermissionInfo spi
                    = PackageParser.SPLIT_PERMISSIONS[is];
            if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
                    || !pkg.requestedPermissions.contains(spi.rootPerm)) {
                continue;
            }
            for (int in=0; in<spi.newPerms.length; in++) {
                final String perm = spi.newPerms[in];
                if (!pkg.requestedPermissions.contains(perm)) {
                    pkg.requestedPermissions.add(perm);
                }
            }
        }

        if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
        }
        if (supportsNormalScreens != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
        }
        if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
        }
        if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
        }
        if (resizeable < 0 || (resizeable > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
        }
        if (anyDensity < 0 || (anyDensity > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
        }

        return pkg;
    }

包被解析後,最終在內存是PackagePackagePackageParser的內部類,它的部分成員變量如下所示。

    /**
     * Representation of a full package parsed from APK files on disk. A package
     * consists of a single base APK, and zero or more split APKs.
     */
    public final static class Package {

        public String packageName; // 包名

        /** Names of any split APKs, ordered by parsed splitName */
        public String[] splitNames;

        // TODO: work towards making these paths invariant

        public String volumeUuid;

        /**
         * Path where this package was found on disk. For monolithic packages
         * this is path to single base APK file; for cluster packages this is
         * path to the cluster directory.
         */
        public String codePath;

        /** Path of base APK */
        public String baseCodePath;
        /** Paths of any split APKs, ordered by parsed splitName */
        public String[] splitCodePaths;

        /** Revision code of base APK */
        public int baseRevisionCode;
        /** Revision codes of any split APKs, ordered by parsed splitName */
        public int[] splitRevisionCodes;

        /** Flags of any split APKs; ordered by parsed splitName */
        public int[] splitFlags;

        /**
         * Private flags of any split APKs; ordered by parsed splitName.
         *
         * {@hide}
         */
        public int[] splitPrivateFlags;

        public boolean baseHardwareAccelerated;

        // For now we only support one application per package.
        public final ApplicationInfo applicationInfo = new ApplicationInfo();

		//4 大組件
        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
        public final ArrayList<Service> services = new ArrayList<Service>(0);
        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);

        public final ArrayList<String> requestedPermissions = new ArrayList<String>();

        public ArrayList<String> protectedBroadcasts;

        public ArrayList<String> libraryNames = null;
        public ArrayList<String> usesLibraries = null;
        public ArrayList<String> usesOptionalLibraries = null;
        public String[] usesLibraryFiles = null;

        public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;

        public ArrayList<String> mOriginalPackages = null;
        public String mRealPackage = null;
        public ArrayList<String> mAdoptPermissions = null;
        
        // We store the application meta-data independently to avoid multiple unwanted references
        public Bundle mAppMetaData = null;

        // The version code declared for this package.
        public int mVersionCode;

        // The version name declared for this package.
        public String mVersionName;
        
        // The shared user id that this package wants to use.
        public String mSharedUserId;

        // The shared user label that this package wants to use.
        public int mSharedUserLabel;

        // Signatures that were read from the package.
        public Signature[] mSignatures;
        public Certificate[][] mCertificates;

        // For use by package manager service for quick lookup of
        // preferred up order.
        public int mPreferredOrder = 0;

        // For use by package manager to keep track of where it needs to do dexopt.
        public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4);

        // For use by package manager to keep track of when a package was last used.
        public long mLastPackageUsageTimeInMills;

        // // User set enabled state.
        // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
        //
        // // Whether the package has been stopped.
        // public boolean mSetStopped = false;

        // Additional data supplied by callers.
        public Object mExtras;

        // Applications hardware preferences
        public ArrayList<ConfigurationInfo> configPreferences = null;

        // Applications requested features
        public ArrayList<FeatureInfo> reqFeatures = null;

        // Applications requested feature groups
        public ArrayList<FeatureGroupInfo> featureGroups = null;

        public int installLocation;

        public boolean coreApp;

        /* An app that's required for all users and cannot be uninstalled for a user */
        public boolean mRequiredForAllUsers;

        /* The restricted account authenticator type that is used by this application */
        public String mRestrictedAccountType;

        /* The required account type without which this application will not function */
        public String mRequiredAccountType;

        /**
         * Digest suitable for comparing whether this package's manifest is the
         * same as another.
         */
        public ManifestDigest manifestDigest;

        public String mOverlayTarget;
        public int mOverlayPriority;
        public boolean mTrustedOverlay;

        /**
         * Data used to feed the KeySetManagerService
         */
        public ArraySet<PublicKey> mSigningKeys;
        public ArraySet<String> mUpgradeKeySets;
        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;

注意4大組件那裏,ActivityProviderService都是PackageParser的靜態內部類,例如Activity

  public final static class Activity extends Component<ActivityIntentInfo> {
        public final ActivityInfo info;

        public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
            super(args, _info);
            info = _info;
            info.applicationInfo = args.owner.applicationInfo;
        }
        
        public void setPackageName(String packageName) {
            super.setPackageName(packageName);
            info.packageName = packageName;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Activity{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

Package簡圖如下:
在這裏插入圖片描述
從這個簡圖中可以發現Package的數據結構是如何設計的:

  1. Package中存有許多組件,比如ActicityProviderPermission等等,它們都繼承基類Component
  2. 每個組件都包含一個info數據,比如Activity類中包含了成員變量ActivityInfo,這個ActivityInfo纔是真正的Activity數據。
  3. 四大組件的標籤內可能包含<intent-filter>來過濾Intent信息,因此需要IntentInfo來保存組件的intent信息,組件基類Component依賴於IntentInfo
    public static class Component<II extends IntentInfo> {
        public final Package owner;
        public final ArrayList<II> intents;
        public final String className;
        public Bundle metaData;

        ComponentName componentName;
        String componentShortName;
--->IntentInfo的代碼如下:
    public static class IntentInfo extends IntentFilter {
        public boolean hasDefault;
        public int labelRes;
        public CharSequence nonLocalizedLabel;
        public int icon;
        public int logo;
        public int banner;
        public int preferred;
    }
--->IntentFilter的代碼如下:
public class IntentFilter implements Parcelable {
...
  public static final String SCHEME_HTTPS = "https";

    private int mPriority;
    private final ArrayList<String> mActions;
    private ArrayList<String> mCategories = null;
    private ArrayList<String> mDataSchemes = null;
    private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
    private ArrayList<AuthorityEntry> mDataAuthorities = null;
    private ArrayList<PatternMatcher> mDataPaths = null;
    private ArrayList<String> mDataTypes = null;
    private boolean mHasPartialTypes = false;
}

IntentInfo有三個子類ActivityIntentInfoServiceIntentInfoProviderIntentInfo,不同組件依賴的IntentInfo會有所不同,如下:

public final class Activity extends Component<ActivityIntentInfo>
public final static class Service extends Component<ServiceIntentInfo> 
public final static class Provider extends Component<ProviderIntentInfo>

4.VirtualApp中PMS的實現

VirtualApp中解析後的數據都保存在PackageParser類中,由PackageParserExPackageParserEx擴展了PackageParser)解析,對外的數據是VPackage

4.1 PackageParser類簡介

VA自建的android.content.pm.PackageParse,它其實更多的意義是佔位類,讓反射方式更優雅,它和系統的android.content.pm.PackageParse保持一致,但實際上,系統解析得到的android.content.pm.PackageParse,還是系統那一份。
PackageParser類最底層數據仍是IntentInfo,如下:

    public static class IntentInfo extends IntentFilter { 
        public boolean hasDefault;
        public int labelRes;
        public CharSequence nonLocalizedLabel;
        public int icon;
        public int logo;
        public int banner;
    }

同樣有一個Component基類,如下:

    public static class Component<II extends IntentInfo> {
        public Package owner;
        public ArrayList<II> intents;
        public String className;
        public Bundle metaData;

        public ComponentName getComponentName() {
            return null;
        }
    }

getComponentName代替了 ComponentName componentName;
前面提到Package中存有許多組件,比如ActicityProviderPermission等等,它們都繼承基類Component,另外前面提到Package中每個組件都包含一個info數據,info數據纔是真正的數據,VA採用了同樣的構造方式,如下:

    public final static class Activity extends Component<ActivityIntentInfo> {
        public ActivityInfo info; 
    }
    
    public class ActivityIntentInfo extends IntentInfo {
        public Activity activity;
    }

對比系統的,完全一致:

    public final static class Activity extends Component<ActivityIntentInfo> {
        public final ActivityInfo info;
        ...
        }
public final static class ActivityIntentInfo extends IntentInfo {
        public final Activity activity;
        ...

對應Package類也 相對砍掉了不少變量,其中SigningDetailsAPI28才增加的新變量

    public class Package {
        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
        public final ArrayList<Service> services = new ArrayList<Service>(0);
        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
        public final ArrayList<String> requestedPermissions = new ArrayList<String>();
        public Signature[] mSignatures;
        public SigningDetails mSigningDetails; // API 28
        public Bundle mAppMetaData;
        public Object mExtras;
        public String packageName;
        public int mPreferredOrder;
        public String mSharedUserId;
        public ArrayList<String> usesLibraries;
        public int mVersionCode;
        public ApplicationInfo applicationInfo;
        public String mVersionName;

        // Applications hardware preferences
        public ArrayList<ConfigurationInfo> configPreferences = null;

        // Applications requested features
        public ArrayList<FeatureInfo> reqFeatures = null;
        public int mSharedUserLabel;
    }

PackageParse類沒有解析函數,保存一些仿系統的數據類的簡化版,對應的Parcelable實現爲VPackage
解析代碼位於PackageParserEx
PackageParse類位於android.content.pm,這和系統保持一致

4.2 VPackage類簡介

PackageParse類對應的Parcelable實現爲VPackage類,
比如PackageParse.IntentInfo對應VPackage.IntentInfo,VPackage.IntentInfo提供了相應的構造函數進行轉換

  public static class IntentInfo implements Parcelable {
  		...
  		public IntentInfo(PackageParser.IntentInfo info) {
            this.filter = info;
            this.hasDefault = info.hasDefault;
            this.labelRes = info.labelRes;
            if (info.nonLocalizedLabel != null) {
                this.nonLocalizedLabel = info.nonLocalizedLabel.toString();
            }
            this.icon = info.icon;
            this.logo = info.logo;
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
                this.banner = info.banner;
            }
        }
  }

同樣的,PackageParse.Component對應VPackage.ComponentVPackage.Component提供了相應的構造函數進行轉換

        public Component(PackageParser.Component component) {
            this.className = component.className;
            this.metaData = component.metaData;
        }

同理,一一對應,VPackage均提供了相應的構造函數進行轉換
PackageParse.ActivityIntentInfo對應VPackage.ActivityIntentInfo
PackageParse.ServiceIntentInfo對應VPackage.ServiceIntentInfo
PackageParse.ProviderIntentInfo對應VPackage.ProviderIntentInfo
PackageParse.Activity對應VPackage.ActivityComponent
PackageParse.Service對應VPackage.ServiceComponent
PackageParse.Provider對應VPackage.ProviderComponent
PackageParse.Instrumentation對應VPackage.InstrumentationComponent
PackageParse.Permission對應VPackage.PermissionComponent
PackageParse.PermissionGroup對應VPackage.PermissionGroupComponent

4.3 PackageParserEx類簡介

PackageParserEx繼承於PackageParse類,類似於系統的PackageParse類,它提供了parsePackage接口,這個函數第一行是:

   PackageParser parser = PackageParserCompat.createParser(packageFile);

VA自建的android.content.pm.PackageParse,它其實更多的意義是佔位符,讓反射方式更優雅,它和系統的android.content.pm.PackageParse保持一致,但實際上,系統解析得到的android.content.pm.PackageParse,所以這裏返回的還是系統的那一份PackageParser ,不要理解爲VA的那一份

PackageParserCompat是一個包裝類,內部通過區分APK版本號來反射系統的android.content.pm.PackageParse
parsePackage代碼如下:

public static VPackage parsePackage(File packageFile) throws Throwable { // 59
        PackageParser parser = PackageParserCompat.createParser(packageFile);
        PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0);
        ...
        return buildPackageCache(p);
    }
 -->PackageParserCompat.createParser代碼如下:
   public static PackageParser createParser(File packageFile) {
        if (API_LEVEL >= M) {
            return PackageParserMarshmallow.ctor.newInstance();
         ...

佔位類的好處是可以直接調用系統隱藏類的變量,不能再通過反射方式去獲取了, 如下圖:
在這裏插入圖片描述
buildPackageCache方法用於把將系統的Package.Package轉換成可以序列化的 VPackage 類型

4.4 VAMS中安裝包的詳細實現流程

VAMS中安裝包的函數是installPackageImpl,以下是全流程跟進:

假定我們的安裝包路徑是:/data/app/com.hgy413.refclass-xxx==/base.apk

===> 1.利用PackageParserEx.parsePackage(packageFile);base.apk解析成 VPackage pkg
內部仍是利用系統的android.content.pm.PackageParser解包成PackageParser.Package
雖然我們定義了一個PackageParser,但它的作用就是簡單的避免反射調用系統的android.content.pm.PackageParser

   PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0);

然後通過buildPackageCachePackageParser.Package轉成 VPackage pkg
最後通過addOwnerVPackage.ActivityComponent, VPackage.ServiceComponent等的owner設置爲 VPackage pkg自身。
如果是google service,還需要加上 flags |= ApplicationInfo.FLAG_PERSISTENT;

===> 2.判斷是否需要升級
PackageCacheManager類:
它的成員 ArrayMap<String, VPackage> PACKAGE_CACHE記錄了所有的 VPackagekey爲包名。
VPackagemExtras成員變量中又記錄了PackageSetting對象,所以PackageSetting對象和 VPackage就關聯起來了。

升級邏輯如下:
首先去PackageCacheManager類中查找是否已存在相同包名的 VPackage,不存在就繼續 。

如果存在,檢查傳入的InstallOptions.updateStrategy值,默認是COMPARE_VERSION,也就是比較新舊安裝包的versioncode,僅當新的>舊的才覆蓋安裝。

如果需要升級,就要調用VActivityManagerService.get().killAppByPkg把原來的進程kill掉,並從PACKAGE_CACHE中移除原來的VPackage。如果不需要升級,就直接返回升級失敗。

===> 3.初始化PackageSetting ps對象
如果PACKAGE_CACHE中已存在VPackage,就取VPackagemExtras成員變量,否則創建一個新的PackageSetting ps對象。

===> 4.複製abi下的so文件到virtual目錄

  1. NativeLibraryHelperCompat.getSupportAbiList通過zip解壓遍歷base.apk得到它內部有哪些abi,也就是base.apklib目錄下有哪些abi,如armeabi-v7ax86等。

  2. 如果abiarm64-v8ax86_64mips64中一個目錄,就認爲這個apk支持x64, 這種屬性通過PackageSetting ps.flag來記錄

if (support32bit) {
            if (support64bit) {
                ps.flag = PackageSetting.FLAG_RUN_BOTH_32BIT_64BIT;
            } else {
                ps.flag = PackageSetting.FLAG_RUN_32BIT;
            }
        } else {
            ps.flag = PackageSetting.FLAG_RUN_64BIT;
        }
  1. NativeLibraryHelperCompat.copyNativeBinaries執行復制abi操作,例如把/data/app/com.hgy413.refclass-xxx==/base.apk中的lib/x86/libv++.so拷貝到/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/lib/libv++.so,系統安裝apk時,系統會把abi解壓到data/app/com.hgy.ndk/lib, VA以類似的方式保存在它的virtual目錄中, 差別是它把x86這個abi目錄去掉了。

  2. 默認如果是系統安裝過的apk,則useSourceLocationApk=true, 這時使用外部的apk。
    但如果是未被安裝的apk安裝包,則useSourceLocationApk=false, 這時需要做apk拷貝。
    例如:把/data/app/com.hgy413.refclass-xxx==/base.apk拷貝到/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/base.apk,並把packageFile指向拷貝後的目錄。

===> 5.設置PackageSetting ps對象
設置以下信息到ps中,在前面還設置了ps.flag

 ps.appMode = useSourceLocationApk ? MODE_APP_USE_OUTSIDE_APK : MODE_APP_COPY_APK;
 ps.packageName = pkg.packageName;
 ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));
  if (res.isUpdate) {
            ps.lastUpdateTime = installTime;
        } else {
            ps.firstInstallTime = installTime;
            ps.lastUpdateTime = installTime;
            for (int userId : VUserManagerService.get().getUserIds()) {
                boolean installed = userId == 0;// 默認用戶 userId = 0時,installed爲true
                ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
            }
        }
  ---> ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
  public class PackageUserState implements Parcelable {
    public boolean launched;
    public boolean hidden;
    public boolean installed;
  }

VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));仿系統的UMS機制,默認appid10001開始,依次遞增。
ps.setUserState內部會針對每個用戶環璋生成一個PackageUserState,並記錄這個ps在每個用戶環境的安裝狀態。
默認用戶環境(userid=0)是launched = false, hidden = false, installed = true;, 其餘用戶環境是launched = false, hidden = false, installed = false;

簡單的說,PackageSetting記錄了安裝時間,更新時間,每個用戶環境的安裝狀態,包名,appid, 是否拷貝了apk, 是否支持x64,等信息。

===> 6.把VPackage pkg寫入package.inisignature.ini文件
PackageParserEx.savePackageCache(pkg);所做的操作:

  1. 先獲取virtual/data/app/packageName/package.ini,以及virtual/data/app/packageName/signature.ini
  2. Parcel方式把pkg寫入到package.ini中:(在loadPackageInnerLocked中會跨進程從package.ini讀取pkg)
            pkg.writeToParcel(p, 0);
            FileOutputStream fos = new FileOutputStream(cacheFile);
            fos.write(p.marshall());
            fos.close();
  1. Parcel方式把pkg.mSignatures寫入到signature.ini

===> 7.把VPackage pkgPackageSetting ps都存在PackageCacheManager

 PackageParserEx.initApplicationInfoBase(ps, pkg);
 PACKAGE_CACHE.put(pkg.packageName, pkg); // 存入`VPackage pkg`
 pkg.mExtras = ps; // 存入`PackageSetting ps`
 VPackageManagerService.get().analyzePackageLocked(pkg);

PackageParserEx.initApplicationInfoBase(ps, pkg);就是把pkg.ApplicationInfo根據ps做一些初始化,如uid,sharedLibraryFiles

===> 8. 接第7點的VPackageManagerService.get().analyzePackageLocked(pkg)繼續分析:
主要是VPMS保存VPackage pkg的一些信息

  1. 保存pkg的activitiesmActivities
    mActivities的類型是ActivityIntentResolver,它內部的addActivity函數如下:
 public final void addActivity(VPackage.ActivityComponent a, String type) {
            mActivities.put(a.getComponentName(), a);
            final int NI = a.intents.size();
            for (int j = 0; j < NI; j++) {
                VPackage.ActivityIntentInfo intent = a.intents.get(j);
                if (intent.filter.getPriority() > 0 && "activity".equals(type)) {
                    intent.filter.setPriority(0);
                    Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " + a.className
                            + " with priority > 0, forcing to 0");
                }
                addFilter(intent);
            }
        }

ComponentName類由packageNameclassName組成,例如:mClass = com.hgy.ndk.MainActivity, mPackage = com.hgy.ndk

所有的activity都被ActivityIntentResolver記錄到它的mActivities中,keyComponentName:
HashMap<ComponentName, VPackage.ActivityComponent> mActivities

所有的intent都被ActivityIntentResolver記錄到它的mFilters中:
private HashSet<F> mFilters = new HashSet<F>();

同時intent也會被解析出action之類的字符串加入到內部mActionToFilter, schemes字符串加入到內部mSchemeToFilter

以獲取action字符串爲例:

VPackage.ActivityComponent a
--->取第1個測試下:
VPackage.ActivityIntentInfo intent = a.intents.get(0);
--->
Iterator<String> i = f.filter.actionsIterator()
--->遍歷Iterator<String> i,就可以得到android.intent.action.MAIN這種字符串了,加入mActionToFilter:
register_intent_filter(f, f.filter.actionsIterator(), mActionToFilter, "      Action: ");

在這裏插入圖片描述

這部分代碼具體參考了系統的PackageManagerService.ActivityIntentResolver

  1. 保存pkg的servicesmServices
    這部分和1非常類似,mServices的類型是ServiceIntentResolver,它內部的addService函數如下:
        public final void addService(VPackage.ServiceComponent s) {
            mServices.put(s.getComponentName(), s);
            final int NI = s.intents.size();
            int j;
            for (j = 0; j < NI; j++) {
                VPackage.ServiceIntentInfo intent = s.intents.get(j);
                addFilter(intent);
            }
        }
  1. 保存pkg的receiversmReceivers
    mReceivers的類型同樣是ActivityIntentResolver,所以它和1唯一的區別是傳入的type是"receiver"
 mReceivers.addActivity(a, "receiver");
  1. 保存pkg的providersmProviders
        N = pkg.providers.size();
        for (int i = 0; i < N; i++) {
            VPackage.ProviderComponent p = pkg.providers.get(i);
            if (p.info.processName == null) {
                p.info.processName = p.info.packageName;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                mProviders.addProvider(p);
            }
            String names[] = p.info.authority.split(";");
            synchronized (mProvidersByAuthority) {
                for (String name : names) {
                    if (!mProvidersByAuthority.containsKey(name)) {
                        mProvidersByAuthority.put(name, p);
                    }
                }
            }
            mProvidersByComponent.put(p.getComponentName(), p);
        }

mProviders的類型是ProviderIntentResolvermProviders.addProvider(p);這部分和1非常類似。
另外它要取得authority保存到mProvidersByAuthority中,它也會保存到mProvidersByComponent中,這是爲了方便通過authorityComponentName快速查詢到 VPackage.ProviderComponent

  1. 保存pkg的permissionsmPermissionspermissionGroupsmPermissionGroups,例如:
<permission
        android:name="android.permission.SAFE_ACCESS"
        android:protectionLevel="signature" />

下面的permission.info.name對應就是android.permission.SAFE_ACCESS,代碼如下:

        N = pkg.permissions.size();
        for (int i = 0; i < N; i++) {
            VPackage.PermissionComponent permission = pkg.permissions.get(i);
            mPermissions.put(permission.info.name, permission);
        }
        N = pkg.permissionGroups.size();
        for (int i = 0; i < N; i++) {
            VPackage.PermissionGroupComponent group = pkg.permissionGroups.get(i);
            mPermissionGroups.put(group.className, group);
        }
  1. 保存pkg的requestedPermissions中的危險權限到mDangerousPermissions,例如:
<uses-permission android:name="android.permission.WRITE_SOCIAL_STREAM" />
 <uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />

這裏會通過PermissionCompat.findDangerousPermissions來確認請求的是否爲危險權限,如果是,則加入mDangerousPermissions中,危險權限包含了這些:

public static Set<String> DANGEROUS_PERMISSION = new HashSet<String>() {{
        // CALENDAR group
        add(Manifest.permission.READ_CALENDAR);
        add(Manifest.permission.WRITE_CALENDAR);

        // CAMERA
        add(Manifest.permission.CAMERA);

        // CONTACTS
        add(Manifest.permission.READ_CONTACTS);
        add(Manifest.permission.WRITE_CONTACTS);
        add(Manifest.permission.GET_ACCOUNTS);

        // LOCATION
        add(Manifest.permission.ACCESS_FINE_LOCATION);
        add(Manifest.permission.ACCESS_COARSE_LOCATION);

        // PHONE
        add(Manifest.permission.READ_PHONE_STATE);
        add(Manifest.permission.CALL_PHONE);
        if (Build.VERSION.SDK_INT >= 16) {
            add(Manifest.permission.READ_CALL_LOG);
            add(Manifest.permission.WRITE_CALL_LOG);
        }
        add(Manifest.permission.ADD_VOICEMAIL);
        add(Manifest.permission.USE_SIP);
        add(Manifest.permission.PROCESS_OUTGOING_CALLS);

        // SMS
        add(Manifest.permission.SEND_SMS);
        add(Manifest.permission.RECEIVE_SMS);
        add(Manifest.permission.READ_SMS);
        add(Manifest.permission.RECEIVE_WAP_PUSH);
        add(Manifest.permission.RECEIVE_MMS);

        add(Manifest.permission.RECORD_AUDIO);
        // STORAGE
        add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (Build.VERSION.SDK_INT >= 16) {
            add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }
        if (Build.VERSION.SDK_INT >= 20) {
            // SENSORS
            add(Manifest.permission.BODY_SENSORS);
        }
    }};

<uses-permission>是官方定義的權限,是調用別人的東西的時候自己需要聲明的權限,<permission>是自己定義的權限,就是別人調用這個程序時需要用<uses-permission>來聲明。

===> 9. 把PackageCacheManager的內容寫入到文件packages.ini
前面提到了PackageCacheManager類包含了所有的VPackagePackageSetting數據,所以這裏以Parcel寫文件的方式把PackageCacheManager類保存到virtual/data/app/system/packages.ini中。

後續會在VDeviceManagerService中讀取它們。

===> 10. 如果是支持32位又未被系統安裝的apk(即sd卡下的apk安裝包),嘗試把apk解析成dex,並動態加載dex。

 if (support32bit && !useSourceLocationApk) {
       DexOptimizer.optimizeDex(packageFile.getPath(), VEnvironment.getOdexFile(ps.packageName).getPath());
}

/data/app/com.hgy413.refclass-xxx==/base.apk爲例,主體邏輯如下:

  1. DexOptimizer.optimizeDex傳入參數分別爲/data/app/com.hgy413.refclass-xxx==/base.apk/data/data/io.busniess.va/virtual/opt/data@[email protected]@[email protected]

  2. 判斷是否爲art,並且在android5.0-androi7.1之間,如果是,則調用 DexOptimizer.interpretDex2OatinterpretDex2Oat這個函數內部是調用了系統的dex2oatcmd命令來生成oat的,它被廣泛使用,如微信的tinker。

  3. 調用DexFile.loadDex(dexFilePath, optFilePath, 0)

===> 11. 是否通知安裝完成。
默認在AppRepository.addVirtualApp中初始化InstallOptionsnotify=false

 public InstallResult addVirtualApp(AppInfoLite info) { // 174
        InstallOptions options = InstallOptions.makeOptions(info.notCopyApk, false, InstallOptions.UpdateStrategy.COMPARE_VERSION);

notify=true時,觸發通知邏輯,允許外部獲得安裝回調,代碼如下:

    private void notifyAppInstalled(PackageSetting setting, int userId) {
        final String pkg = setting.packageName;
        int N = mRemoteCallbackList.beginBroadcast();
        while (N-- > 0) {
            try {
                if (userId == -1) {
                    mRemoteCallbackList.getBroadcastItem(N).onPackageInstalled(pkg);
                    mRemoteCallbackList.getBroadcastItem(N).onPackageInstalledAsUser(0, pkg);

                } else {
                    mRemoteCallbackList.getBroadcastItem(N).onPackageInstalledAsUser(userId, pkg);
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        sendInstalledBroadcast(pkg, new VUserHandle(userId));
        mRemoteCallbackList.finishBroadcast();
        VAccountManagerService.get().refreshAuthenticatorCache(null);
    }
    --->
     RemoteCallbackList<IPackageObserver> mRemoteCallbackList = new RemoteCallbackList<>()
   --->客戶端註冊:
      public void registerObserver(IPackageObserver observer) {
        try {
            getService().registerObserver(observer);
        } catch (RemoteException e) {
            VirtualRuntime.crash(e);
        }
    }     

mRemoteCallbackListVirtualCore.registerObserver註冊。
sendInstalledBroadcast會廣播ACTION_PACKAGE_ADDED消息,String ACTION_PACKAGE_ADDED = "virtual." + Intent.ACTION_PACKAGE_ADDED;,代碼如下:

  public void sendBroadcastAsUser(Intent intent, VUserHandle user) { // 1150
        // intent包裝一次設置成va自定義的action
        // eg: intent.setAction("virtual.android.intent.action.PACKAGE_ADDED")
        SpecialComponentList.protectIntent(intent);
        Context context = VirtualCore.get().getContext();
        if (user != null) {
            intent.putExtra("_VA_|_user_id_", user.getIdentifier());
        }
        context.sendBroadcast(intent);
    }

===> 12.客戶端發起安裝,接收安裝結果:

客戶端點擊安裝的代碼流程如下:
添加App按鈕點擊事件爲HomeActivity.onAddAppButtonClick
—>
ListAppActivity.gotoListApp(this); 請求的requestCodeVCommends.REQUEST_SELECT_APP
—>
ListAppActivity爲顯示系統已安裝Apk界面,它由mViewPager構成:

mViewPager.setAdapter(new AppPagerAdapter(getSupportFragmentManager()));

—>
AppPagerAdapter一般是兩個ListAppFragment組成, 第一頁是設備上已安裝的apk,和存儲中下載好的可安裝的apk包。
第一頁dirs.add(null); 所以可以根據是否爲null,來確認是否取得已安裝apk還是下載的apk包。
new ListAppPresenterImpl(getActivity(), this, getSelectFrom()).start(); // 獲得App列表(系統已安裝的或系統內部存儲已安裝)。
內部通過Context.getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS)取得系統已安裝的Apk列表
或通過findAndParseAPKs來遍歷SCAN_PATH_LIST文件夾列表取得可安裝的apk包。
—>
選中後的item操作位於ListAppFragment.onViewCreated中,

  mInstallButton.setOnClickListener( v -> { // 點擊安裝軟件
            Integer[] selectedIndices = mAdapter.getSelectedIndices();
            ArrayList<AppInfoLite> dataList = new ArrayList<AppInfoLite>(selectedIndices.length);
            for (int index : selectedIndices) {
                AppInfo info = mAdapter.getItem(index);
                dataList.add(new AppInfoLite(info));
            }
            Intent data = new Intent();
            data.putParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST, dataList);
            getActivity().setResult(Activity.RESULT_OK, data);
            getActivity().finish();
        });

—>
getActivity().finish()後響應起始請求的requestCode(VCommends.REQUEST_SELECT_APP)

protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 390
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == VCommends.REQUEST_SELECT_APP) {
            if (resultCode == RESULT_OK && data != null) {
                List<AppInfoLite> appList = data.getParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST);
                if (appList != null) {
                    for (AppInfoLite info : appList) {
                        mPresenter.addApp(info);
                    }
                }
            }
        }
        

—>
mPresenter.addApp(info);,其中mPresenter = new HomePresenterImpl(this);
–>
HomePresenterImpl.addApp內部使用了 jdeferred庫異步安裝,異步處理返回結果

  public void addApp(AppInfoLite info) {
        class AddResult {
            private PackageAppData appData;
            private int userId;
        }
        AddResult addResult = new AddResult();
        ProgressDialog dialog = ProgressDialog.show(mActivity, null, mActivity.getString(R.string.tip_add_apps));
        VUiKit.defer().when(() -> {
                 ...
                InstallResult res = mRepo.addVirtualApp(info);
                ...
            }
        }).then((res) -> {
            addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);
        }).fail((e) -> {
            dialog.dismiss();
        }).done(res -> {
            if (addResult.userId == 0) {
                PackageAppData data = addResult.appData;
                data.isLoading = true;
                mView.addAppToLauncher(data);
                handleLoadingApp(data);
            } else {
               // 多用戶處理
               ...
            }
            dialog.dismiss();
        });
    }

–>
InstallResult res = mRepo.addVirtualApp(info);安裝,mRepo = new AppRepository(mActivity);

–>
進入AppRepository類中:

 public InstallResult addVirtualApp(AppInfoLite info) {
        InstallOptions options = InstallOptions.makeOptions(info.notCopyApk, false, InstallOptions.UpdateStrategy.COMPARE_VERSION);
        return VirtualCore.get().installPackageSync(info.path, options);
    }
    
--->VirtualCore.get().installPackageSync的代碼實現:
public InstallResult installPackageSync(String apkPath, InstallOptions options) {
        final ConditionVariable lock = new ConditionVariable();
        final InstallResult[] out = new InstallResult[1];
        installPackage(apkPath, options, new InstallCallback() {
            @Override
            public void onFinish(InstallResult result) {
                out[0] = result;
                lock.open();
            }
        });
        lock.block();
        return out[0];
    }
--->installPackage的代碼實現:
public void installPackage(String apkPath, InstallOptions options, final InstallCallback callback) {
        ResultReceiver receiver = new ResultReceiver(null) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                resultData.setClassLoader(InstallResult.class.getClassLoader());
                if (callback != null) {
                    InstallResult res = resultData.getParcelable("result");
                    callback.onFinish(res);
                }
            }
        };
        try {
            getService().installPackage(apkPath, options, receiver);
        } catch (RemoteException e) {
            VirtualRuntime.crash(e);
        }
    }

getService().installPackage(apkPath, options, receiver);會調到VAMS中安裝包的函數是installPackageImpl,也就是起始分析的地方。
onReceiveResult中接收安裝結果。

4.5 VAMS中安裝包完成後交互流程

回到 HomePresenterImpl.addApp函數, 再看一次:

 public void addApp(AppInfoLite info) {
        class AddResult {
            private PackageAppData appData;
            private int userId;
        }
        AddResult addResult = new AddResult();
        ProgressDialog dialog = ProgressDialog.show(mActivity, null, mActivity.getString(R.string.tip_add_apps));
        VUiKit.defer().when(() -> {
                 ...
                InstallResult res = mRepo.addVirtualApp(info);
                ...
            }
        }).then((res) -> {
            addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);
        }).fail((e) -> {
            dialog.dismiss();
        }).done(res -> {
            if (addResult.userId == 0) {// 默認用戶
                PackageAppData data = addResult.appData;
                data.isLoading = true;
                mView.addAppToLauncher(data);
                handleLoadingApp(data);
            } else {
               // 多用戶處理
               ...
            }
            dialog.dismiss();
        });
    }

在安裝完成後,會返回InstallResult resres.isSuccess=ture表示成功,之後執行:

 addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);

PackageAppData類描述了它在VA中顯示的名字和圖標,如下定義:

public class PackageAppData extends AppData {
    public String packageName;
    public String name;
    public Drawable icon;

PackageAppDataStorage類用於緩存所有的PackageAppData,它的acquire函數定義如下:

    public PackageAppData acquire(String packageName) {
        PackageAppData data;
        synchronized (packageDataMap) {
            data = packageDataMap.get(packageName);// 先從本地緩存取
            if (data == null) {
                data = loadAppData(packageName);// 如果緩存沒有,就從遠程VAMS去取
            }
        }
        return data;
    }

—>loadAppData嘗試從VPMS去取,如果成功,就加入本地緩存, 代碼如下:

    private PackageAppData loadAppData(String packageName) { // 45
        InstalledAppInfo setting = VirtualCore.get().getInstalledAppInfo(packageName, 0);
        ....
        PackageAppData data = new PackageAppData(App.getApp(), setting);
        packageDataMap.put(packageName, data);// 如果成功,就加入本地緩存
         ...
    }

—>InstalledAppInfo settingVAMS交互取得,流程如下:

     --->VirtualCore類中的getInstalledAppInfo:
    public InstalledAppInfo getInstalledAppInfo(String pkg, int flags) {
            return getService().getInstalledAppInfo(pkg, flags);
            }
            
     --->調用到VAMS中:
      public InstalledAppInfo getInstalledAppInfo(String packageName, int flags) {
        synchronized (PackageCacheManager.class) {
            if (packageName != null) {
                PackageSetting setting = PackageCacheManager.getSetting(packageName);
                if (setting != null) {
                    return setting.getAppInfo();
                }
            }
            return null;
        }
    }

可以看到就是從前面提到的PackageCacheManager類中取。
—>將InstalledAppInfo setting轉換成 PackageAppData dataPackageAppData的構造函數如下:

    public PackageAppData(Context context, InstalledAppInfo installedAppInfo) {
        this.packageName = installedAppInfo.packageName;
        this.isFirstOpen = !installedAppInfo.isLaunched(0);
        loadData(context, installedAppInfo.getApplicationInfo(installedAppInfo.getInstalledUsers()[0]));
    }
    --->loadData實現如下:
    private void loadData(Context context, ApplicationInfo appInfo) { // 28
        PackageManager pm = context.getPackageManager();
        name = appInfo.loadLabel(pm).toString();
        icon = appInfo.loadIcon(pm);
    }

—>loadData中第二個參數ApplicationInfo appInfoVPMS交互取得,流程如下:

    public ApplicationInfo getApplicationInfo(int userId) {
        return VPackageManager.get().getApplicationInfo(packageName, 0, userId);
    }
    
    --->VPackageManager類中的getApplicationInfo:
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
            return getService().getApplicationInfo(packageName, flags, userId);
     }
 --->遠程VPMS中的getApplicationInfo:
     
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
          ...
            VPackage p = mPackages.get(packageName);
            if (p != null) {
                PackageSetting ps = (PackageSetting) p.mExtras;
                return PackageParserEx.generateApplicationInfo(p, flags, ps.readUserState(userId),
                        userId);
            }
        }
        return null;
    } 
    
     --->PackageParserEx.generateApplicationInfo:
    public static ApplicationInfo generateApplicationInfo(VPackage p, int flags,
                                                          PackageUserState state, int userId) {
      ...
        ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
        if ((flags & PackageManager.GET_META_DATA) != 0) {
            ai.metaData = p.mAppMetaData;
        }
        initApplicationAsUser(ai, userId);
        return ai;
    }

可以看出VPMS返回的是VPackage.applicationInfo

—>最後顯示圖標出來:

        }).done(res -> {
            if (addResult.userId == 0) {// 默認用戶
                PackageAppData data = addResult.appData;
                data.isLoading = true;
                mView.addAppToLauncher(data); // HomeActivity
                handleLoadingApp(data);
       --->handleLoadingApp:
private void handleLoadingApp(AppData data) {
        VUiKit.defer().when(() -> {
           ....
        }).done((res) -> {
            if (data instanceof PackageAppData) {
                ((PackageAppData) data).isLoading = false;
                ((PackageAppData) data).isFirstOpen = true;
            } else if (data instanceof MultiplePackageAppData) {
                ((MultiplePackageAppData) data).isLoading = false;
                ((MultiplePackageAppData) data).isFirstOpen = true;
            }
            mView.refreshLauncherItem(data);
        });
    }

也就是通過HomeActivityaddAppToLauncherrefreshLauncherItem加載和顯示安裝APK的圖標。

參考:
Android包管理機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章