Android PackageManagerService--04:APK安裝流程

本文轉(zhuǎn)載自:Android PackageManagerService總結(jié)(四) APK安裝流程

本文基于Android 10源碼分析

前言

??PackageManagerService(簡稱PKMS),是Android系統(tǒng)中核心服務(wù)之一,管理著所有與package相關(guān)的工作,常見的比如安裝桦山、卸載應(yīng)用, 信息查詢等工作馍资,主要完成以下核心功能:

  1. 解析AndroidManifest.xml清單文件咪辱,解析清單文件中的所有節(jié)點信息员凝;

  2. 掃描本地文件响鹃,主要針對apk依啰,主要是系統(tǒng)應(yīng)用乎串、本地安裝應(yīng)用等;

  3. 管理本地apk孔飒,主要包括安裝灌闺、刪除等等;

  4. 管理設(shè)備上安裝的所有應(yīng)用程序坏瞄,并在系統(tǒng)啟動時加載應(yīng)用程序桂对;

  5. 根據(jù)請求的Intent匹配到對應(yīng)的Activity、Provider鸠匀、Service蕉斜,提供包含包名和Component的信息對象;

  6. 調(diào)用需要權(quán)限的系統(tǒng)函數(shù)時缀棍,檢查程序是否具備相應(yīng)權(quán)限從而保證系統(tǒng)安全宅此;

  7. 提供應(yīng)用程序的安裝、卸載的接口爬范。

??本篇文章重點介紹一下apk安裝流程父腕。

1.安裝apk的方式

??Android應(yīng)用安裝有如下四種方式:

  1. 系統(tǒng)應(yīng)用和預(yù)制應(yīng)用安裝――開機時完成,沒有安裝界面青瀑,在PKMS的構(gòu)造函數(shù)中完成安裝璧亮;

  2. 網(wǎng)絡(luò)下載應(yīng)用安裝――通過應(yīng)用商店應(yīng)用完成萧诫,調(diào)用PackageManager.installPackages(),有安裝界面枝嘶;

  3. ADB工具安裝――沒有安裝界面帘饶,它通過啟動pm腳本的形式,然后調(diào)用com.android.commands.pm.Pm類群扶,之后調(diào)用到PKMS.installStage()完成安裝及刻;

  4. 第三方應(yīng)用安裝――通過SD卡里的APK文件安裝,有安裝界面竞阐,由packageinstaller.apk應(yīng)用處理安裝及卸載過程的界面缴饭。

??上述幾種方式均通過PackageInstallObserver來監(jiān)聽安裝是否成功。

2.apk文件結(jié)構(gòu)

??生成的APK文件本質(zhì)還是一個zip文件馁菜,只不過被Google強行修改了一下后綴名稱而已茴扁。所以我們將APK的后綴修改成.zip就可以查看其包含的內(nèi)容了铃岔。

(1)主要由7部分組成 (下圖來源于其他作者博客: Android apk結(jié)構(gòu)分析

PKMS04.PNG

(2)META-INF目錄下3個重要文件

PKMS05.PNG

(3)res目錄下的文件說明

PKMS06.PNG

(4)細節(jié)說明

  • META-INF:關(guān)于簽名的信息存放汪疮,應(yīng)用安裝驗證簽名的時候會驗證該文件里面的信息,里面的資源文件毁习,是被編譯過的智嚷。raw和圖片是保持原樣的,但是其他的文件會被編譯成二進制文件纺且;

  • res: 這里面的資源是不經(jīng)過編譯原樣打包進來的盏道;

  • AndroidManifest.xml:程序全局配置文件,該文件是每個應(yīng)用程序都必須定義和包含的文件载碌,它描述了應(yīng)用程序的名字猜嘱、版本、權(quán)限嫁艇、引用的庫文件等等信息朗伶;

  • classes.dex:Dalvik字節(jié)碼文件,Android會將所有的class文件全部放到這一個文件里步咪;

  • resources.arsc:編譯后的二進制資源文件论皆,保存資源文件的索引,由aapt生成猾漫;

  • lib: 如果存在的話点晴,存放的是ndk編出來的so庫。

3.apk安裝過程

??這里我們主要來講解下載APK后悯周,點擊進行安裝的過程粒督,整體上來說大致分為4個步驟:

  1. 將APK的信息通過IO流的形式寫入到PackageInstaller.Session中;

  2. 調(diào)用PackageInstaller.Session的commit方法禽翼,將APK的信息交由PKMS處理屠橄;

  3. 拷貝APK萨惑;

  4. 安裝apk。

??整個過程涉及到3個跨進程通信的Binder:

PKMS07.png

APK從應(yīng)用市場下載后點擊安裝仇矾,則會跳轉(zhuǎn)到(com.android.packageinstaller) PackageInstaller.apk 的安裝界面上庸蔼,供用戶選擇安裝或取消,之前也分析過前半段的流程贮匕,是怎么跳轉(zhuǎn)到PackageInstaller.apk 的安裝界面中, 請查閱:Android PackageManagerService--01:應(yīng)用市場下載安裝apk流程

3.1 點擊安裝到APK拷貝

(1)時序圖

PKMS08.png
PKMS09.png

(2)點擊一個未安裝的apk后姐仅,會彈出安裝界面,點擊確定按鈕后刻盐,會進入PackageInstallerActivity.java的bindUi()中的mAlert點擊事件(Activity中的onCreate中調(diào)用bindUi)掏膏;

??點擊apk后,彈出的安裝界面底部顯示的是一個Dialog敦锌,主要由bindUi構(gòu)成馒疹,上面有取消和安裝兩個按鈕,點擊安裝之后調(diào)用startInstall()進行安裝乙墙。bindUi方法的代碼如下:

// frameworks/base/packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
private void bindUi() {
        mAlert.setIcon(mAppSnippet.icon);
        mAlert.setTitle(mAppSnippet.label);
        mAlert.setView(R.layout.install_content_view);
        mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
                (ignored, ignored2) -> {
                    if (mOk.isEnabled()) {
                        if (mSessionId != -1) {
                            mInstaller.setPermissionsResult(mSessionId, true);
                            finish();
                        } else {
                            // 進行APK的安裝, 關(guān)鍵代碼
                            startInstall();
                        }
                    }
                }, null);
        mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
                (ignored, ignored2) -> {
                    // Cancel and finish
                    setResult(RESULT_CANCELED);
                    if (mSessionId != -1) {
                        // 如果mSessionId存在颖变,執(zhí)行setPermissionResult()完成取消安裝
                        mInstaller.setPermissionsResult(mSessionId, false);
                    }
                    finish();
                }, null);
        setupAlert();

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

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

(3)接下來看看startInstall()方法,封裝了一個Intent跳轉(zhuǎn)到InstallInstalling.java文件中

// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
private void startInstall() {
    Intent newIntent = new Intent();
    ...
    newIntent.setClass(this, InstallInstalling.class);
    ...
    startActivity(newIntent);
}

(4)InstallInstalling的Activity啟動后听想,進入onCreate方法

// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
     protected void onCreate(@Nullable Bundle savedInstanceState) {
        ....
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
            //根據(jù)mPackageURI創(chuàng)建一個對應(yīng)的File
            final File sourceFile = new File(mPackageURI.getPath());

            // 1.如果savedInstanceState不為null腥刹,獲取此前保存的mSessionId和mInstallId,
            // 其中mSessionId是安裝包的會話Id汉买,mInstallId是等待安裝的事件Id
            if (savedInstanceState != null) {
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                mInstallId = savedInstanceState.getInt(INSTALL_ID);

                // Reregister for result; might instantly call back if result was delivered while
                // activity was destroyed
                try {
                    // 2\. 根據(jù)mInstallId向InstallEventReceiver注冊一個觀察者衔峰,           
                    // launchFinishBasedOnResult會接收到安裝事件的回調(diào),無論安裝成功還是失敗
                    // 都會關(guān)閉當(dāng)前的Activity(InstallInstalling)蛙粘。如果savedInstanceState為null垫卤,代碼的邏輯也是類似的
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult); // 監(jiān)聽安裝結(jié)果
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                //3\. 創(chuàng)建SessionParams,它用來代表安裝會話的參數(shù)出牧,組裝params
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.setInstallAsInstantApp(false);
                params.setReferrerUri(getIntent().getParcelableExtra(Intent.EXTRA_REFERRER));
                params.setOriginatingUri(getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
                params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        UID_UNKNOWN));
                params.setInstallerPackageName(getIntent().getStringExtra(
                        Intent.EXTRA_INSTALLER_PACKAGE_NAME));
                params.setInstallReason(PackageManager.INSTALL_REASON_USER);
                // 4.根據(jù)mPackageUri對包進行輕量級的解析穴肘,并將解析的參數(shù)賦值給SessionParams
                File file = new File(mPackageURI.getPath());
                try {
                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
                    params.setAppPackageName(pkg.packageName);
                    //設(shè)置apk的安裝路徑
                    params.setInstallLocation(pkg.installLocation);
                    //設(shè)置apk的大小
                    params.setSize(
                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));

                    ....
                     //5\. 向InstallEventReceiver注冊一個觀察者返回一個新的mInstallId,
                    // 其中InstallEventReceiver繼承自BroadcastReceiver崔列,用于接收安裝事件
                    // 并回調(diào)給EventResultPersister
                 try {
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                 } catch (EventResultPersister.OutOfIdsException e) {
                     launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                 }

                 ....
                 try {
                     // 6\. PackageInstaller的createSession方法內(nèi)部會通過IPackageInstaller與
                     // PackageInstallerService進行進程間通信梢褐,最終調(diào)用的是
                     // PackageInstallerService的createSession方法阿里創(chuàng)建并返回mSessionId
                     mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                 } catch (IOException e) {
                     launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                 }
                 .....
}

再來總結(jié)一下上面onCreate方法中所做的工作:

  1. 如果savedInstanceState不為null,獲取此前保存的mSessionId和mInstallId赵讯,其中mSessionId是安裝包的會話Id盈咳,mInstallId是等待的安裝事件Id;

  2. 根據(jù)mInstallId向InstallEventReceiver注冊一個觀察者边翼,launchFinishBasedOnResult會接收到安裝事件的回調(diào)鱼响,無論安裝成功或者是安裝失敗都會關(guān)閉當(dāng)前的Activity(InstallInstalling)。如果saveInstanceState為null组底,代碼的邏輯也是類似的丈积。

  3. 創(chuàng)建SessionParams筐骇,它用來代表安裝會話的參數(shù),組裝Params江滨;

  4. 根據(jù)mPackageUri對包(APK)進行輕量級的解析铛纬,并將解析的參數(shù)賦值SessionParams;

  5. 向InstallEventReceiver注冊一個觀察者返回一個新的mInstallId唬滑;

  6. PackageInstaller的createSession方法內(nèi)部會通過IPackageInstallerService進行進程間通信告唆,最終調(diào)用的是PackageInstallerService的createSession方法來創(chuàng)建并返回mSessionId。

(5)接下來繼續(xù)分析 InstallInstalling.java的onResume()方法

// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@Override
protected void onResume() {
    super.onResume();
    ...
            if (sessionInfo != null && !sessionInfo.isActive()) {
                // 創(chuàng)建內(nèi)部類InstallingAsyncTask的對象晶密,調(diào)用execute()擒悬,最終進入onPostExecute()方法
                mInstallingTask = new InstallingAsyncTask();
                mInstallingTask.execute();
            } else {
                // we will receive a broadcast when the install is finished
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            }
    ...
}

(6)InstallingAsyncTask的doInBackground()會根據(jù)包(APK)的Uri,將APK的信息通過IO流的形式寫入到PackageInstaller.Session中, 最后在onPostExecute()中調(diào)用PackageInstaller.Session的commit方法稻艰,進行安裝懂牧。

// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
        volatile boolean isDone;

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

            session.setStagingProgress(0);

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

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

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

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

                            // 將APK的信息通過IO流的形式寫入到PackageInstaller.Session中
                            out.write(buffer, 0, numRead);
                            ...
        }

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

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

                // 調(diào)用PackageInstaller.Session的commit方法,進行安裝
                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);

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

(7)接著看一下PackageInstaller.Session的commit方法, 通過IPackageInstallerSession.aidl 跨進程通信,調(diào)用 PackageInstallerSession.java 中的commit方法

// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    ...
    //調(diào)用markAsCommitted()
    if (!markAsCommitted(statusReceiver, forTransfer)) {
        return;
    }

     mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    ...
}

markAsCommitted方法中會將包的信息封裝為PackageInstallObserverAdapter 尊勿,它在PKMS中被定義僧凤,然后返回到commit()中,向Handler發(fā)送一個類型為MSG_COMMIT的消息 运怖。

(8)接著看消息處理的方法

// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_COMMIT:
                    handleCommit();// 處理消息
                    break;
        ....
}
-----------------------
private void handleCommit() {
      .....
      synchronized (mLock) {
          commitNonStagedLocked(childSessions);
      }
      ....
}

(9)commitNonStagedLocked()中首先調(diào)用了PackageInstallObserver的onPackageInstalled方法拼弃,將Complete方法出現(xiàn)的PackageManagerException的異常信息回調(diào)給PackageInstallObserverAdapter夏伊。

// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
 @GuardedBy("mLock")
    private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
            throws PackageManagerException {
            ....
            if (!success) {
                try {
                    mRemoteObserver.onPackageInstalled(
                            null, failure.error, failure.getLocalizedMessage(), null);
                } catch (RemoteException ignored) {
                }
                return;
            }
            // 最終調(diào)用這個方法
            mPm.installStage(activeChildSessions);
            ....
}

(10)最終調(diào)用installStage()摇展,進入PKMS中

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
 void installStage(List<ActiveInstallSession> children)
            throws PackageManagerException {
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    .....
    mHandler.sendMessage(msg);
    .....
}
-------------------------------------------
 void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    if (params != null) {
                        if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                System.identityHashCode(params));
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                        //執(zhí)行APK拷貝動作
                        params.startCopy();
                        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                    }
                    break;
                }
......

(11)在Handler中對INIT_COPY消息的處理中,調(diào)用了HandlerParams.startCopy方法

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
final void startCopy() {
    if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
    handleStartCopy();
    handleReturnCode();
}

(12)handleStartCopy()需要執(zhí)行下面幾步:

  1. 首先檢查文件和cid是否已經(jīng)生成溺忧,如果生成則設(shè)置installFlags咏连;

  2. 檢查空間大小,如果空間不夠就會釋放無用的空間鲁森;

  3. 覆蓋原有安裝位置的文件祟滴,并根據(jù)返回結(jié)果來確定函數(shù)的返回值,并設(shè)置installFlags歌溉;

  4. 確定是否有任何已安裝的包安裝器垄懂,如果有,則延遲檢測痛垛。主要分三步:

1)首先新建一個驗證Intent草慧,然后設(shè)置相關(guān)的信息;

2)之后獲取驗證器列表匙头;

3)最后向每個驗證器發(fā)送驗證Intent漫谷。

// Android 11.0
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
        public void handleStartCopy() {
            int ret = PackageManager.INSTALL_SUCCEEDED;

            // 1.首先檢查文件和cid是否已經(jīng)生成,如生成則設(shè)置installFlags
            if (origin.staged) {
                if (origin.file != null) {
                    installFlags |= PackageManager.INSTALL_INTERNAL;
                } else {
                    throw new IllegalStateException("Invalid stage location");
                }
            }
            .....

            // 2\. 檢查空間大小蹂析,如果空間不夠則釋放無用空間
            if (!origin.staged && pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                // TODO: focus freeing disk space on the target device
                final StorageManager storage = StorageManager.from(mContext);
                final long lowThreshold = storage.getStorageLowBytes(
                        Environment.getDataDirectory());

                final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
                        origin.resolvedPath, packageAbiOverride);
                if (sizeBytes >= 0) {
                    try {
                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                                origin.resolvedPath, installFlags, packageAbiOverride);
                    } catch (InstallerException e) {
                        Slog.w(TAG, "Failed to free cache", e);
                    }
                }

                if (pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                }
            }

            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                .....

                    {
                    // 3\. 覆蓋原有安裝位置的文件舔示,并根據(jù)返回結(jié)果來確定函數(shù)的返回值碟婆,
                    // 并設(shè)置installFlags
                    loc = installLocationPolicy(pkgLite);
                    if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                        ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                    } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
                        ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
                    } else if (!onInt) {
                        // Override install location with flags
                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                            // Set the flag to install on external media.
                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                        } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
                            if (DEBUG_INSTANT) {
                                Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
                            }
                            installFlags |= PackageManager.INSTALL_INSTANT_APP;
                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                        } else {
                            // Make sure the flag for installing on external
                            // media is unset
                            installFlags |= PackageManager.INSTALL_INTERNAL;
                        }
                    }
                }
            }

            final InstallArgs args = createInstallArgs(this);
            mVerificationCompleted = true;
            mIntegrityVerificationCompleted = true;
            mEnableRollbackCompleted = true;
            mArgs = args;

            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                final int verificationId = mPendingVerificationToken++;

                // Perform package verification (unless we are simply moving the package).
                if (!origin.existing) {
                    PackageVerificationState verificationState =
                            new PackageVerificationState(this);
                    mPendingVerification.append(verificationId, verificationState);
                    // 發(fā)送一個請求來檢查包的完整性
                    sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
                    // 向驗證者發(fā)送驗證包的請求
                    ret = sendPackageVerificationRequest(
                            verificationId, pkgLite, verificationState); 
                }
...

            mRet = ret;
        }

(13)在Android 11.0 中是通過sendPackageVerificationRequest來驗證包的

// Android 11.0
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
int sendPackageVerificationRequest(
                int verificationId,
                PackageInfoLite pkgLite,
                PackageVerificationState verificationState) {
            int ret = INSTALL_SUCCEEDED;

            .....            

            if (!origin.existing
                    && isVerificationEnabled
                    && (!isIncrementalInstall || !isV4Signed)) {
                // 4\. 確定是否有任何已安裝的包驗證器,如有惕稻,則延遲檢測竖共。主要分三步:
                // 首先新建一個驗證Intent,然后設(shè)置相關(guān)的信息俺祠,之后獲取驗證器列表
                // 最后向每個驗證器發(fā)送驗證Intent
                // 4.1 構(gòu)造驗證Intent
                final Intent verification = new Intent(
                        Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
                verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
                        PACKAGE_MIME_TYPE);
                verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

                ....

                populateInstallerExtras(verification);

                // 4.2 獲取驗證器列表
                final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
                        receivers, verificationState);

                DeviceIdleInternal idleController =
                        mInjector.getLocalDeviceIdleController();
                final long idleDuration = getVerificationTimeout();
                final BroadcastOptions options = BroadcastOptions.makeBasic();
                options.setTemporaryAppWhitelistDuration(idleDuration);

                /*
                 * If any sufficient verifiers were listed in the package
                 * manifest, attempt to ask them.
                 */
                if (sufficientVerifiers != null) {
                    final int n = sufficientVerifiers.size();
                    if (n == 0) {
                        Slog.i(TAG, "Additional verifiers required, but none installed.");
                        ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
                    } else {
                        for (int i = 0; i < n; i++) {
                            final ComponentName verifierComponent = sufficientVerifiers.get(i);
                            idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
                                    verifierComponent.getPackageName(), idleDuration,
                                    verifierUser.getIdentifier(), false, "package verifier");

                            // 4.3 向每個驗證器發(fā)送驗證Intent
                            // 向驗證器客戶端發(fā)送Intent肘迎,只有當(dāng)驗證成功之后才開啟copy工作
                            // 如果沒有任何驗證器則直接拷貝
                            final Intent sufficientIntent = new Intent(verification);
                            sufficientIntent.setComponent(verifierComponent);
                            mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
                                    /* receiverPermission= */ null,
                                    options.toBundle());
                        }
                    }
                }

            .....                

            return ret;
        }

(14)向驗證器客戶端發(fā)送Intent,只有當(dāng)驗證成功之后才會開啟copy工作锻煌。如果沒有任何驗證器則直接拷貝妓布。在handleReturnCode中調(diào)用copyApk()進行APK的拷貝工作。

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
        void handleReturnCode() {
            if (mVerificationCompleted
                    && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
                    String packageName = "";
                    ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                            new ParseTypeImpl(
                                    (changeId, packageName1, targetSdkVersion) -> {
                                        ApplicationInfo appInfo = new ApplicationInfo();
                                        appInfo.packageName = packageName1;
                                        appInfo.targetSdkVersion = targetSdkVersion;
                                        return mPackageParserCallback.isChangeEnabled(changeId,
                                                appInfo);
                                    }).reset(),
                            origin.file, 0);
                    if (result.isError()) {
                        Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(),
                                result.getException());
                    } else {
                        packageName = result.getResult().packageName;
                    }
                    try {
                        observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
                    } catch (RemoteException e) {
                        Slog.i(TAG, "Observer no longer exists.");
                    }
                    return;
                }
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    // 調(diào)用copyApk方法
                    mRet = mArgs.copyApk();
                }
                processPendingInstall(mArgs, mRet); // 這里是安裝apk的流程宋梧,放到3.2節(jié)中分析
            }
        }

(15)調(diào)用了InstallArgs.copyApk方法匣沼,最終會調(diào)用到FileInstallArgs.copyApk方法

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
        int copyApk() {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
            try {
                // 調(diào)用doCopyApk方法
                return doCopyApk();
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        }
 ------------------------------------------
        private int doCopyApk() {

            .....

            // 調(diào)用PackageManagerServiceUtils.copyPackage方法
            int ret = PackageManagerServiceUtils.copyPackage(
                    origin.file.getAbsolutePath(), codeFile);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "Failed to copy package");
                return ret;
            }
            .....
            return ret;
        }

(16)在doCopyApk方法中調(diào)用了PackageManagerServiceUtils.copyPackage方法,其代碼如下:

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java\
    public static int copyPackage(String packagePath, File targetDir) {
        if (packagePath == null) {
            return PackageManager.INSTALL_FAILED_INVALID_URI;
        }

        try {
            final File packageFile = new File(packagePath);
            final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
            copyFile(pkg.baseCodePath, targetDir, "base.apk");
            if (!ArrayUtils.isEmpty(pkg.splitNames)) {
                for (int i = 0; i < pkg.splitNames.length; i++) {
                    // 調(diào)用了copyFile方法
                    copyFile(pkg.splitCodePaths[i], targetDir,
                            "split_" + pkg.splitNames[i] + ".apk");
                }
            }
            return PackageManager.INSTALL_SUCCEEDED;
        } catch (PackageParserException | IOException | ErrnoException e) {
            Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        }
    }

(17)在copyFile方法中捂龄,通過文件流的操作释涛,把APK拷貝到/data/app等目錄

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java\
    private static void copyFile(String sourcePath, File targetDir, String targetName)
            throws ErrnoException, IOException {
        if (!FileUtils.isValidExtFilename(targetName)) {
            throw new IllegalArgumentException("Invalid filename: " + targetName);
        }
        Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);

        final File targetFile = new File(targetDir, targetName);
        final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
                O_RDWR | O_CREAT, 0644);
        Os.chmod(targetFile.getAbsolutePath(), 0644);
        FileInputStream source = null;
        try {
            source = new FileInputStream(sourcePath);
            FileUtils.copy(source.getFD(), targetFd);
        } finally {
            IoUtils.closeQuietly(source);
        }
    }

3.2 安裝apk流程

??APK拷貝完成后,進入真正的安裝倦沧,時序圖如下:

PKMS10.png

(1)在上述handleReturnCode方法中唇撬,執(zhí)行了copyApk方法后,最后又執(zhí)行了processPendingInstall方法展融。

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        if (args.mMultiPackageInstallParams != null) {
            args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
        } else {
            // 1\. 設(shè)置安裝參數(shù)
            PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
            // 2\. 創(chuàng)建一個新線程窖认,處理安裝參數(shù),進行安裝
            processInstallRequestsAsync(
                    res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                    Collections.singletonList(new InstallRequest(args, res)));
        }
    }
--------------------------------------------------
    private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
        mHandler.post(() -> {
            if (success) {
                for (InstallRequest request : installRequests) {
                    // 1\. 如果之前安裝失敗告希,清除無用信息
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    // 2\. installPackagesTracedLI是安裝過程的核心方法
                    // 然后調(diào)用installPackagesLI進行安裝
                    installPackagesTracedLI(installRequests);
                }
                for (InstallRequest request : installRequests) {
                    // 3\. 如果之前安裝失敗扑浸,清除無用信息
                    request.args.doPostInstall(
                            request.installResult.returnCode, request.installResult.uid);
                }
            }
            for (InstallRequest request : installRequests) {
                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
                        new PostInstallData(request.args, request.installResult, null));
            }
        });
    }
--------------------------------------------------
        int doPreInstall(int status) {
            if (status != PackageManager.INSTALL_SUCCEEDED) {
                // 清除無用信息
                cleanUp();
            }
            return status;
        }
--------------------------------------------------
        int doPostInstall(int status, int uid) {
            if (status != PackageManager.INSTALL_SUCCEEDED) {
                // 調(diào)用cleanUp清除無用信息
                cleanUp();
            }
            return status;
        }

(2)在installPackagesLI方法中,以原子的方式安裝一個或多個包燕偶。此操作分為四個階段:

1)Prepare準備:分析任何當(dāng)前安裝狀態(tài)喝噪,分析包并對其進行初始驗證;

2)Scan 掃描:掃描分析準備階段拿到的包指么;

3) Reconcile協(xié)調(diào):包的掃描結(jié)果酝惧,用于協(xié)調(diào)可能向系統(tǒng)中添加的一個或多個包;

4) Commit提交:提交所有掃描的包并更新系統(tǒng)狀態(tài)伯诬,這是安裝流程中唯一可以修改系統(tǒng)狀態(tài)的地方晚唇,必須在此階段之前確定所有的可預(yù)測的錯誤;

5)完成APK的安裝姑廉。

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installPackagesLI(List<InstallRequest> requests) {
    ......
    // 1\. Prepare 準備:分析任何當(dāng)前安裝狀態(tài)缺亮,分析包并對其進行初始驗證
    prepareResult = preparePackageLI(request.args, request.installResult);
    ......         
    // 2\. Scan 掃描:掃描分析準備階段拿到的包
    final ScanResult result = scanPackageTracedLI(
                            prepareResult.packageToScan, prepareResult.parseFlags,
                            prepareResult.scanFlags, System.currentTimeMillis(),
                            request.args.user, request.args.abiOverride);
    ......
    // 3\. Reconcile協(xié)調(diào):包的掃描結(jié)果,用于協(xié)調(diào)可能向系統(tǒng)中添加的一個或多個包
    ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
                    installResults,
                    prepareResults,
                    mSharedLibraries,
                    Collections.unmodifiableMap(mPackages), versionInfos,
                    lastStaticSharedLibSettings);
    ......
    // 4\. Commit 提交:提交所有掃描的包并更新系統(tǒng)狀態(tài)。這是安裝流程中唯一可以修改系統(tǒng)狀態(tài)的地方萌踱,
    // 必須在此階段之前確定所有的可預(yù)測的錯誤
    commitPackagesLocked(commitRequest);
    ......
    // 5\. 完成APK的安裝
    executePostCommitSteps(commitRequest);
}

(3)executePostCommitSteps安裝APK葵礼,并為新的代碼路徑準備應(yīng)用程序配置文件,并在此檢查是否需要dex優(yōu)化并鸵。

??如果是直接安裝新包鸳粉,會為新的代碼路徑準備應(yīng)用程序配置文件;如果是替換安裝园担,其主要過程為更新設(shè)置届谈,清除原有的某些APP數(shù)據(jù),重新生成相關(guān)的app數(shù)據(jù)目錄等步驟弯汰,同時要區(qū)分系統(tǒng)應(yīng)用替換和非系統(tǒng)應(yīng)用替換艰山。而安裝新包,則直接更新設(shè)置咏闪,生成APP數(shù)據(jù)即可曙搬。

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void executePostCommitSteps(CommitRequest commitRequest) {
    for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
        ......
        // 1) 進行安裝
        prepareAppDataAfterInstallLIF(pkg);
        // 2) 如果需要替換安裝,則需要清除原有的App數(shù)據(jù)
        if (reconciledPkg.prepareResult.clearCodeCache) {
            clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
                | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
        }

        ...

        // 3) 為新的代碼路徑準備應(yīng)用程序配置文件鸽嫂。這需要在調(diào)用dexopt之前完成纵装,
        // 以便任何安裝時配置文件都可以用于優(yōu)化
        mArtManagerService.prepareAppProfiles(
                    pkg,
                    resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),
                    /* updateReferenceProfileContent= */ true);

        // 4) 檢查是否需要優(yōu)化dex文件
        final boolean performDexopt =
                    (!instantApp || Global.getInt(mContext.getContentResolver(),
                    Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                    && !pkg.isDebuggable()
                    && (!onIncremental);

        if (performDexopt) {               

            // 5) 執(zhí)行dex優(yōu)化
            mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
                        null /* instructionSets */,
                        getOrCreateCompilerPackageStats(pkg),
                        mDexManager.getPackageUseInfoOrDefault(packageName),
                        dexoptOptions);

        }

        BackgroundDexOptService.notifyPackageChanged(packageName);

        notifyPackageChangeObserversOnUpdate(reconciledPkg);
    }
}

(4)PackageManagerService.prepareAppDataAfterInstallLIF()通過一系列的調(diào)用,最終會調(diào)用到Installer.java的createAppData()方法進行安裝据某,交給installed進程進行APK的安裝橡娄。調(diào)用過程如下:

prepareAppDataAfterInstallLIF()
    |
prepareAppDataLIF()
    |
prepareAppDataLeafLIF()
    |
[Installer.java]createAppData()
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
    ......
    for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
        ......
        if (ps.getInstalled(user.id)) {
            // TODO: when user data is locked, mark that we're still dirty
            prepareAppDataLIF(pkg, user.id, flags); 
        }
    }
}
-----------------------------------------------------
private void prepareAppDataLIF(AndroidPackage pkg, int userId, int flags) {
    if (pkg == null) {
        Slog.wtf(TAG, "Package was null!", new Throwable());
        return;
    }
    // 調(diào)用prepareAppDataLeafLIF方法
    prepareAppDataLeafLIF(pkg, userId, flags);
}
-----------------------------------------------------
private void prepareAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
    ......
    try {
        // 調(diào)用Install守護進程的入口
        ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                    appId, seInfo, pkg.getTargetSdkVersion());
    } catch (InstallerException e) {
        if (pkg.isSystem()) {
             destroyAppDataLeafLIF(pkg, userId, flags);
             try {
                 ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId,                 
                     flags,appId, seInfo, pkg.getTargetSdkVersion());

             } catch (InstallerException e2) {
                 ......
             }
         }
    }
}

(5)跳轉(zhuǎn)到Installer.java

// frameworks/base/services/core/java/com/android/server/pm/Installer.java
public long createAppData(String uuid, String packageName, int userId, int flags, int     
        appId,String seInfo, int targetSdkVersion) throws InstallerException {
    if (!checkBeforeRemote()) return -1;
    try {
        // mInstalld為IInstall的對象,即通過Binder調(diào)用到進程installd,
        // 最終調(diào)用installd的createAppData()
        return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
                    targetSdkVersion);
    } catch (Exception e) {
        throw InstallerException.from(e);
    }
}

至于之后的IInstall服務(wù)安裝過程這里不再分析癣籽,可參考:Android安裝服務(wù)installd源碼分析Android 源碼 installd 啟動流程分析挽唉。

4.總結(jié)

??APK的安裝主要分為以下四步:

1)將APK的信息通過IO流的形式寫入到 PackageInstaller.Session中;

2)調(diào)用PackageInstaller.Session的commit方法才避,將APK的信息交由PKMS處理橱夭;

3)拷貝APK;

4)最后進行安裝桑逝。

??最終是交給IInstalld守護進程進行真正的安裝操作。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俏让,一起剝皮案震驚了整個濱河市楞遏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌首昔,老刑警劉巖寡喝,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勒奇,居然都是意外死亡预鬓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門赊颠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來格二,“玉大人劈彪,你說我怎么就攤上這事《ゲ拢” “怎么了沧奴?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長长窄。 經(jīng)常有香客問我滔吠,道長,這世上最難降的妖魔是什么挠日? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任疮绷,我火速辦了婚禮,結(jié)果婚禮上嚣潜,老公的妹妹穿的比我還像新娘矗愧。我一直安慰自己,他們只是感情好郑原,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布唉韭。 她就那樣靜靜地躺著,像睡著了一般犯犁。 火紅的嫁衣襯著肌膚如雪属愤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天酸役,我揣著相機與錄音住诸,去河邊找鬼。 笑死涣澡,一個胖子當(dāng)著我的面吹牛贱呐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播入桂,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奄薇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抗愁?” 一聲冷哼從身側(cè)響起馁蒂,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜘腌,沒想到半個月后沫屡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娇唯,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡阀捅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拍埠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡勺届,死狀恐怖驶俊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涮因,我是刑警寧澤废睦,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站养泡,受9級特大地震影響嗜湃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜澜掩,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一购披、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肩榕,春花似錦刚陡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乔妈,卻和暖如春蝙云,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背路召。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工勃刨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人股淡。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓身隐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唯灵。 傳聞我的和親對象是個殘疾皇子贾铝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容