Android 8.1 源碼_核心篇 -- 深入研究 PackageManagerService 系列(5)

開篇

核心源碼

關鍵類 路徑
PackageInstaller.java frameworks/base/core/java/android/content/pm/PackageInstaller.java
PackageInstallerActivity.java packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
InstallInstalling.java packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
PackageInstallerSession.java frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

前言

在本系列上一篇文章深入研究 PackageManagerService 系列(4)之 PackageInstaller 初始化中,我們學習了 PackageInstaller 是如何初始化的,這一篇文章我們繼續學習 PackageInstaller 是如何安裝 APK 的。

PackageInstallerActivity

在上一篇文章的末尾階段,我們知道 PackageInstallerActivity 調用startInstallConfirm 方法初始化安裝確認界面後,這個安裝確認界面就會呈現給用戶,用戶如果想要安裝這個應用程序就會點擊確定按鈕,就會調用 PackageInstallerActivity 的 onClick 方法。

onClick

    public void onClick(View v) {
        if (v == mOk) {
            if (mOk.isEnabled()) {
                if (mOkCanInstall || mScrollView == null) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                    } else {
                        startInstall();     // 💥 💥 💥 💥 💥 💥 
                    }
                } else {
                    mScrollView.pageScroll(View.FOCUS_DOWN);
                }
            }
        } else if (v == mCancel) {
            // Cancel and finish
            setResult(RESULT_CANCELED);
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, false);
            }
            finish();
        }
    }

onClick 方法中分別對"確定"和"取消"按鈕做了處理,主要查看對"確定"按鈕的處理,我們發現調用了 startInstall 方法。

startInstall

    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, InstallInstalling.class);     // 💥 💥 💥 💥 💥 💥 
        String installerPackageName = getIntent().getStringExtra(
                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
        if (mOriginatingURI != null) {
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
        }
        ... ...

        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
        /**
         * startInstall 方法用於跳轉到 InstallInstalling 這個 Activity
         */
        startActivity(newIntent);
        finish();     // 關閉當前的 PackageInstallerActivity
    }

InstallInstalling

InstallInstalling 主要用於向包管理器發送包的信息並處理包管理的回調。

onCreate

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.install_installing);

        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mPackageURI = getIntent().getData();

        // 分別對 package 和 content 協議的 Uri 進行處理
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
            // 根據 mPackageURI 創建一個對應的 File
            final File sourceFile = new File(mPackageURI.getPath());
            PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
                    sourceFile), R.id.app_snippet);
            // 如果 savedInstanceState 不爲 null,獲取此前保存的 mSessionId 和 mInstallId
            if (savedInstanceState != null) {
                mSessionId = savedInstanceState.getInt(SESSION_ID);    // mSessionId 是安裝包的會話 id
                mInstallId = savedInstanceState.getInt(INSTALL_ID);    // mInstallId 是等待的安裝事件 id

                /**
                 * 根據 mInstallId 向 InstallEventReceiver 註冊一個觀察者
                 * launchFinishBasedOnResult 會接收到安裝事件的回調,無論安裝成功或者失敗都會關閉當前的 Activity(InstallInstalling)
                 */
                try {
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                // savedInstanceState 爲 null
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
                params.originatingUri = getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
                params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        UID_UNKNOWN);

                File file = new File(mPackageURI.getPath());
                try {
                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
                    params.setAppPackageName(pkg.packageName);
                    params.setInstallLocation(pkg.installLocation);
                    params.setSize(
                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
                } catch (PackageParser.PackageParserException e) {
                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                } catch (IOException e) {
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                }

                try {
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

                try {
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }
            ... ...

            mSessionCallback = new InstallSessionCallback();
        }
    }

onResume

我們查看 InstallInstalling 的 onResume 方法:

    @Override
    protected void onResume() {
        super.onResume();

        // This is the first onResume in a single life of the activity
        if (mInstallingTask == null) {
            PackageInstaller installer = getPackageManager().getPackageInstaller();
            // 根據 mSessionId 得到 SessionInfo,SessionInfo 代表安裝會話的詳細信息
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

            // 如果 sessionInfo 不爲 Null 並且不是活動的,就創建並執行 InstallingAsyncTask
            if (sessionInfo != null && !sessionInfo.isActive()) {
                mInstallingTask = new InstallingAsyncTask();           // 💥 💥 💥 💥 💥 💥
                mInstallingTask.execute();
            } else {
                // we will receive a broadcast when the install is finished
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            }
        }
    }

InstallingAsyncTask

既然啓動了 InstallingAsyncTask,那我們就來看看 相關的 doInBackground 和 onPostExecute 方法:

    /**
     * Send the package to the package installer and then register a event result observer that
     * will call {@link #launchFinishBasedOnResult(int, int, String)}
     */
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
        volatile boolean isDone;

        // 根據包(APK)的 Uri,將 APK 的信息通過 IO 流的形式寫入到 PackageInstaller.Session 中
        @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;
                            }

                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }

                return session;
            } catch (IOException | SecurityException e) {
                Log.e(LOG_TAG, "Could not write package", e);

                session.close();

                return null;
            } finally {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
            }
        }

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

                // 創建了一個 PendingIntent
                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                        InstallInstalling.this,
                        mInstallId,
                        broadcastIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
                /**
                 * 將該 PendingIntent 的 IntentSender 通過
                 * 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);
                }
            }
        }
    }

Ok,問題來了,commit 到哪去了?我們不妨來看一下 PackageInstaller.Session 的 commit 方法:

commit

        public void commit(@NonNull IntentSender statusReceiver) {
            try {
                mSession.commit(statusReceiver, false);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

mSession

    public static class Session implements Closeable {
        private IPackageInstallerSession mSession;

我們發現,mSession 的類型爲 IPackageInstallerSession ,這說明要通過 IPackageInstallerSession 來進行進程間的通信,最終會調用 PackageInstallerSession 的 commit 方法,又回到框架層處理了。

PackageInstallerSession

commit

    @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        Preconditions.checkNotNull(statusReceiver);

        final boolean wasSealed;
        synchronized (mLock) {
            assertCallerIsOwnerOrRootLocked();
            assertPreparedAndNotDestroyedLocked("commit");

            // 將包的信息封裝爲 PackageInstallObserverAdapter
            final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
                    mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);
            mRemoteObserver = adapter.getBinder();
            ... ...

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

        ... ...
    }

Handler.Callback

    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_COMMIT:
                    synchronized (mLock) {
                        try {
                            commitLocked();        // 💥 💥 💥 💥 💥 💥
                        } catch (PackageManagerException e) {
                            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                            Slog.e(TAG,
                                    "Commit of session " + sessionId + " failed: " + completeMsg);
                            destroyInternal();
                            dispatchSessionFinished(e.error, completeMsg, null);     // 💥 💥 💥 💥 💥 💥
                        }
                    }

                    break;
                case MSG_SESSION_FINISHED_WITH_EXCEPTION:
                    PackageManagerException e = (PackageManagerException) msg.obj;

                    dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e),
                            null);
                    break;
            }

            return true;
        }
    };

commitLocked

    private final PackageManagerService mPm;
        
    private void commitLocked()
            throws PackageManagerException {
        ... ...

        /**
         * commitLocked 方法很長,我們主要關注這一行代碼
         * 調用 PackageManagerService 的 installStage 方法
         * 這樣安裝 APK 的代碼邏輯就進入了 PackageManagerService 中
         */
        mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
                mInstallerPackageName, mInstallerUid, user, mCertificates);
    }

dispatchSessionFinished

我們這邊再看一下,如果 commitLocked 出現了異常後的流程處理:

    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
        final IPackageInstallObserver2 observer;
        final String packageName;
        ... ...

        if (observer != null) {
            try {
                // 調用 IPackageInstallObserver2 的 onPackageInstalled 方法
                observer.onPackageInstalled(packageName, returnCode, msg, extras);      // 💥 💥 💥 💥 💥 💥
            } catch (RemoteException ignored) {
            }
        }
        ... ...

        mCallback.onSessionFinished(this, success);
    }

它的實現方法在 PackageInstallObserver 類中:

public class PackageInstallObserver {
    private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() {
        @Override
        public void onUserActionRequired(Intent intent) {
            PackageInstallObserver.this.onUserActionRequired(intent);
        }

        @Override
        public void onPackageInstalled(String basePackageName, int returnCode,
                String msg, Bundle extras) {
            /**
             * 調用了 PackageInstallObserver 的 onPackageInstalled 方法
             * 實現這個方法的類爲 PackageInstallObserver 的子類:PackageInstallObserverAdapter
             */
            PackageInstallObserver.this.onPackageInstalled(basePackageName, returnCode, msg,
                    extras);
        }
    };
    ... ...
}

也就是說:dispatchSessionFinished 方法會通過 Observer 的 onPackageInstalled 方法,將出現的 PackageManagerException 異常信息回調給 PackageInstallObserverAdapter。

總結

本篇文章講解了 PackageInstaller 安裝 APK 的過程,簡單來說就兩步:
        ✨  1. 將 APK 的信息通過 IO流 的形式寫入到 PackageInstaller.Session中。
        ✨  2. 調用 PackageInstaller.Session 的 commit 方法,將 APK 的信息交由 PMS 處理。

參考

 01. https://blog.csdn.net/itachi8...

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