主頁 > 移動端開發 > ??Android 從原始碼解讀 Apk 的安裝程序 ??

??Android 從原始碼解讀 Apk 的安裝程序 ??

2021-09-25 17:47:20 移動端開發

目錄

App安裝程序

PackageInstallerActivity

onCreat()

onResume()

bindUi()

startInstall()

InstallInstalling

onCreat()

onStart()

onResume()

InstallingAsyncTask

PackageInstaller.commit()

PackageInstallerSession

commit()

dispatchStreamValidateAndCommit()

mHandler()

handleStreamValidateAndCommit()

mHandler()

handleInstall()

installNonStagedLocked()

PackageManagerService

installStage()

mHandler()

HandlerParams.startCopy()

InstallParams

FileInstallArgs.copyApk()

PackageManagerServiceUtils.copyPackage()

InstallParams

processPendingInstall()

processInstallRequestsAsync()

installPackagesTracedLI()

installPackagesLI()

preparePackageLI()

installPackagesLI()

executePostCommitSteps()

prepareAppDataAfterInstallLIF()

prepareAppDataLIF()

prepareAppDataLeafLIF()

Installer

流程圖

相關推薦

Android Gradle 詳解

Android apk 包體積優化

Android Apk 的打包程序


App安裝程序

原始碼:Android SDK 30

當我們點擊某一個 App 安裝包進行安裝時,首先會彈出一個系統界面指示我們進行安裝操作,這個界面是 Android Framework 中預置的一個 Activity>PackageInstallerActivity.java

PackageInstallerActivity

frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

/**
 * This activity is launched when a new application is installed via side loading
 * The package is first parsed and the user is notified of parse errors via a dialog.
 * If the package is successfully parsed, the user is notified to turn on the install unknown
 * applications setting. A memory check is made at this point and the user is notified of out
 * of memory conditions if any. If the package is already existing on the device,
 * a confirmation dialog (to replace the existing package) is presented to the user.
 * Based on the user response the package is then installed by launching InstallAppConfirm
 * sub activity. All state transitions are handled in this activity
 */
public class PackageInstallerActivity extends AlertActivity {
      ...
}

看到 Activity 當然是先從 onCreate() 開始了

onCreat()

public class PackageInstallerActivity extends AlertActivity {
    @Override
    protected void onCreate(Bundle icicle) {
        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);

        super.onCreate(null);

        if (icicle != null) {
            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
        }

        //mPm = PackageManagerService
        mPm = getPackageManager();
        //mIpm = IPackageManager
        //將原始介面回傳給包管理器,
        mIpm = AppGlobals.getPackageManager();
        //AppOpsManager 涵蓋了從幫助運行時權限訪問控制和跟蹤到電池消耗跟蹤的各種功能,
        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
        //提供在設備上安裝、升級和洗掉應用程式的能力,
        mInstaller = mPm.getPackageInstaller();
        //在多用戶系統上管理用戶和用戶詳細資訊
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

        final Intent intent = getIntent();
        ...
        //安裝包Uri
        final Uri packageUri;

        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
            //獲取android.content.pm.extra.SESSION_ID
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            //獲取安裝Session的詳細資訊
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
                //獲取SessionInfo失敗退出安裝
                finish();
                return;
            }

            mSessionId = sessionId;
            packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
            mOriginatingURI = null;
            mReferrerURI = null;
        } else {
            mSessionId = -1;
            packageUri = intent.getData();
            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
        }

        if (packageUri == null) {
            //獲取packageUri失敗退出安裝
            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
            finish();
            return;
        }
        //決議 Uri 并為此包設定安裝程式,
        boolean wasSetUp = processPackageUri(packageUri);
        if (!wasSetUp) {
            return;
        }
    }
}

在 onCreate() 方法中,首先執行一些初始化操作,獲取PackageManager和PackageInstaller、UserManager等物件,根據當前Intent的資訊最一些邏輯判斷并彈出訊息彈窗,然后獲取一些安裝包資訊,接下來咱們安裝 Activity 的生命周期往下看 onStart() 沒有重寫,咱繼續往下看

onResume()

    @Override
    protected void onResume() {
        super.onResume();
        //processPackageUri已拿到mAppSnippet
        if (mAppSnippet != null) {
            // 在禁用確定按鈕的情況下加載虛擬布局,直到我們覆寫此布局
            // 開始安裝確認
            bindUi();
            // 檢查是否允許安裝包,如果允許則啟動安裝, 
            //如果不允許顯示適當的對話框,
            checkIfAllowedAndInitiateInstall();
        }

        if (mOk != null) {
            mOk.setEnabled(mEnableOk);
        }
    }

bindUi()

    private void bindUi() {
        ...
        mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
                (ignored, ignored2) -> {
                    if (mOk.isEnabled()) {
                        ...
                            startInstall();
                        ...
                    }
                }, null);
        ...
    }

這里點擊安裝按鈕進去 startInstall() 方法,失敗的情況咱就不看了,繼續往下走著,

startInstall()

    private void startInstall() {
        // 啟動子Activity,安裝應用程式
        Intent newIntent = new Intent();
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
        newIntent.setData(mPackageURI);
        newIntent.setClass(this, InstallInstalling.class);
        ...
        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        startActivity(newIntent);
        finish();
    }

startInstall 呼叫即使啟用了一個新的Activity–>InstallInstalling,會把安裝包的資訊通過 Intent 傳遞到 InstallInstalling 這個 Activity,

InstallInstalling

frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

/**
 * Send package to the package manager and handle results from package manager. Once the
 * installation succeeds, start {@link InstallSuccess} or {@link InstallFailed}.
 * <p>This has two phases: First send the data to the package manager, then wait until the package
 * manager processed the result.</p>
 */
public class InstallInstalling extends AlertActivity {
      ...
}

在這個頁面點擊安裝時,InstallInstalling 的作用主要是向 PMS 發送包資訊以及處理回呼, 這有兩個階段:

  • 首先將資料發送給 PackageManager ;

  • 等待 PackageManager 處理結果(InstallSuccess 或 InstallFailed),

看到 Activity 當然是先從 onCreate() 開始了

onCreat()

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

        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        //要安裝的包的 URI
        mPackageURI = getIntent().getData();
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                //注釋1:安裝現有包
                getPackageManager().installExistingPackage(appInfo.packageName);
                //安裝成功頁面
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                //安裝失敗頁面
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
             //注釋2:沒有現有包
             //根據 mPackageURI 創建一個對應的 File
            final File sourceFile = new File(mPackageURI.getPath());
            //顯示應用資訊 icon,應用名
            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);

            mAlert.setIcon(as.icon);
            ...
            if (savedInstanceState != null) {
                //咱們第一次僅savedInstanceState==null
                //安裝包的會話 ID
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                //我們等待的安裝事件的 ID
                mInstallId = savedInstanceState.getInt(INSTALL_ID);
                //重新登記結果;如果結果是在 Activtiy 被銷毀時交付的,則可能會立即回呼
                try {
                    //添加一個觀察者,如果此id已存在事件,請在此呼叫內回呼,
                    // 向 InstallEventReceiver 注冊一個觀察者
                    // InstallEventReceiver 是一個 BroadcastReceiver,可以通過 EventResultPersister 接收到所有的安裝事件
                    // 這里事件會回呼給 this::launchFinishBasedOnResult
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                //咱們第一次僅savedInstanceState==null
                
                //創建 SessionParams,它用來攜帶會話的引數
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.setInstallAsInstantApp(false);
                ...
                params.setInstallReason(PackageManager.INSTALL_REASON_USER);

                File file = new File(mPackageURI.getPath());
                try {
                    // 對 APK 進行輕量級的決議,并將決議的結果賦值給 SessionParams 相關欄位 
                    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) {
                    ...
                } catch (IOException e) {
                    ...
                }

                try {
                    // 向 InstallEventReceiver 注冊一個觀察者回傳一個新的 mInstallId
                    // InstallEventReceiver 是一個 BroadcastReceiver,可以通過 EventResultPersister 接收到所有的安裝事件
                    // 這里事件會回呼給 this::launchFinishBasedOnResult
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

                try {
                    //PackageInstaller 的 createSession 
                    //方法內部會通過 IPackageInstaller 與 PackageInstallerService進行行程間通信,
                    //最終呼叫的是 PackageInstallerService 的 createSession 方法來創建并回傳 mSessionId
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }

            mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);

            //監聽會話的更改并更新進度條
            mSessionCallback = new InstallSessionCallback();
        }
    }

NameNotFoundException:當找不到給定的包、應用程式或組件名稱時,將引發此例外,

在 onCreate() 方法中,首先執行一些初始化操作,比如獲取 mInstallId、mSessionId以及監聽回呼 mSessionCallback ,接下來看看 onStart() ,

onStart()

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

        //設定回話監聽回呼
        getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
    }

onResume()

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

        //This is the first onResume in a single life of the activity
        if (mInstallingTask == null) {
            //PackageInstaller:提供在設備上安裝、升級和洗掉應用程式的能力,
            //這包括支持打包為單個“整體”APK 的應用程式或打包為多個“拆分”APK 的應用程式,
            PackageInstaller installer = getPackageManager().getPackageInstaller();
            //安裝Session的詳細資訊
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

            if (sessionInfo != null && !sessionInfo.isActive()) {
                //創建mInstallingTask并啟動
                mInstallingTask = new InstallingAsyncTask();
                mInstallingTask.execute();
            } else {
                // 安裝完成后我們會收到廣播
                //mCancelButton不允許點擊
                mCancelButton.setEnabled(false);
                //Activity.setFinishOnTouchOutside():設定此活動在其視窗邊界外觸摸
                setFinishOnTouchOutside(false);
            }
        }
    }

InstallingAsyncTask

    /**
     * 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;

        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
                //打開現有Session
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
                return null;
            }
            //設定暫存此會話的當前進度(0-1z之間),
            session.setStagingProgress(0);

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

                //輸入流in
                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    //打開一個流以將 APK 檔案寫入會話,
                    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);
                            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(getPackageName());
                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

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

                // 呼叫 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);
                }
            }
        }
    }

其他沒啥好看的,咱么看看具體的session.commit

PackageInstaller.commit()

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

mSession 就是 IPackageInstallerSession ,這說明要通過 IPackageInstallerSession 來進行行程間的通信,最侄訓呼叫PackageInstallerSession 的 commit 方法

PackageInstallerSession

frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

public class PackageInstallerSession extends IPackageInstallerSession.Stub {
      ...
}

commit()

    @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (hasParentSessionId()) {
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + mParentSessionId +  " and may not be committed directly.");
        }

        if (!markAsSealed(statusReceiver, forTransfer)) {
            return;
        }
        //return true 多個包
        if (isMultiPackage()) {
            ...
        }

        dispatchStreamValidateAndCommit();
    }

dispatchStreamValidateAndCommit()

    private void dispatchStreamValidateAndCommit() {
        // 通過 Handler 處理訊息事件
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }

mHandler()

    Handler mHandler = new Handler(looper, mHandlerCallback);
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit();
                    break;
                ...
            }

            return true;
        }
    };

handleStreamValidateAndCommit()

    private void handleStreamValidateAndCommit() {
        PackageManagerException unrecoverableFailure = null;
        // 這將跟蹤會話和任何子項是否已通過驗證并準備好進入下一安裝階段
        boolean allSessionsReady = false;
        try {
            allSessionsReady = streamValidateAndCommit();
        } catch (PackageManagerException e) {
            unrecoverableFailure = e;
        }

        //多包,不是多包直接過
        if (isMultiPackage()) {
            ....
        }

        if (!allSessionsReady) {
            return;
        }

        // 通過 Handler 處理MSG_INSTALL訊息事件
        mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
    }

mHandler()

    Handler mHandler = new Handler(looper, mHandlerCallback);
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                ...
                case MSG_INSTALL:
                    handleInstall();
                    break;
                ...
            }

            return true;
        }
    };

handleInstall()

    private void handleInstall() {
        ...
        //return true 會話被再次啟用,
        if (params.isStaged) {
            mStagingManager.commitSession(this);
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            return;
        }
        //安裝 APEX 包,則回傳 true,
        if (isApexInstallation()) {
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                    "APEX packages can only be installed using staged sessions.", null);
            return;
        }

        // 對于 multiPackage 會話,讀取鎖外的子會話,
        // 因為讀取持有鎖的子會話可能會導致死鎖 (b/123391593),
        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();

        try {
            synchronized (mLock) {
                //接下來咱們就看看這個方法,
                installNonStagedLocked(childSessions);
            }
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
            destroyInternal();
            dispatchSessionFinished(e.error, completeMsg, null);
        }
    }

installNonStagedLocked()

    @GuardedBy("mLock")
    private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
            throws PackageManagerException {
        final PackageManagerService.ActiveInstallSession installingSession =
                makeSessionActiveLocked();
        if (installingSession == null) {
            return;
        }
        //return true 多個包
        if (isMultiPackage()) {
            List<PackageManagerService.ActiveInstallSession> installingChildSessions =
                    new ArrayList<>(childSessions.size());
            ...
            mPm.installStage(installingChildSessions);
        } else {
            mPm.installStage(installingSession);
        }
    }

這里不管不管怎么樣都會呼叫 mPm.installStage(installingChildSessions);

mPm 就是系統服務 PackageManagerService,

PackageManagerService

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
}

installStage()

installStage 方法就是正式開始 apk 的安裝程序,這個程序包括兩大步:

1、拷貝安裝包;

2、裝載代碼,

    void installStage(ActiveInstallSession activeInstallSession) {
        ...
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        //把之前傳入的 sessionParams 安裝資訊,及其它資訊封裝成 InstallParams
        final InstallParams params = new InstallParams(activeInstallSession);
        params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
        msg.obj = params;

        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
                System.identityHashCode(msg.obj));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(msg.obj));

        mHandler.sendMessage(msg);
    }

    void installStage(List<ActiveInstallSession> children)
            throws PackageManagerException {
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final MultiPackageInstallParams params =
                new MultiPackageInstallParams(UserHandle.ALL, children);
        params.setTraceMethod("installStageMultiPackage")
                .setTraceCookie(System.identityHashCode(params));
        msg.obj = params;

        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStageMultiPackage",
                System.identityHashCode(msg.obj));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(msg.obj));
        mHandler.sendMessage(msg);
    }

MultiPackageInstallParams:多包安裝的容器,指的是一起提交的所有安裝會話和引數,

這兩個方法很詳細就是把之前傳入的 sessionParams 安裝資訊及其它資訊封裝成 InstallParams(MultiPackageInstallParams),

mHandler 發送的訊息 INIT_COPY 從名字上就知道是去初始化復制,

mHandler()

    class PackageHandler extends Handler {
        PackageHandler(Looper looper) {
            super(looper);
        }
        public void handleMessage(Message msg) {
            try {
                doHandleMessage(msg);
            } finally {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            }
        }
        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");
                        params.startCopy();
                        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                    }
                    break;
                }
                ...
            }
        }
    }

HandlerParams.startCopy()

    private abstract class HandlerParams {
        /** User handle for the user requesting the information or installation. */
        private final UserHandle mUser;
        String traceMethod;
        int traceCookie;
        ...
        final void startCopy() {
            if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
            handleStartCopy();
            handleReturnCode();
        }

        abstract void handleStartCopy();
        abstract void handleReturnCode();
    }

startCopy(),里面有兩個抽象方法,咱只能去找他的實作類 InstallParams 里面的這兩個方法了,

InstallParams

    class InstallParams extends HandlerParams {
        ...
            /*
         * 呼叫遠程方法來獲取包資訊和安裝位置值,
         * 如果需要,根據默認策略覆寫安裝位置,然后根據安裝位置創建安裝引數,
         */
        public void handleStartCopy() {
            int ret = PackageManager.INSTALL_SUCCEEDED;

            // 表示檔案已下載,不需要再次下載,
            if (origin.staged) {
                 // 設定安裝標志位,決定是安裝在手機內部存盤空間還是 sdcard 中
                if (origin.file != null) {
                    installFlags |= PackageManager.INSTALL_INTERNAL;
                } else {
                    throw new IllegalStateException("Invalid stage location");
                }
            }

            // 判斷安裝位置
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
            final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
            PackageInfoLite pkgLite = null;


            //決議給定的包并回傳最少的細節,
            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                    origin.resolvedPath, installFlags, packageAbiOverride);

            if (DEBUG_INSTANT && ephemeral) {
                Slog.v(TAG, "pkgLite for install: " + pkgLite);
            }

            /*
             * 如果我們的可用空間太少,請在放棄之前嘗試釋放快取,
             */
            if (!origin.staged && pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                // TODO: 釋放目標設備上的磁盤空間
                final StorageManager storage = StorageManager.from(mContext);
                //回傳給定路徑被(Environment.getDataDirectory())認為存盤空間不足的可用位元組數,
                final long lowThreshold = storage.getStorageLowBytes(
                        Environment.getDataDirectory());

                //回傳:-1 計算安裝包大小有錯誤
                //回傳其他:安裝包大小,
                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) {
                //指定推薦的安裝位置, 可以是其中之一
                //RECOMMEND_INSTALL_INTERNAL 安裝在內部存盤上,
                //RECOMMEND_INSTALL_EXTERNAL 安裝在外部媒體上,
                //RECOMMEND_FAILED_INSUFFICIENT_STORAGE 用于存盤錯誤,
                //或 RECOMMEND_FAILED_INVALID_APK 決議錯誤,
                int loc = pkgLite.recommendedInstallLocation;
                ...
            }

            //創建安裝引數
            final InstallArgs args = createInstallArgs(this);
            mVerificationCompleted = true;
            mIntegrityVerificationCompleted = true;
            mEnableRollbackCompleted = true;
            mArgs = args;

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

                // 執行安裝包驗證(除非我們只是簡單地移動裝包),
                if (!origin.existing) {
                    ...
                }


                //INSTALL_ENABLE_ROLLBACK 的標志引數以指示應為此安裝啟用回滾,
                if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                    // TODO(ruhler) b/112431924: 在“move”的情況下不要這樣做?
                    final int enableRollbackToken = mPendingEnableRollbackToken++;
                    Trace.asyncTraceBegin(
                            TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
                    mPendingEnableRollback.append(enableRollbackToken, this);

                    Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
                    enableRollbackIntent.putExtra(
                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
                            enableRollbackToken);
                    enableRollbackIntent.putExtra(
                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
                            mSessionId);
                    enableRollbackIntent.setType(PACKAGE_MIME_TYPE);
                    enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

                    //允許在啟動完成前發送廣播, 在早期啟動中提交分階段會話的 apk 部分時需要這樣做, 回滾管理器在啟動程序中足夠早地注冊其接收器,以免錯過廣播,
                    // 在早期啟動中提交分階段會話的 apk 部分時需要這樣做,
                    // 回滾管理器在啟動程序中足夠早地注冊其接收器,以免錯過廣播,
                    enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

                    mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
                            android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
                            new BroadcastReceiver() {
                                @Override
                                public void onReceive(Context context, Intent intent) {
                                    // 等待回滾啟用的持續時間,以毫秒為單位
                                    long rollbackTimeout = DeviceConfig.getLong(
                                            DeviceConfig.NAMESPACE_ROLLBACK,
                                            PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
                                            DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
                                    if (rollbackTimeout < 0) {
                                        rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
                                    }
                                    final Message msg = mHandler.obtainMessage(
                                            ENABLE_ROLLBACK_TIMEOUT);
                                    msg.arg1 = enableRollbackToken;
                                    msg.arg2 = mSessionId;
                                    mHandler.sendMessageDelayed(msg, rollbackTimeout);
                                }
                            }, null, 0, null, null);

                    mEnableRollbackCompleted = false;
                }
            }

            mRet = ret;
        }
        @Override
        void handleReturnCode() {
            if (mVerificationCompleted
                    && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                //INSTALL_DRY_RUN :installPackage 的標志引數,指示只應驗證包而不應安裝包,
                if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
                    ...
                }
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    //注釋1
                    mRet = mArgs.copyApk();
                }
                //注釋2
                processPendingInstall(mArgs, mRet);
            }
        }
    }
    }

注釋1:mArgs 其實就是 InstallArgs ,而 InstallArgs 則是通過 createInstallArgs(this); 創建如下代碼: createInstallArgs(this)

    private InstallArgs createInstallArgs(InstallParams params) {
        if (params.move != null) {
            //處理現有已安裝應用程式移動的邏輯,
            return new MoveInstallArgs(params);
        } else {
            //處理新應用程式安裝的邏輯,包括復制和重命名邏輯,
            return new FileInstallArgs(params);
        }
    }

這里createInstallArgs 回傳的是 FileInstallArgs 物件,

FileInstallArgs.copyApk()

拷貝部分

    class FileInstallArgs extends InstallArgs {
        private File codeFile;
        private File resourceFile;

        // Example topology:
        // /data/app/com.example/base.apk
        // /data/app/com.example/split_foo.apk
        // /data/app/com.example/lib/arm/libfoo.so
        // /data/app/com.example/lib/arm64/libfoo.so
        // /data/app/com.example/dalvik/arm/base.apk@classes.dex

        /** New install */
        FileInstallArgs(InstallParams params) {
            super(params);
        }

        /** Existing install */
        FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
            super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                    null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
                    PackageParser.SigningDetails.UNKNOWN,
                    PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT,
                    false, null /* parent */, DataLoaderType.NONE);
            this.codeFile = (codePath != null) ? new File(codePath) : null;
            this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
        }

        //執行此處方法
        int copyApk() {
            try {
                //就呼叫個doCopyApk(),接著往下看
                return doCopyApk();
            } finally {
            }
        }

        private int doCopyApk() {
            //表示檔案已下載
            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 {
                final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
                //創建路徑
                final File tempDir =
                        mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
                codeFile = tempDir;
                resourceFile = tempDir;
            } catch (IOException e) {
                Slog.w(TAG, "Failed to create copy file: " + e);
                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            }

            //將包復制到目標位置(origin.file.getAbsolutePath()),
            //回傳狀態碼,注釋3
            int ret = PackageManagerServiceUtils.copyPackage(
                    origin.file.getAbsolutePath(), codeFile);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "無法復制安裝包");
                return ret;
            }

            final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
            final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
            NativeLibraryHelper.Handle handle = null;
            try {
                handle = NativeLibraryHelper.Handle.create(codeFile);
                // 將 apk 中的動態庫 .so 檔案也拷貝到目標路徑中,
                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                        abiOverride, isIncremental);
            } catch (IOException e) {
                Slog.e(TAG, "Copying native libraries failed", e);
                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            } finally {
                IoUtils.closeQuietly(handle);
            }

            return ret;
        }
    }

注釋3:PackageManagerServiceUtils.copyPackage() 這個看名字 就是呼叫 工具類 的 copyPackage 方法拷貝安裝包,具體咱們往下看,

PackageManagerServiceUtils.copyPackage()

frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java

    /**
     * 將包復制到目標位置,
     * @param packagePath 要復制的包的絕對路徑, 
     * 可以是單個單一的 APK 檔案,也可以是包含一個或多個 APK 的集群目錄,
     * @return 根據PackageManager 中的狀態回傳狀態碼
     */
    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的原始碼,咱一起看
            copyFile(pkg.baseCodePath, targetDir, "base.apk");
            if (!ArrayUtils.isEmpty(pkg.splitNames)) {
                for (int i = 0; i < pkg.splitNames.length; i++) {
                    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;
        }
    }

    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);
            //將 source.getFD() 的內容復制到targetFd,
            //FileUtils:frameworks/base/core/java/android/os/FileUtils.java
            FileUtils.copy(source.getFD(), targetFd);
        } finally {
            IoUtils.closeQuietly(source);
        }
    }

看到這里 最終安裝包在 data/app 目錄下以 base.apk 的方式保存,至此安裝包拷貝作業就已經完成,

例:/data/app/com.example/base.apk

InstallParams

接下來咱們看看 InstallParams 中 的 注釋2 processPendingInstall(mArgs, mRet); 也就是裝載部分,

    class InstallParams extends HandlerParams {
        ...
        @Override
        void handleReturnCode() {
            if (mVerificationCompleted
                    && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                ...
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    //注釋1
                    mRet = mArgs.copyApk();
                }                
                //注釋2
                processPendingInstall(mArgs, mRet);
            }
        }
    }
    }

processPendingInstall()

裝載部分

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        //分包
        if (args.mMultiPackageInstallParams != null) {
            args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
        } else {
            //
            PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
            //異步操作,因為包安裝可能需要一段時間,
            processInstallRequestsAsync(
                    res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                    Collections.singletonList(new InstallRequest(args, res)));
        }
    }

processInstallRequestsAsync()

    private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
        mHandler.post(() -> {
            if (success) {
                for (InstallRequest request : installRequests) {
                     // 預安裝操作,主要是檢查安裝包的狀態,確保安裝環境正常,如果安裝環境有問題會清理拷貝檔案
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    //真正的安裝階段
                    installPackagesTracedLI(installRequests);
                }
                for (InstallRequest request : installRequests) {
                    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));
            }
        });
    }

installPackagesTracedLI()

    @GuardedBy({"mInstallLock", "mLock"})
    private void installPackagesTracedLI(List<InstallRequest> requests) {
        try {
            //添加跟蹤 Trace,
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
            //呼叫 installPackageLI 方法進行安裝,咱們接著看
            installPackagesLI(requests);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

installPackagesLI()

    @GuardedBy("mInstallLock")
    private void installPackagesLI(List<InstallRequest> requests) {
        ...
        boolean success = false;
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
            for (InstallRequest request : requests) {
                final PrepareResult prepareResult;
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
                    //注釋1
                    //成功準備好的安裝軟體包所需的資料集,這包括將用于掃描和協調包的資料,
                    prepareResult =
                            preparePackageLI(request.args, request.installResult);
                } catch (PrepareFailure prepareFailure) {
                    ...
                    return;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                ...
            }
        }
    }

注釋1:preparePackageLI() 包含內容過多咱們開始逐步講解,

preparePackageLI()

    @GuardedBy("mInstallLock")
    private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
            throws PrepareFailure {
        final int installFlags = args.installFlags;
        final File tmpPackageFile = new File(args.getCodePath());
        final boolean onExternal = args.volumeUuid != null;
        final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
        final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
        final boolean virtualPreload =
                ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
        @ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
        ...
        
        ParsedPackage parsedPackage;
        //PackageParser2:PackageParser 的 v2 用于在服務中啟動決議時使用,并且必須包含服務包含的狀態,
        try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null,
                mPackageParserCallback)) {
            // 注釋1:pp.parsePackage()新包決議
            parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
            AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
        } catch (PackageParserException e) {
            throw new PrepareFailure("Failed parse during installPackageLI", e);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
        ...

        try {
            // 要么使用我們得到的,要么直接從APK決議
            if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
                parsedPackage.setSigningDetails(args.signingDetails);
            } else {
                parsedPackage.setSigningDetails(
                        ParsingPackageUtils.getSigningDetails(parsedPackage, false /* skipVerify */));
            }
        } catch (PackageParserException e) {
            throw new PrepareFailure("Failed collect during installPackageLI", e);
        }

        //未使用v2簽名
        if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion
                < SignatureSchemeVersion.SIGNING_BLOCK_V2) {
            ...
        }

        boolean systemApp = false;
        boolean replace = false;
        synchronized (mLock) {
            // 檢查是否已安裝現有軟體包
            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
            ...
              }
      
            PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);

                // 將簽名與最新庫版本的包設定進行比較,
                PackageSetting signatureCheckPs = ps;
                if (parsedPackage.isStaticSharedLibrary()) {
                    ...
                }

                //注釋2:快速校驗安裝包簽名
                final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
                    if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
                        throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                + parsedPackage.getPackageName() + " upgrade keys do not match the "
                                + "previously installed version");
                    }
                } else {
                    ...
                }

                if (ps.pkg != null) {
                    systemApp = ps.pkg.isSystem();
                }
                res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
            }


            // 注釋3:設定相關權限,生成、移植權限 
            int N = ArrayUtils.size(parsedPackage.getPermissions());
            for (int i = N - 1; i >= 0; i--) {
                final ParsedPermission perm = parsedPackage.getPermissions().get(i);
                final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());

                ...
            }
        }
        if (args.move != null) {
            // 我們進行了就地移動,因此 dex 已準備就緒
            ...
        } else {
            // 啟用 SCAN_NO_DEX 標志以在稍后階段跳過 dexopt
            scanFlags |= SCAN_NO_DEX;

            try {
                final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
                PackageSetting pkgSetting;
                synchronized (mLock) {
                    pkgSetting = mSettings.getPackageLPr(pkgName);
                }
                // 注釋4:生成安裝包Abi(Application binary interface,應用二進制介面)
                String abiOverride =
                        (pkgSetting == null || TextUtils.isEmpty(pkgSetting.cpuAbiOverrideString)
                        ? args.abiOverride : pkgSetting.cpuAbiOverrideString);
                boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
                        && pkgSetting.getPkgState().isUpdatedSystemApp();
                AndroidPackage oldPackage = mPackages.get(pkgName);
                boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
                final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
                        derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
                        isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
                        abiOverride, extractNativeLibs);
                derivedAbi.first.applyTo(parsedPackage);
                derivedAbi.second.applyTo(parsedPackage);
            } catch (PackageManagerException pme) {
                Slog.e(TAG, "Error deriving application ABI", pme);
                throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
                        "Error deriving application ABI");
            }
        }
        //注釋5:凍結 安裝包
        final PackageFreezer freezer =
                freezePackageForInstall(pkgName, installFlags, "installPackageLI");
        boolean shouldCloseFreezerBeforeReturn = true;
        try {
            final AndroidPackage existingPackage;
            String renamedPackage = null;
            boolean sysPkg = false;
            int targetScanFlags = scanFlags;
            int targetParseFlags = parseFlags;
            final PackageSetting ps;
            final PackageSetting disabledPs;
            if (replace) {
                //更新包
                ...
            } else { 
                // 新包安裝
                ...
            }
            // 解凍 安裝包
            shouldCloseFreezerBeforeReturn = false;

            //回傳成功準備好的安裝包所需的資料集,
            return new PrepareResult(replace, targetScanFlags, targetParseFlags,
                    existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
                    ps, disabledPs);
        } finally {
            res.freezer = freezer;
            if (shouldCloseFreezerBeforeReturn) {
                freezer.close();
            }
        }
    }

咱再回來看 installPackagesLI()

installPackagesLI()

    @GuardedBy("mInstallLock")
    private void installPackagesLI(List<InstallRequest> requests) {
        ...
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
            for (InstallRequest request : requests) {
                final PrepareResult prepareResult;
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
                    //注釋1
                    //成功準備好的安裝軟體包所需的資料集,這包括將用于掃描和協調包的資料,
                    prepareResult =
                            preparePackageLI(request.args, request.installResult);
                ...
                try {
                    //掃描包并回傳新決議的包,
                    //出現錯誤時回傳null,
                    final ScanResult result = scanPackageTracedLI(
                            prepareResult.packageToScan, prepareResult.parseFlags,
                            prepareResult.scanFlags, System.currentTimeMillis(),
                            request.args.user, request.args.abiOverride);
                    ...
                } catch (PackageManagerException e) {
                    request.installResult.setError("Scanning Failed.", e);
                    return;
                }
            }
            //包掃描結果和相關請求詳細資訊用于協調一個或多個包到系統的潛在添加,
            ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
                    installResults,
                    prepareResults,
                    mSharedLibraries,
                    Collections.unmodifiableMap(mPackages), versionInfos,
                    lastStaticSharedLibSettings);
            CommitRequest commitRequest = null;
            synchronized (mLock) {
                //ReconciledPackage:提交到記憶體中的資料結構和磁盤所需的所有資料的容器,
                //TODO:將此處包含的大部分資料移動到PackageSetting中進行提交,
                
                Map<String, ReconciledPackage> reconciledPackages;
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                    reconciledPackages = reconcilePackagesLocked(
                            reconcileRequest, mSettings.mKeySetManagerService);
                } catch (ReconcileFailure e) {
                    for (InstallRequest request : requests) {
                        request.installResult.setError("Reconciliation failed...", e);
                    }
                    return;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
                    commitRequest = new CommitRequest(reconciledPackages,
                            mUserManager.getUserIds());
                    commitPackagesLocked(commitRequest);
                    success = true;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
            }
            //注釋2
            executePostCommitSteps(commitRequest);
        } finally {
            ...
        }
    }

注釋2:這里有個 executePostCommitSteps() 方法,咱們一起來看看,

executePostCommitSteps()

    /**
     * 成功安裝后,在提交完成并釋放包鎖后執行剩余步驟, 
     * 這些通常更昂貴或需要呼叫 installd,這通常會鎖定 mLock,
     */
    private void executePostCommitSteps(CommitRequest commitRequest) {
        final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
        for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
            final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
                            & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
            
            final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
            final String packageName = pkg.getPackageName();
            //注釋1:安裝并準備 APP 資料 
            prepareAppDataAfterInstallLIF(pkg);
            ...
            final boolean performDexopt =
                    (!instantApp || Global.getInt(mContext.getContentResolver(),
                    Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                    && !pkg.isDebuggable()
                    && (!onIncremental)
                    && dexoptOptions.isCompilationEnabled();

              //優化dex檔案(實際為 dex2oat 操作,用來將 apk 中的 dex 檔案轉換為 oat 檔案)
            if (performDexopt) {
                // Compile the layout resources.
                if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
                    mViewCompiler.compileLayouts(pkg);
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }

                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
                ScanResult result = reconciledPkg.scanResult;


                PackageSetting realPkgSetting = result.existingSettingCopied
                        ? result.request.pkgSetting : result.pkgSetting;
                if (realPkgSetting == null) {
                    realPkgSetting = reconciledPkg.pkgSetting;
                }

                // Unfortunately, the updated system app flag is only tracked on this PackageSetting
                boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState()
                        .isUpdatedSystemApp();

                realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);

                mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
                        null /* instructionSets */,
                        getOrCreateCompilerPackageStats(pkg),
                        mDexManager.getPackageUseInfoOrDefault(packageName),
                        dexoptOptions);
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }

            // Notify BackgroundDexOptService that the package has been changed.
            // If this is an update of a package which used to fail to compile,
            // BackgroundDexOptService will remove it from its denylist.
            // TODO: Layering violation
            BackgroundDexOptService.notifyPackageChanged(packageName);

            notifyPackageChangeObserversOnUpdate(reconciledPkg);
        }
        NativeLibraryHelper.waitForNativeBinariesExtraction(incrementalStorages);
    }

注釋1:安裝并準備 APP 資料,咱們來看看 prepareAppDataAfterInstallLIF(pkg);

prepareAppDataAfterInstallLIF()

    /**
     * 在安裝或升級后立即為給定的應用程式準備應用程式資料, 
     * 此方法僅會小心地接觸為其安裝的用戶,并強制 restorecon 處理任何 seinfo 更改,
     * 
     * 驗證目錄是否存在以及所有已安裝的應用程式的所有權和標簽是否正確,
     * 如果所有權不匹配,它將嘗試通過擦除資料來恢復系統應用程式; 第三方應用程式資料保持不變,
     * 
     * 注意:為了避免死鎖,不要在持有mLock鎖的情況下呼叫這個方法
     */
    private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
        final PackageSetting ps;
        synchronized (mLock) {
            ps = mSettings.mPackages.get(pkg.getPackageName());
            mSettings.writeKernelMappingLPr(ps);
        }

        UserManagerInternal umInternal = mInjector.getUserManagerInternal();
        StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
        for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
            final int flags;
            if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
            } else if (umInternal.isUserRunning(user.id)) {
                flags = StorageManager.FLAG_STORAGE_DE;
            } else {
                continue;
            }

            if (ps.getInstalled(user.id)) {
                // TODO:這里是我們關注的,咱們接著看
                prepareAppDataLIF(pkg, user.id, flags);

                if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
                    // 在外部存盤上準備應用程式資料;
                    // 目前這用于設定由安裝程式正確創建的任何 OBB 目錄,
                    int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
                    smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
                }
            }
        }
    }
    

prepareAppDataLIF()

    private void prepareAppDataLIF(AndroidPackage pkg, int userId, int flags) {
        if (pkg == null) {
            Slog.wtf(TAG, "Package was null!", new Throwable());
            return;
        }
        prepareAppDataLeafLIF(pkg, userId, flags);
    }

prepareAppDataLeafLIF()

    private void prepareAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
        if (DEBUG_APP_DATA) {
            Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x"
                    + Integer.toHexString(flags));
        }

        final PackageSetting ps;
        synchronized (mLock) {
            ps = mSettings.mPackages.get(pkg.getPackageName());
        }
        final String volumeUuid = pkg.getVolumeUuid();
        final String packageName = pkg.getPackageName();

        final int appId = UserHandle.getAppId(pkg.getUid());

        String pkgSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);

        Preconditions.checkNotNull(pkgSeInfo);

        final String seInfo = pkgSeInfo + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : "");
        long ceDataInode = -1;
        try {
            ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                    appId, seInfo, pkg.getTargetSdkVersion());
        } catch (InstallerException e) {
            if (pkg.isSystem()) {
                logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
                        + ", but trying to recover: " + e);
                destroyAppDataLeafLIF(pkg, userId, flags);
                try {
                    //注釋1: 最終呼叫 系統服務 Installer 安裝
                    ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                            appId, seInfo, pkg.getTargetSdkVersion());
                    logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
                } catch (InstallerException e2) {
                    logCriticalInfo(Log.DEBUG, "Recovery failed!");
                }
            } else {
                Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
            }
        }
        
        if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
            mArtManagerService.prepareAppProfiles(pkg, userId,
                /* updateReferenceProfileContent= */ false);
        }

        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
            // TODO: mark this structure as dirty so we persist it!
            synchronized (mLock) {
                if (ps != null) {
                    ps.setCeDataInode(ceDataInode, userId);
                }
            }
        }

        prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
    }

注釋1:mInstaller

    // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
    // LOCK HELD.  Can be called with mInstallLock held.
    @GuardedBy("mInstallLock")
    final Installer mInstaller;

咱們看看 Installer

Installer

public class Installer extends SystemService {
    ...
    private volatile IInstalld mInstalld;
    public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
            String seInfo, int targetSdkVersion) throws InstallerException {
        if (!checkBeforeRemote()) return -1;
        try {
            return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
                    targetSdkVersion);
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }  
    ...
}

呼叫 系統服務 Installer 安裝

至此整個 apk 的安裝程序結束,實際上安裝成功之后,還會發送一個 App 安裝成功的廣播 ACTION_PACKAGE_ADDED,手機桌面應用注冊了這個廣播,當接收到應用安裝成功之后,就將 apk 的啟動 icon 顯示在桌面上,

流程圖

相關推薦

Android Gradle 詳解

Android apk 包體積優化

Android Apk 的打包程序

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/302825.html

標籤:其他

上一篇:知道了螞蟻森林這個“秘密”,我坐不住了!!

下一篇:Android從入門到進階之高級控制元件

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more