Android Framework

Android 包管理机制---PackageInstaller

2021-08-02  本文已影响0人  我看Android

本文基于API 30即Android11源码解读

前些日子做了一个电视机的应用市场APP,搞了一下Android的应用管理机制。撸篇文章记录一下。

一、安装应用 PackageInstaller初始化

    /**
     * 安装Apk
     * 非静默安装
     */
    private fun installApk(context: Context, path: String){
        val intent =Intent(Intent.ACTION_VIEW)
        val file=File(path)
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
            //7.0版本及以上
            val apkUri=FileProvider.getUriForFile(context,"包名.provider",file)
            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
            intent.setDataAndType(apkUri,"application/vnd.android.package-archive")
        }else{
            //7.0版本以下
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            val uri=Uri.fromFile(file)
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
        }
        context.startActivity(intent)
    }

由于StrictMode API 政策,我们在7.0及以上不能再使用file:// Uri形式进行应用安装。但无论7.0以上还是一下,我们都调用了

      val intent =Intent(Intent.ACTION_VIEW)
      context.startActivity(intent)

Intent.ACTION_VIEW在7.0隐式匹配的是Activity为PackageInstallerActivity,
8.0及以上隐式匹配的是Activity为InstallStart,从调用startActivity()方法我们可以认为应用安装的入口是拉起一个Activity界面。我们这里查看的是Android11.0的源码,那么便进入InstallStart的onCreate()方法看一看

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mIPackageManager = AppGlobals.getPackageManager();
        mIPermissionManager = AppGlobals.getPermissionManager();
        mUserManager = getSystemService(UserManager.class);
        
        ......

        final boolean isSessionInstall =
                PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());

        ......
        ......

        if (isSessionInstall) {
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();

            if (packageUri != null && packageUri.getScheme().equals(
                    ContentResolver.SCHEME_CONTENT)) {
                // [IMPORTANT] This path is deprecated, but should still work. Only necessary
                // features should be added.

                // Copy file to prevent it from being changed underneath this process
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(
                    PackageInstallerActivity.SCHEME_PACKAGE)) {
                nextActivity.setClass(this, PackageInstallerActivity.class);
            } else {
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            }
        }

        if (nextActivity != null) {
            startActivity(nextActivity);
        }
        finish();
    }

这里首先会判断intent传进来的action是否为CONFIRM_INSTALL,根据我们方法参数传入情况显然不是。然后对packageUri 的Scheme进行判断。如果是content就跳转到InstallStaging,如果是package就跳转到PackageInstallerActivity,否则就置空。在Android7.0及以上安装应用,我们都会使用FileProvider来处理URI ,FileProvider会隐藏文件真实路径,将路径转换成content://Uri路径,这样就满足packageUri Scheme协议匹配content的条件,后续跳转进入InstallStaging界面。
在InstallStagingon的核心逻辑它的onResume()方法执行

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

        // This is the first onResume in a single life of the activity
        if (mStagingTask == null) {
            // File does not exist, or became invalid
            if (mStagedFile == null) {
                // Create file delayed to be able to show error
                try {
                    mStagedFile = TemporaryFileManager.getStagedFile(this);
                } catch (IOException e) {
                    showError();
                    return;
                }
            }

            mStagingTask = new StagingAsyncTask();
            mStagingTask.execute(getIntent().getData());
        }
    }

    private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return false;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
                // Despite the comments in ContentResolver#openInputStream the returned stream can
                // be null.
                if (in == null) {
                    return false;
                }

                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[1024 * 1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        // Be nice and respond to a cancellation
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException | IllegalStateException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean success) {
            if (success) {
                // Now start the installation again from a file
                Intent installIntent = new Intent(getIntent());
                installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
                installIntent.setData(Uri.fromFile(mStagedFile));

                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                }

                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivity(installIntent);

                InstallStaging.this.finish();
            } else {
                showError();
            }
        }
    }

onResume()方法其实就是执行了一个StagingAsyncTask任务,在StagingAsyncTask的doInBackground(Uri... params)方法中会将packageUri协议写到mStagedFile中,然后再onPostExecute(Boolean success)方法中执行对DeleteStagedFileOnResult的跳转,并将mStagedFile传入,在DeleteStagedFileOnResult中执行了对PackageInstallerActivity的跳转,执行一圈,最终还是回到了PackageInstallerActivity类

    @Override
    protected void onCreate(Bundle icicle) {
        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);

        super.onCreate(null);

        ......
        ......

      
        boolean wasSetUp = processPackageUri(packageUri);
        if (!wasSetUp) {
            return;
        }

        // load dummy layout with OK button disabled until we override this layout in
        // startInstallConfirm
        bindUi();
        checkIfAllowedAndInitiateInstall();
    }

在PackageInstallerActivity的onCreate(Bundle icicle)方法中会调用processPackageUri()方法对packageUri进行预处理, bindUi()方法进行应用安装界面初始化 ,checkIfAllowedAndInitiateInstall()方法用来判断是否是未知来源的应用,如果开启允许安装未知来源选项则直接初始化安装

    private void checkIfAllowedAndInitiateInstall() {
        // Check for install apps user restriction first.
        final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
                UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
        if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
            showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
            return;
        } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
            startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
            finish();
            return;
        }
        //如果允许安装,就进入安装流程
        if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
            //调用安装方法
            initiateInstall();
        } else {
            // Check for unknown sources restrictions.
            final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
            final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
            final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
                    & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
            if (systemRestriction != 0) {
                showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
            } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
                startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
            } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
                startAdminSupportDetailsActivity(
                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
            } else {
                handleUnknownSources();
            }
        }
    }
    
    
    private void initiateInstall() {
        String pkgName = mPkgInfo.packageName;
        // Check if there is already a package on the device with this name
        // but it has been renamed to something else.
        String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
            pkgName = oldName[0];
            mPkgInfo.packageName = pkgName;
            mPkgInfo.applicationInfo.packageName = pkgName;
        }
        // Check if package is already installed. display confirmation dialog if replacing pkg
        try {
            // This is a little convoluted because we want to get all uninstalled
            // apps, but this may include apps with just data, and if it is just
            // data we still want to count it as "installed".
            mAppInfo = mPm.getApplicationInfo(pkgName,
                    PackageManager.MATCH_UNINSTALLED_PACKAGES);
            if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
                mAppInfo = null;
            }
        } catch (NameNotFoundException e) {
            mAppInfo = null;
        }
         //设置安装确认界面可见
        startInstallConfirm();
    }
    
     
        private void startInstallConfirm() {
        View viewToEnable;

        if (mAppInfo != null) {
            viewToEnable = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                    ? requireViewById(R.id.install_confirm_question_update_system)
                    : requireViewById(R.id.install_confirm_question_update);
        } else {
            // This is a new application with no permissions.
            viewToEnable = requireViewById(R.id.install_confirm_question);
        }

        viewToEnable.setVisibility(View.VISIBLE);

        mEnableOk = true;
        mOk.setEnabled(true);
        mOk.setFilterTouchesWhenObscured(true);
    }

在initiateInstall()方法中,mPm.getApplicationInfo(pkgName,
PackageManager.MATCH_UNINSTALLED_PACKAGES)会通过包名拿到应用信息。在startInstallConfirm()方法中会通过 viewToEnable.setVisibility(View.VISIBLE)使确认界面对用户可见,执行到这个阶段PackageInstaller的初始化工作便完成了

二、APK的验证拷贝

在第一阶段对PackageInstaller 完成了初始化工作并拉起安装确认界面,那么第二阶段便从安装确认界面的确认按钮点击事件开始

    private void bindUi() {
  
    ......
    ......
        mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
                (ignored, ignored2) -> {
                    if (mOk.isEnabled()) {
                        if (mSessionId != -1) {
                            mInstaller.setPermissionsResult(mSessionId, true);
                            finish();
                        } else {
                            startInstall();
                        }
                    }
                }, null);
    ......
    ......  

    }

mSessionId 是在PackageInstallerActivity的onCreate()方法中进行赋值的,我们直接去看 startInstall()方法

    private void startInstall() {
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
        newIntent.setData(mPackageURI);
        newIntent.setClass(this, InstallInstalling.class);
        String installerPackageName = getIntent().getStringExtra(
                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
        if (mOriginatingURI != null) {
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
        }
        if (mReferrerURI != null) {
            newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
        }
        if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
        }
        if (installerPackageName != null) {
            newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                    installerPackageName);
        }
        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
        }
        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
        startActivity(newIntent);
        finish();
    }

startInstall() 主要作用就是跳转到InstallInstalling类并关闭当前安装确认界面。
在InstallInstalling的onCreate()方法会通过PackageParser.parsePackageLite(file, 0)对apk文件进行轻量解析,并调用getPackageManager().getPackageInstaller().createSession(params)。实际是调用PackageInstaller的createSession()方法

    public int createSession(@NonNull SessionParams params) throws IOException {
        try {
            return mInstaller.createSession(params, mInstallerPackageName, mUserId);
        } catch (RuntimeException e) {
            ExceptionUtils.maybeUnwrapIOException(e);
            throw e;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

mInstaller是一个IPackageInstaller对象,IPackageInstaller是一个aidl接口类,它的实现类PackageInstallerService,所以mInstaller.createSession()最终调用到的是PackageInstallerService的createSession()方法并返回了mSessionId。 由此二者实现了进程间的通信
在看一下InstallInstalling的onResume()方法

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

        // This is the first onResume in a single life of the activity
        if (mInstallingTask == null) {
            PackageInstaller installer = getPackageManager().getPackageInstaller();
           //用mSessionId通过PackageInstaller 的getSessionInfo()拿到sessionInfo 信息
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

            if (sessionInfo != null && !sessionInfo.isActive()) {
                mInstallingTask = new InstallingAsyncTask();
                mInstallingTask.execute();
            } else {
                // we will receive a broadcast when the install is finished
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            }
        }
    }

在onResume()方法中,通过mSessionId拿到了SessionInfo信息然后执行力InstallingAsyncTask任务,InstallingAsyncTask的doInBackground方法中会根据安装包包(APK)的Uri,将APK的信息通过IO流的形式写入到PackageInstaller.Session中。InstallingAsyncTask的onPostExecute方法如下所示

        @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方法将IntentSender提交
                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);

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

onPostExecute() 中创建了一个PendingIntent,并将该PendingIntent的IntentSender通过PackageInstaller.Session的commit方法发送出去。

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

mSession为IPackageInstallerSession实例化对象,是一个aidl对象,它的实现类为PackageInstallerSession,这表明要通过IPackageInstallerSession来实现进程间的通信,然后调用到PackageInstallerSession的commit()方法

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

    @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;
        }
        if (isMultiPackage()) {
            final SparseIntArray remainingSessions = mChildSessionIds.clone();
            final IntentSender childIntentSender =
                    new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
                            .getIntentSender();
            boolean sealFailed = false;
            for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
                final int childSessionId = mChildSessionIds.keyAt(i);
                // seal all children, regardless if any of them fail; we'll throw/return
                // as appropriate once all children have been processed
                if (!mSessionProvider.getSession(childSessionId)
                        .markAsSealed(childIntentSender, forTransfer)) {
                    sealFailed = true;
                }
            }
            if (sealFailed) {
                return;
            }
        }

        dispatchStreamValidateAndCommit();
    }

    private void dispatchStreamValidateAndCommit() {
        //向Handler发送消息
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }
     //处理对应消息
    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;
                case MSG_INSTALL:
                    handleInstall();
                    break;
                ......
                ......
            }

            return true;
        }
    };

    private void handleStreamValidateAndCommit() {
        PackageManagerException unrecoverableFailure = null;
        // This will track whether the session and any children were validated and are ready to
        // progress to the next phase of install
        boolean allSessionsReady = false;
        ......
        ......

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

    private void handleInstall() {
        ......
        ......
        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);
        }
    }


    @GuardedBy("mLock")
    private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
            throws PackageManagerException {
        final PackageManagerService.ActiveInstallSession installingSession =
                makeSessionActiveLocked();
        if (installingSession == null) {
            return;
        }
        if (isMultiPackage()) {
            ......
            ......
           //mPm是PackageManagerService的实例对象
            mPm.installStage(installingChildSessions);
        } else {
            mPm.installStage(installingSession);
        }
    }

在PackageInstallerSession##commit()方法中会调用到dispatchStreamValidateAndCommit(),该方法作用就是向mHandler以Callback形式发送一条msg.what为MSG_STREAM_VALIDATE_AND_COMMIT的消息,在mHandler的handleMessage()中会调用handleStreamValidateAndCommit()对该消息进行处理在handleStreamValidateAndCommit()中又会向向mHandler以Callback形式发送一条msg.what为MSG_INSTALL的消息,以 handleInstall()对该消息进行处理,在handleInstall()中通过synchronized 保证了对installNonStagedLocked()方法调用的线程同步,installNonStagedLocked()方法中会调用 mPm.installStage(),mPm是PackageManagerService实例化对象。这样代码逻辑便进入PackageManagerService##installStage()

    void installStage(ActiveInstallSession activeInstallSession) {
        if (DEBUG_INSTANT) {
            if ((activeInstallSession.getSessionParams().installFlags
                    & PackageManager.INSTALL_INSTANT_APP) != 0) {
                Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
            }
        }
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        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);
    }

PackageManagerService##installStage()有两个不同参数的重载方法,这个方法没什么重点,只是创建并向Handler发送一条msg.what为INIT_COPY的消息

    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;
                }
            }
        }
    }

在PackageHandler 对该消息的处理逻辑是调用HandlerParams ##startCopy()

        final void startCopy() {
            if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
            handleStartCopy();
            handleReturnCode();
        }

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

HandlerParams是一个抽象类它的实现类有PackageManagerService##MultiPackageInstallParams及
PackageManagerService##InstallParams。在PackageHandler ##doHandleMessage()方法对msg的处理逻辑中可以发现,INIT_COPY消息体中包含HandlerParams 对象,往前查看发现传入对象为根据调用方法不一样而分为MultiPackageInstallParams与InstallParams两种。先看一下MultiPackageInstallParams##handleStartCopy()实现

        @Override
        void handleStartCopy() {
            for (InstallParams params : mChildParams) {
               //还是调用到了InstallParams 的handleStartCopy()
                params.handleStartCopy();
                if (params.mRet != INSTALL_SUCCEEDED) {
                    mRet = params.mRet;
                }
            }
        }

发现其实就是对InstallParams集合进行了遍历然后调用handleStartCopy()方法。所以,殊途同归handleStartCopy()的实现最终还是要看InstallParams##handleStartCopy()

        public void handleStartCopy() {
            int ret = PackageManager.INSTALL_SUCCEEDED;

            // If we're already staged, we've firmly committed to an install location
            //检查文件是否已生成 是则对installFlags进行赋值
            if (origin.staged) {
                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 we have too little free space, try to free cache
             * before giving up.
             */
             //判断安装空间是否充足,若不充足则尝试释放部分空间
            if (!origin.staged && pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                // TODO: focus freeing disk space on the target device
                final StorageManager storage = StorageManager.from(mContext);
                final long lowThreshold = storage.getStorageLowBytes(
                        Environment.getDataDirectory());

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

                /*
                 * The cache free must have deleted the file we downloaded to install.
                 *
                 * TODO: fix the "freeCache" call to not delete the file we care about.
                 */
                if (pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                }
            }


            if (ret == PackageManager.INSTALL_SUCCEEDED) {//1
                int loc = pkgLite.recommendedInstallLocation;
                if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                    ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                    ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_APK;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_URI;
                } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                    ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
                } else {
                    // Override with defaults if needed.
                    //对包信息进行覆盖
                    loc = installLocationPolicy(pkgLite);
                    if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                        ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                    } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
                        ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
                    } else if (!onInt) {
                        // Override install location with flags
                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                            // Set the flag to install on external media.
                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                        } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
                            if (DEBUG_INSTANT) {
                                Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
                            }
                            installFlags |= PackageManager.INSTALL_INSTANT_APP;
                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                        } else {
                            // Make sure the flag for installing on external
                            // media is unset
                            installFlags |= PackageManager.INSTALL_INTERNAL;
                        }
                    }
                }
            }

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

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

                // Perform package verification (unless we are simply moving the package).
                //验证包信息
                if (!origin.existing) {
                    PackageVerificationState verificationState =
                            new PackageVerificationState(this);
                    mPendingVerification.append(verificationId, verificationState);

                    sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
                    ret = sendPackageVerificationRequest(
                            verificationId, pkgLite, verificationState);

                    // If both verifications are skipped, we should remove the state.
                    if (verificationState.areAllVerificationsComplete()) {
                        mPendingVerification.remove(verificationId);
                    }
                }
            ......
            ......
            }

            mRet = ret;
        }

在InstallParams##handleStartCopy()主要功能就是四点:
1.验证文件是否已生成,若生成则对installFlags进行赋值处理
2.验证空间是否充足 不充足则尝试释放无用空间
3.对包信息进行覆盖处理 同时返回一个int值 loc
4.验证包信息
同样handleReturnCode()最终实现也是在InstallParams类中

        @Override
        void handleReturnCode() {
            if (mVerificationCompleted
                    && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
                    String packageName = "";
                    ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                            new ParseTypeImpl(
                                    (changeId, packageName1, targetSdkVersion) -> {
                                        ApplicationInfo appInfo = new ApplicationInfo();
                                        appInfo.packageName = packageName1;
                                        appInfo.targetSdkVersion = targetSdkVersion;
                                        return mPackageParserCallback.isChangeEnabled(changeId,
                                                appInfo);
                                    }).reset(),
                            origin.file, 0);
                    if (result.isError()) {
                        Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(),
                                result.getException());
                    } else {
                        packageName = result.getResult().packageName;
                    }
                    try {
                        observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
                    } catch (RemoteException e) {
                        Slog.i(TAG, "Observer no longer exists.");
                    }
                    return;
                }
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    mRet = mArgs.copyApk();
                }
                processPendingInstall(mArgs, mRet);
            }
        }

InstallParams##handleReturnCode()中调用InstallArgs的copyApk()进行APK的拷贝工作。InstallArgs是一个抽象类,FileInstallArgs及MoveInstallArgs继承并实现了它。InstallArgs的实例对象会在InstallParams##handleStartCopy()中调用createInstallArgs()进行创建,最终创建出来的是一个FileInstallArgs类型对象。所以代码逻辑又执行到了FileInstallArgs##copyApk()


        int copyApk() {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
            try {
                return doCopyApk();
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        }

        private int doCopyApk() {
            .....
            ......
            
            int ret = PackageManagerServiceUtils.copyPackage(
                    origin.file.getAbsolutePath(), codeFile);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "Failed to copy package");
                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);
                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;
        }
    public static int copyPackage(String packagePath, File targetDir) {
        if (packagePath == null) {
            return PackageManager.INSTALL_FAILED_INVALID_URI;
        }

        try {
            final File packageFile = new File(packagePath);
            final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
            copyFile(pkg.baseCodePath, targetDir, "base.apk");
            if (!ArrayUtils.isEmpty(pkg.splitNames)) {
                for (int i = 0; i < pkg.splitNames.length; i++) {
                    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);
            FileUtils.copy(source.getFD(), targetFd);
        } finally {
            IoUtils.closeQuietly(source);
        }
    }

在copyApk()调用 doCopyApk(),在 doCopyApk()再调用到PackageManagerServiceUtils##copyPackage(),逻辑进入到PackageManagerServiceUtils##copyPackage()会调用PackageManagerServiceUtils##copyFile()方法,而copyFile()方法就是将文件通过IO流将apk拷贝到相关目录下
至此,APK验证拷贝流程完结

三、安装APK

在APK拷贝完成后即InstallParams##handleReturnCode()中mArgs.copyApk()流程执行完毕后,会在调用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)));
        }
    }

    // Queue up an async operation since the package installation may take a little while.
    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));
            }
        });
    }
    @GuardedBy({"mInstallLock", "mLock"})
    private void installPackagesTracedLI(List<InstallRequest> requests) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
            installPackagesLI(requests);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
    @GuardedBy("mInstallLock")
    private void installPackagesLI(List<InstallRequest> requests) {
        final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
        final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
        final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
        final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
        final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
        final Map<String, PackageSetting> lastStaticSharedLibSettings =
                new ArrayMap<>(requests.size());
        final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
        boolean success = false;
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
            for (InstallRequest request : requests) {
                // TODO(b/109941548): remove this once we've pulled everything from it and into
                //                    scan, reconcile or commit.
                final PrepareResult prepareResult;
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
                
                    // Prepare 准备:分析当前安装状态,分析包并对其进行初始验证
                    prepareResult =
                            preparePackageLI(request.args, request.installResult);
                } catch (PrepareFailure prepareFailure) {
                    request.installResult.setError(prepareFailure.error,
                            prepareFailure.getMessage());
                    request.installResult.origPackage = prepareFailure.conflictingPackage;
                    request.installResult.origPermission = prepareFailure.conflictingPermission;
                    return;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                ......
                try {
                    //扫描分析准备阶段拿到的包信息
                    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 reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
                    installResults,
                    prepareResults,
                    mSharedLibraries,
                    Collections.unmodifiableMap(mPackages), versionInfos,
                    lastStaticSharedLibSettings);
            CommitRequest commitRequest = null;
            synchronized (mLock) {
                Map<String, ReconciledPackage> reconciledPackages;
                ......
                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);
                }
            }
            //完成APK的安装
            executePostCommitSteps(commitRequest);
        } finally {
         ......
        }
    }

processPendingInstall()会调用processInstallRequestsAsync()方法,在processInstallRequestsAsync()方法中再调用installPackagesTracedLI()方法执行安装流程,同时会对该方法以synchronized修饰保证线程同步。
在installPackagesTracedLI()中会对应用信息进行处理然后调用executePostCommitSteps ()执行安装。
executePostCommitSteps ()方法中会为新的代码路径准备应用程序配置文件,并在此检查是否需要dex优化。
如果是安装新包,会为新的代码路径准备应用程序配置文件;如果是替换已安装应用,其主要过程为更新设置,清除原有应用数据数据,重新生成相关的app数据目录等步骤,同时要区分系统应用替换和非系统应用替换。而安装新包,则直接更新设置,生成APP数据即可。这部分代码以及总结会后续补全发出

如果你有什么疑问或者认为哪个地方解析有错误,欢迎您在评论区指正。技术都是交流出来的

上一篇下一篇

猜你喜欢

热点阅读