Android app安裝過程分析(基于Nougat)

首先我們要清楚總體可以分成如下幾種安裝的情況

  • 系統(tǒng)開機(jī)的應(yīng)用安裝,安裝的是系統(tǒng)級別的應(yīng)用,用戶在沒有獲取到root權(quán)限的情況下無法卸載的應(yīng)用
  • adb安裝的應(yīng)用,沒有安裝界面
  • 第三方市場下載的應(yīng)用,此處要分情況,部分是通過電腦的客戶端安裝的沒有安裝的界面,部分是手機(jī)上的市場安裝的,會(huì)有安裝的界面

那么我們就對幾種情況一一分析它的安裝流程

開機(jī)安裝

  1. 首先在開機(jī)的時(shí)候systemServer會(huì)啟動(dòng)PackageMangerService來

    具體是通過systemServer的main()->init1()->init2()->new ServerThread()->構(gòu)建PMS

    以上都是SystemServer.cpp中的代碼,了解即可。我們前往PMS查看接下去的邏輯:在PMS的構(gòu)造方法中我們看到了一大坨的邏輯
    ,源碼過于長,我們直接在構(gòu)造方法里面搜索如下這兩個(gè)方法:

scanDirTracedLI   //最后還是會(huì)調(diào)用下面的方法,只是對下面的方法做一下跟蹤
scanDirLI

就是通過這兩個(gè)方法進(jìn)行應(yīng)用的安裝,從scanDirLI開始追蹤:

private void scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
        final File[] files = dir.listFiles();
        ...
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            try {
                scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                        scanFlags, currentTime, null);
            } catch (PackageManagerException e) {
                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

                // Delete invalid userdata apps
                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                        e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                    logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                    removeCodePathLI(file);
                }
            }
        }
    }

繼續(xù)看scanPackageTracedLI方法:

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        pp.setDisplayMetrics(mMetrics);

        if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
            parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
        }

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
        final PackageParser.Package pkg;
        try {
            pkg = pp.parsePackage(scanFile, parseFlags);
        } catch (PackageParserException e) {
            throw PackageManagerException.from(e);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }

        return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
    }

我們看到從這個(gè)方法開始parsePackage, 通過PackageParser解析apk文件,我們可以去到PackageParser的文件中看到這個(gè)parsePackage方法就是把安裝包中的配置文件解析出來,并且保存到Package中返回出來,中間輾轉(zhuǎn)反側(cè)對于配置文件各種分情況進(jìn)行parse,最最主要的是parseBaseApkCommon方法,這個(gè)方法對manifast文件的標(biāo)簽進(jìn)行解析,而其中還調(diào)用了parseBaseApplication方法對application標(biāo)簽進(jìn)行解析。

然后再scanPackageLI->scanPackageDirtyLI,在后者的方法中,我們會(huì)把之前解析出來的

PackageParser.Provider p = pkg.providers.get(i);
                p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        p.info.processName, pkg.applicationInfo.uid);
                mProviders.addProvider(p);
PackageParser.Service s = pkg.services.get(i);
                s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        s.info.processName, pkg.applicationInfo.uid);
                mServices.addService(s);

等等,我們會(huì)把相應(yīng)的provider,service,receiver,activity全都保存到PMS的成員集合類中去.

至此系統(tǒng)應(yīng)用的安裝算是全部完成了,也許你會(huì)說我們并有看到什么install的什么方法,其實(shí)最最精髓的就是上面最后那一坨1000+行的方法,這里面把所有parser出來的信息全部保存到PMS中去了,而這就是所謂的安裝,安裝就是一個(gè)吧apk中的信息解析出來保存給PMS的過程,然后在launcher上生成一個(gè)圖標(biāo),以供用戶打開,僅此而已。


從網(wǎng)絡(luò)上下載應(yīng)用安裝

最終都是通過如下的方式去進(jìn)行安裝的

String fileName = "/mnt/usb/sda4/test.apk";
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
startActivity(intent);

啟動(dòng)的就是PackageInstallerActivity,而這個(gè)activity是Android的系統(tǒng)應(yīng)用之一,源碼在package/app/packageinstaller中,具體源碼可以前往下面鏈接查看:
https://github.com/android/platform_packages_apps_packageinstaller/blob/master/src/com/android/packageinstaller/PackageInstallerActivity.java
我們在其onCreate方法中看到之后會(huì)調(diào)用checkIfAllowedAndInitiateInstall->initiateInstall-> startInstallConfirm,會(huì)處理有關(guān)權(quán)限的一些問題

之后如果我們點(diǎn)擊下方的ok按鈕,會(huì)進(jìn)入到安裝流程:

 public void onClick(View v) {
        if (v == mOk) {
            if (mOkCanInstall || mScrollView == null) {
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, true);
                    clearCachedApkIfNeededAndFinish();
                } else {
                    startInstall();
                }
            } else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        } else if (v == mCancel) {
           ...
    }

我們接著去startInstall方法看:

private void startInstall() {
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();
        ...
        newIntent.setClass(this, InstallAppProgress.class);
        ...
        startActivity(newIntent);
        finish();
    }

跳到InstallAppProgress這個(gè)activity,很明顯就是我們點(diǎn)擊安裝之后進(jìn)入的安裝界面,有個(gè)進(jìn)度條一直在閃的那個(gè)正在安裝的界面。
onCreate()->initView():

void initView() {
        setContentView(R.layout.op_progress);

        ...

        if ("package".equals(mPackageURI.getScheme())) {
            try {
                pm.installExistingPackage(mAppInfo.packageName);
                onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
            } catch (PackageManager.NameNotFoundException e) {
                onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
            }
        } else {
           ...

            mInstallHandler.post(new Runnable() {
                @Override
                public void run() {
                    doPackageStage(pm, params);
                }
            });
        }
    }

精簡了一下,主要是這兩個(gè)方法。

installExistingPackage

一個(gè)是pm.installExistingPackage,我們知道實(shí)際上調(diào)用的就是ApplicationPackageManager的installExistingPackage


    @Override
    public int installExistingPackage(String packageName) throws NameNotFoundException {
        return installExistingPackageAsUser(packageName, mContext.getUserId());
    }

    @Override
    public int installExistingPackageAsUser(String packageName, int userId)
            throws NameNotFoundException {
        try {
            int res = mPM.installExistingPackageAsUser(packageName, userId);
            if (res == INSTALL_FAILED_INVALID_URI) {
                throw new NameNotFoundException("Package " + packageName + " doesn't exist");
            }
            return res;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

繼續(xù),我們前往PMS的installExistingPackageAsUser方法,具體方法不貼出,實(shí)際的意思就是這個(gè)安裝包已經(jīng)裝過了,只是當(dāng)前用戶下還沒有安裝,就會(huì)對后臺(tái)的設(shè)置做一下處理,實(shí)際并不涉及到安裝的東西的。我們來看另外一個(gè)方法

doPackageStage

private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
        final PackageInstaller packageInstaller = pm.getPackageInstaller();
        PackageInstaller.Session session = null;
        try {
            session = packageInstaller.openSession(sessionId);
            ...
            session.commit(pendingIntent.getIntentSender());
        } catch (IOException e) {
            onPackageInstalled(PackageInstaller.STATUS_FAILURE);
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

當(dāng)中對session做了很多操作,我們只看最主要的最后一個(gè)session.commit,

  • PackageInstaller.Session
    這個(gè)session是在5.0之后新加進(jìn)來的一種安裝處理的方式,我們可以把它理解為一種記錄,就和瀏覽器中的session的作用是一樣的,它用來保存應(yīng)用安裝的所有信息,當(dāng)我們的手機(jī)如果因?yàn)楫惓V话惭b了一半退出了之后,當(dāng)我們下回再次打開手機(jī)的時(shí)候,可以繼續(xù)上次的安裝。

ok,我們接著進(jìn)到PackageInstaller.java看session的commit方法

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

這里的mSession我們看到是

        private IPackageInstallerSession mSession;

 /** {@hide} */
        public Session(IPackageInstallerSession session) {
            mSession = session;
        }

萬惡的binder,回到上面看session的定義:

session = packageInstaller.openSession(sessionId);

找openSession方法

public @NonNull Session openSession(int sessionId) throws IOException {
        try {
            return new Session(mInstaller.openSession(sessionId));
        } catch (RuntimeException e) {
            ExceptionUtils.maybeUnwrapIOException(e);
            throw e;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

mInstaller是什么,

    private final IPackageInstaller mInstaller;

又來一個(gè)binder,我們就不繼續(xù)一步一步貼了,相信讀者也可以自己找到最終的實(shí)現(xiàn)是在PackageInstallerSession

@Override
    public void commit(IntentSender statusReceiver) {
        ...
        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
                statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
        mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
    }

省去部分直接看到最后給handler發(fā)了條消息

        mHandler = new Handler(looper, mHandlerCallback);

private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // Cache package manager data without the lock held
            final PackageInfo pkgInfo = mPm.getPackageInfo(
                    params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
            final ApplicationInfo appInfo = mPm.getApplicationInfo(
                    params.appPackageName, 0, userId);

            synchronized (mLock) {
                if (msg.obj != null) {
                    mRemoteObserver = (IPackageInstallObserver2) msg.obj;
                }

                try {
                    commitLocked(pkgInfo, appInfo);
                } catch (PackageManagerException e) {
                    final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                    Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                    destroyInternal();
                    dispatchSessionFinished(e.error, completeMsg, null);
                }

                return true;
            }
        }
    };

重要的就是那句commitLocked,這個(gè)方法比較長,依然是做一些權(quán)限和配置方面的事情,能在方法的最后看到

mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
                installerPackageName, installerUid, user, mCertificates);

我們?nèi)サ絇MS的installStage方法:

void installStage(String packageName, File stagedDir, String stagedCid,
            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
            String installerPackageName, int installerUid, UserHandle user,
            Certificate[][] certificates) {
        ...
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final InstallParams params = new InstallParams(origin, null, observer,
                sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                verificationInfo, user, sessionParams.abiOverride,
                sessionParams.grantedRuntimePermissions, certificates);
        ...
        msg.obj = params;
        ...
        mHandler.sendMessage(msg);
    }

我們把整個(gè)過程精簡到上面三局代碼,發(fā)送一個(gè)what為INIT_COPY的消息

case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    int idx = mPendingInstalls.size();
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                    // 此處為了連接服務(wù),初始情況都為false,待服務(wù)臉上之后就會(huì)把mBound設(shè)置為true,就可以進(jìn)入下面條件
                    // 因此這里我們直接進(jìn)入else的條件
                    if (!mBound) {
                        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                                System.identityHashCode(mHandler));
                        // If this is the only one pending we might
                        // have to bind to the service again.
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            params.serviceError();
                            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                                    System.identityHashCode(mHandler));
                            if (params.traceMethod != null) {
                                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
                                        params.traceCookie);
                            }
                            return;
                        } else {
                            // Once we bind to the service, the first
                            // pending request will be processed.
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                        mPendingInstalls.add(idx, params);
                        // Already bound to the service. Just make
                        // sure we trigger off processing the first request.
                        if (idx == 0) {
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }

直接在最后看到發(fā)送了MCS_BOUND消息

 case MCS_BOUND: {
                    ...
                    //服務(wù)已經(jīng)連上,直接跳到else if
                    if (mContainerService == null) {
                        ...
                    } 
                    //等待安裝的隊(duì)列肯定大于0
                    else if (mPendingInstalls.size() > 0) {
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                    System.identityHashCode(params));
                            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                            //開始進(jìn)入到copy
                            if (params.startCopy()) {
                                // We are done...  look for more work or to
                                // go idle.
                                if (DEBUG_SD_INSTALL) Log.i(TAG,
                                        "Checking for more work or unbind...");
                                // Delete pending install
                                if (mPendingInstalls.size() > 0) {
                                    mPendingInstalls.remove(0);
                                }
                                //如果已經(jīng)把要安裝的都處理完了,那么久斷開服務(wù)連接
                                //否則就再發(fā)送一個(gè)MCS_BOUND信息,繼續(xù)循環(huán)處理
                                if (mPendingInstalls.size() == 0) {
                                    if (mBound) {
                                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                "Posting delayed MCS_UNBIND");
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                        // Unbind after a little delay, to avoid
                                        // continual thrashing.
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                    // There are more pending requests in queue.
                                    // Just post MCS_BOUND message to trigger processing
                                    // of next pending install.
                                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                                            "Posting MCS_BOUND for next work");
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                        }
                    } else {
                        // Should never happen ideally.
                        Slog.w(TAG, "Empty queue");
                    }
                    break;
                }

我們直接進(jìn)入到startCopy

final boolean startCopy() {
            boolean res;
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
                //嘗試的次數(shù)超過了最大嘗試次數(shù),就宣告失敗
                if (++mRetries > MAX_RETRIES) {
                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
                    handleServiceError();
                    return false;
                } else {
                    //開始進(jìn)行拷貝
                    handleStartCopy();
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            //處理return code
            handleReturnCode();
            return res;
        }

我們先來看一下handleStartCopy方法,發(fā)現(xiàn)時(shí)抽象方法,具體的實(shí)現(xiàn),我們網(wǎng)上可以找到是 InstallParams。

public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;
    // 決定是安裝在手機(jī)內(nèi)還是sdcard中,設(shè)置對應(yīng)標(biāo)志位
    if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
        } else if (origin.cid != null) {
            installFlags |= PackageManager.INSTALL_EXTERNAL;
            installFlags &= ~PackageManager.INSTALL_INTERNAL;
        } else {
            throw new IllegalStateException("Invalid stage location");
        }
    }
    ...
    // 檢查APK的安裝位置是否正確
    if (onInt && onSd) {
        // Check if both bits are set.
        Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else if (onSd && ephemeral) {
        Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else {
        ...
    }
    ...
    // createInstallArgs用于創(chuàng)建一個(gè)安裝參數(shù)對象
    final InstallArgs args = createInstallArgs(this);
    
    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        ...
            // 調(diào)用InstallArgs的copyApk函數(shù)
            ret = args.copyApk(mContainerService, true);
        }
    }
    mRet = ret;
}

主要工作就是復(fù)制apk消息到指定位置
再來看handleReturnCode

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

我們一路看,processPendingInstall()->installPackageTracedLI()->installPackageLI(),整個(gè)方法比較長,主要做了清單文件解析和獲取證書,提取簽名的工作,你可以從這個(gè)方法中找到如下代碼段,就是這段代碼處理了安裝


    try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
            "installPackageLI")) {
        if (replace) {
            // 4.更新已經(jīng)存在的packages
            replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                    installerPackageName, res);
        } else {
            // 5.安裝新的packages
            installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                    args.user, installerPackageName, volumeUuid, res);
        }
    }

我們繼續(xù)

/*
     * Install a non-existing package.
     */
    private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
            int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
            PackageInstalledInfo res) {
        ...
        try {
            PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
                    System.currentTimeMillis(), user);

            updateSettingsLI(newPackage, installerPackageName, null, res, user);

            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                prepareAppDataAfterInstallLIF(newPackage);

            } else {
                // Remove package from internal structures, but keep around any
                // data that might have already existed
                deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                        PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
            }
        } catch (PackageManagerException e) {
            res.setError("Package couldn't be installed in " + pkg.codePath, e);
        }
        ...
    }

經(jīng)過刪減省略,我們看到了我們熟悉的方法scanPackageTracedLI,再具體的步驟見上面系統(tǒng)應(yīng)用安裝。


最后我們再來看一下另外一種ADB的安裝

adb安裝

當(dāng)我們在在命令行窗口輸入adb install的時(shí)候?qū)嶋H上,系統(tǒng)會(huì)幫我們運(yùn)行如下的程序

db_commandline
    install_app_legacy or install_app 
        pm_command
            send_shell_command
                Pm.runInstall()

這個(gè)過程會(huì)把a(bǔ)pk文件copy到data/local/tmp/目錄下,然后向shell服務(wù)發(fā)送pm命令安裝apk,最后調(diào)用Pm.runInstall()方法來安裝apk。
我們可以看到這個(gè)方法:

private int runInstall() throws RemoteException {
        ...
        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);

        try {
            if (inPath == null && params.sessionParams.sizeBytes == 0) {
                System.err.println("Error: must either specify a package size or an APK file");
                return 1;
            }
            if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            if (doCommitSession(sessionId, false /*logSuccess*/)
                    != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            System.out.println("Success");
            return 0;
        } finally {
            try {
                mInstaller.abandonSession(sessionId);
            } catch (Exception ignore) {
            }
        }
    }

我們依然省略著看,把上面這一段略去,直接在最下面看到三個(gè)重要的方法,doCreateSession,doWriteSession,doCommitSession,直接點(diǎn)開最后一個(gè)

private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
        PackageInstaller.Session session = null;
        try {
            session = new PackageInstaller.Session(
                    mInstaller.openSession(sessionId));

            final LocalIntentReceiver receiver = new LocalIntentReceiver();
            session.commit(receiver.getIntentSender());

            final Intent result = receiver.getResult();
            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                    PackageInstaller.STATUS_FAILURE);
            if (status == PackageInstaller.STATUS_SUCCESS) {
                if (logSuccess) {
                    System.out.println("Success");
                }
            } else {
                System.err.println("Failure ["
                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
            }
            return status;
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

看到熟悉的session.commit了么,


總結(jié)

下面是有安裝界面的app安裝流程,中途插入adb install以及沒有安裝界面的系統(tǒng)安裝。

  • PackageInstallerActivity的onCreate();
  • 根據(jù)從Intent中獲取的Data的scheme的不同,調(diào)用不同的處理邏輯安裝App;
  • 解析App文件到Package對象;
  • initInstall()函數(shù)獲取apk中的權(quán)限信息;
  • onClick()監(jiān)聽用戶點(diǎn)擊同意安裝按鈕;
  • 啟動(dòng)InstallAppProcess Activity;
  • onCrete()函數(shù)調(diào)用
  • initView()函數(shù);
  • 繼續(xù)調(diào)用doPackageStage()函數(shù),做session.commit();
    此處插入adb install 1.Pm.runInstall() 2.Pm.doCommitSession()
  • 通過Handler機(jī)制啟動(dòng)了startCopy()函數(shù);
  • startCopy()調(diào)用了啟動(dòng)了handlerStartCopy()將apk拷貝到/data/app、pkg-name/和把.so文件拷貝到/data/app/pkg-name/lib/的
  • handlerReturnCode()函數(shù);
  • handlerReturnCode調(diào)用了installPackageLI()函數(shù);
  • 在installPackageLI()中,首先調(diào)用了collectCertificates()函數(shù)對apk的簽名做了校驗(yàn),確保apk沒有被非法修改(修改其中的文件);
    接著,調(diào)用collectManifestDigest()函數(shù)計(jì)算了Manifest.xml的文件摘要,并且存在了Package的manifestDigest成員下;
    接著,將從apk文件中提提取的manifest計(jì)算摘要和之前installer解析到的進(jìn)行比較,若不匹配,則拋出異常。
    然后,對App升級或者使用了共享屬性sharedUid的情況校驗(yàn)待安裝的App的證書是否匹配,并就升級方式做了具體處理,這里有兩個(gè)問題,就是升級過程中的Upgrade keysets升級方式和Signature[]的獲得不明朗。
  • 根據(jù)是升級App還是安裝新App,調(diào)用不同的函數(shù)進(jìn)行處理環(huán)節(jié)。
    此處插入系統(tǒng)應(yīng)用安裝 1.scanPackageTracedLI 2.scanPackageLI 3.scanPackageDirtyLI
  • 最后在scanPackageDirtyLI方法中,把所有的provider,service,receiver,activity從parser中拿出來保存到PMS中。

以上!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,001評論 25 709
  • APK安裝流程系列文章整體內(nèi)容如下: APK安裝流程詳解0——前言APK安裝流程詳解1——有關(guān)"安裝ing"的實(shí)體...
    隔壁老李頭閱讀 9,423評論 4 15
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,547評論 19 139
  • 從復(fù)興門橋到建國門橋 穿過長安街 走遍日壇路的使館區(qū) 第一次騎單車連續(xù)兩個(gè)小時(shí) 假裝欣賞美景 用運(yùn)動(dòng)讓身體疲勞 從...
    霖山閱讀 2,368評論 67 132

友情鏈接更多精彩內(nèi)容