分析APP的安装流程 API29

2023-05-16

先总结一下安装流程,以及比较重要的类

PackageInstallerActivity.java:

        在文件管理器里点击apk后就会调用该类,主要用于显示要安装的apk的一些权限信息。

InstallAppProgress.java:

        当看完所有权限后,点安装后就会调用该类,用于显示安装进度,这时候PackageManagerService就在默默的安装应用。

ApplicationPackageManager.java:

        这是类是PackageManager的子类,我们使用mContext.getPackageManager得到的其实就是ApplicationPackageManager的对象,它爹PackageManager是个抽象类,对外的方法都定义在里面。

PackageParser.java:

        解析app,主要解析apk中的AndroidManifest.xml,解析里面的四大组件以及权限信息放入内存里,最后写到packages.xml和package.list(/data/system下)中。

AssetManager.java:

        把AndroidManifest.xml从app中拿出来给PackageParser.java去解析。

DefaultContainerService.java:

        这个服务用于检查存储状态,得到合适的安装位置。

Installer.java:

        PackageManagerService调用它去执行安装,他会把PackageManagerService传过来的数据封装成命令,然后让底层的Installer去执行。

PackageManagerService.java:

        管理app的安装、移动、卸载、查询等。

大致步骤

        a.调起PackageInstallerActivity--这里检查未知来源应用

        b.点击 “安装” 按钮 跳转到InstallInstalling 界面

        c.通过PackageInstallerSession把文件流转到PMS

        d.PMS发送 INIT_COPY命令准备复制apk

        e.HandlerParams执行startCopy方法(handleStartCopy和handleReturnCode 2个方法)

                e.a 首先检查文件和cid是否已生成,如生成则设置installFlags

                e.b 检查空间大小,如果空间不够则释放无用空间

                e.c 覆盖原有安装位置的文件,并根据返回结果来确定函数的返回值,并设置installFlags

                e.e 确定是否有任何已安装的包验证器,如有,则延迟检测

                e.f handleReturnCode执行真正复制操作--复制APK和so文件到/data/下目录

       f.APK拷贝完成后,进入APK装载阶段核心方法installPackagesTracedLI

                f.a prepare分析任何当前安装状态,分析包并对其进行初始验证 --解析app,主要解析apk中的AndroidManifest.xml,解析里面的四大组件以及权限信息放入内存里,最后写到packages.xml和 package.list(/data/system下)

                f.b Scan  扫描:考虑到prepare中收集的上下文,询问已分析的包

                f.c Reconcile 调和:在彼此的上下文和当前系统状态中验证扫描的包,以确保安装成功

                f.d Commit 提交:提交所有扫描的包并更新系统状态。这是安装流中唯一可以修改系统状态的地方,必须在此阶段之前确定所有可预测的错误

                f.e 安装 executePostCommitSteps

        g.最终调用Installer的createUserData和install方法,连接底层的Installed服务来安装

        h.dexopt优化 - 会使用dexopt把base.apk中的dex文件优化为odex,存储在/data/dalvik-cache中,如果是ART模式,则会使用dex2oat优化成oat文件也存储在该目录下

安装过程会影响到的目录 全部明确了

  • /system/app
  • /data/app
  • /data/data
  • /data/dalvik-cache
  • /data/system
  • /data/system/package.xml 和 /data/system/package.list

流程图1. 点击安装到APK拷贝

 流程图2. 拷贝完成-安装

 下面是详细的分析过程:

通常使用下面的代码开始安装流程

Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(context, "authorities", apkFile); //authorities就是provider里声明的authorities
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        startActivity(intent);

Android 7.0增加了一个authorities,才能正常拉起PackageInstallerActivity页面

这里是一个隐式启动activity,看看对应的acitivty是哪里

<activity android:name=".InstallStart"
                android:exported="true"
                android:excludeFromRecents="true">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="file" />
                <data android:scheme="content" />
                <data android:mimeType="application/vnd.android.package-archive" />
            </intent-filter>
         ...
        </activity>

上面的代码实际是启动得InstallStart这个activity,是PackageInstallerActivity的入口 InstallStart.java

  @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ...
           if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {//1
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();
            if (packageUri == null) {//2
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);
                nextActivity = null;
            } else {
                if (packageUri.getScheme().equals(SCHEME_CONTENT)) {//3
                    nextActivity.setClass(this, InstallStaging.class);
                } else {
                    nextActivity.setClass(this, PackageInstallerActivity.class);
                }
            }
        }
        if (nextActivity != null) {
            startActivity(nextActivity);
        }
        finish();
    }

虽然看到了PackageInstallerActivity,但是分析一下,并不是直接到注释1处代码

注释1处判断Intent的Action是否为CONFIRM_PERMISSIONS,根据本文的应用情景显然不是,接着往下看,

注释2处判断packageUri 是否为空也不成立,注释3处,判断Uri的Scheme协议是否是content,如果是就跳转到InstallStaging,如果不是就跳转到PackageInstallerActivity。

本文的应用情景中,Android7.0以及更高版本我们会使用FileProvider来处理URI ,FileProvider会隐藏共享文件的真实路径,将路径转换成content://Uri路径,这样就会跳转到InstallStaging.java

packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java

 @Override
    protected void onResume() {
        super.onResume();
        if (mStagingTask == null) {
            if (mStagedFile == null) {
                try {
                    mStagedFile = TemporaryFileManager.getStagedFile(this);//1
                } catch (IOException e) {
                    showError();
                    return;
                }
            }
            mStagingTask = new StagingAsyncTask();
            mStagingTask.execute(getIntent().getData());//2
        }
    }

注释1处如果File类型的mStagedFile 为null,则创建mStagedFile ,mStagedFile用于存储临时数据。

注释2处启动StagingAsyncTask,并传入了content协议的Uri,如下所示。 packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java

 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)) {
                if (in == null) {
                    return false;
                }
                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }
        @Override
        protected void onPostExecute(Boolean success) {
            if (success) {
                Intent installIntent = new Intent(getIntent());
                installIntent.setClass(InstallStaging.this, PackageInstallerActivity.class);
                installIntent.setData(Uri.fromFile(mStagedFile));
                installIntent
                        .setFlags(installIntent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivityForResult(installIntent, 0);
            } else {
                showError();
            }
        }
    }
}

doInBackground方法中将packageUri(content协议的Uri)的内容写入到mStagedFile中,

如果写入成功,onPostExecute方法中会跳转到PackageInstallerActivity中,并将mStagedFile传进去。

绕了一圈又回到了PackageInstallerActivity,这里可以看出InstallStaging主要起了转换的作用,将content协议的Uri转换为File协议,

然后跳转到PackageInstallerActivity,这样就可以像此前版本(Android7.0之前)一样启动安装流程了

终于进入到PackageInstallerActivity.java

 @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        if (icicle != null) {
            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
        }
        mPm = getPackageManager();
        mIpm = AppGlobals.getPackageManager();
        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
        mInstaller = mPm.getPackageInstaller();
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
        ...
        //根据Uri的Scheme进行预处理
        boolean wasSetUp = processPackageUri(packageUri);//1
        if (!wasSetUp) {
            return;
        }
        bindUi(R.layout.install_confirm, false);
        //判断是否是未知来源的应用,如果开启允许安装未知来源选项则直接初始化安装
        checkIfAllowedAndInitiateInstall();//2
    }

首先初始话安装所需要的各种对象,比如PackageManager、IPackageManager、AppOpsManager和UserManager等等

然后安装的时候需要点击开始安装按钮,直接去看那里

private void bindUi() {
    mAlert.setIcon(mAppSnippet.icon);
    mAlert.setTitle(mAppSnippet.label);
    mAlert.setView(R.layout.install_content_view);
    mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
            (ignored, ignored2) -> {
                if (mOk.isEnabled()) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                    } else {
                        startInstall();
                    }
                }
            }, null);
    mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
            (ignored, ignored2) -> {
                // Cancel and finish
                setResult(RESULT_CANCELED);
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, false);
                }
                finish();
            }, null);
    setupAlert();

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

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

不同版本代码可能有差异,但是下一步很明显,调用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方法,主要工作就是构建intent,传入数据,打开InstallInstalling.java

InstallInstalling的onCreate方法,主要工作是

1.判断是否为当前应用

2.如果savedInstanceState不为null,获取此前保存的mSessionId和mInstallId,其中mSessionId是安装包的会话id,mInstallId是等待的安装事件id

3.根据mInstallId向InstallEventReceiver注册一个观察者,launchFinishBasedOnResult会接收到安装事件的回调,无论安装成功或者失败都会关闭当前的Activity(InstallInstalling)。如果savedInstanceState为null,代码的逻辑也是类似的

4.创建SessionParams,它用来代表安装会话的参数,组装params

5.根据mPackageUri对包(APK)进行轻量级的解析,并将解析的参数赋值给SessionParams

6.向InstallEventReceiver注册一个观察者返回一个新的mInstallId,其中InstallEventReceiver继承自BroadcastReceiver,用于接收安装事件并回调给EventResultPersister。

7.PackageInstaller的createSession方法内部会通过IPackageInstaller与PackageInstallerService进行进程间通信,最终调用的是PackageInstallerService的createSession方法来创建并返回mSessionId

protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    ApplicationInfo appInfo = getIntent()
            .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
    mPackageURI = getIntent().getData();
 
    if ("package".equals(mPackageURI.getScheme())) {
        try {
        //表示安装当前应用,就是自己更新自己
        //安装与该应用同名的应用,应该比较快,否则会抛出异常
            getPackageManager().installExistingPackage(appInfo.packageName);
            launchSuccess();
        } catch (PackageManager.NameNotFoundException e) {
            launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
        }
    } else {
        //安装一个新应用    	
        //根据mPackageURI创建一个对应的File
        final File sourceFile = new File(mPackageURI.getPath());
        PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
 
        mAlert.setIcon(as.icon);
        mAlert.setTitle(as.label);
        mAlert.setView(R.layout.install_content_view);
        mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
                (ignored, ignored2) -> {
                    if (mInstallingTask != null) {
                        mInstallingTask.cancel(true);
                    }
 
                    if (mSessionId > 0) {
                        getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                        mSessionId = 0;
                    }
 
                    setResult(RESULT_CANCELED);
                    finish();
                }, null);
        setupAlert();
        requireViewById(R.id.installing).setVisibility(View.VISIBLE);
 
        //1.如果savedInstanceState不为null,获取此前保存的mSessionId和mInstallId,其中mSessionId是安装包的会话id,mInstallId是等待的安装事件id
        if (savedInstanceState != null) {
            mSessionId = savedInstanceState.getInt(SESSION_ID);
            mInstallId = savedInstanceState.getInt(INSTALL_ID);
 
            // Reregister for result; might instantly call back if result was delivered while
            // activity was destroyed
            try {
                //2.根据mInstallId向InstallEventReceiver注册一个观察者,launchFinishBasedOnResult会接收到安装事件的回调,
                //无论安装成功或者失败都会关闭当前的Activity(InstallInstalling)。如果savedInstanceState为null,代码的逻辑也是类似的
                InstallEventReceiver.addObserver(this, mInstallId,
                        this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                // Does not happen
            }
        } else {
            //3.创建SessionParams,它用来代表安装会话的参数,组装params
            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
            params.setInstallAsInstantApp(false);
            params.setReferrerUri(getIntent().getParcelableExtra(Intent.EXTRA_REFERRER));
            params.setOriginatingUri(getIntent()
                    .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
            params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                    UID_UNKNOWN));
            params.setInstallerPackageName(getIntent().getStringExtra(
                    Intent.EXTRA_INSTALLER_PACKAGE_NAME));
            params.setInstallReason(PackageManager.INSTALL_REASON_USER);
 
            //4.根据mPackageUri对包(APK)进行轻量级的解析,并将解析的参数赋值给SessionParams
            File file = new File(mPackageURI.getPath());
            try {
                PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
                params.setAppPackageName(pkg.packageName);
                params.setInstallLocation(pkg.installLocation);
                params.setSize(
                        PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
            } catch (PackageParser.PackageParserException e) {
                Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
                Log.e(LOG_TAG,
                        "Cannot calculate installed size " + file + ". Try only apk size.");
                params.setSize(file.length());
            } catch (IOException e) {
                Log.e(LOG_TAG,
                        "Cannot calculate installed size " + file + ". Try only apk size.");
                params.setSize(file.length());
            }
 
            try {
                //5.向InstallEventReceiver注册一个观察者返回一个新的mInstallId,
                //其中InstallEventReceiver继承自BroadcastReceiver,用于接收安装事件并回调给EventResultPersister。
                mInstallId = InstallEventReceiver
                        .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
 
            try {
                //6.PackageInstaller的createSession方法内部会通过IPackageInstaller与PackageInstallerService进行进程间通信,
                //最终调用的是PackageInstallerService的createSession方法来创建并返回mSessionId
                //最终就是要把组装的参数丢进去创建一个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();
    }
}

然后进入它的onResume方法,调用onPostExecute()方法创建一个AsyncTask并执行

if (sessionInfo != null && !sessionInfo.isActive()) {
    mInstallingTask = new InstallingAsyncTask();
    mInstallingTask.execute();
} 

在这个task中,把apk通过io流的方式写入到PackageInstaller.Session中

然后在onPostExecute()中调用PackageInstaller.Session的commit方法,进行安装

private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
        volatile boolean isDone;

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

            session.setStagingProgress(0);

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

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

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

                            if (isCancelled()) {
                                session.close();
                                break;
                            }
    			 //将APK的信息通过IO流的形式写入到PackageInstaller.Session中
                            out.write(buffer, 0, numRead);
                            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.Session.java] commit
public void commit(@NonNull IntentSender statusReceiver) {
    try {
        //调用PackageInstallerSession的commit方法,进入到java框架层
        mSession.commit(statusReceiver, false);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

commit()中 mSession的类型为IPackageInstallerSession,这说明要通过IPackageInstallerSession来进行进程间的通信,最终会调用PackageInstallerSession的commit方法,这样代码逻辑就到了Java框架层的。

[PackageInstallerSession.java] commit()
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    if (mIsPerfLockAcquired && mPerfBoostInstall != null) {
        mPerfBoostInstall.perfLockRelease();
        mIsPerfLockAcquired = false;
    }
    ...
    //调用markAsCommitted()
    if (!markAsCommitted(statusReceiver, forTransfer)) {
        return;
    }
    ...
    mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}

markAsCommitted方法中会将包的信息封装为 PackageInstallObserverAdapter ,它在PKMS中被定义,然后返回到commit()中,

向Handler发送一个类型为MSG_COMMIT的消息

public boolean markAsCommitted(
        @NonNull IntentSender statusReceiver, boolean forTransfer) {
    Preconditions.checkNotNull(statusReceiver);
 
    List<PackageInstallerSession> childSessions = getChildSessions();
 
    final boolean wasSealed;
    synchronized (mLock) {
        assertCallerIsOwnerOrRootLocked();
        assertPreparedAndNotDestroyedLocked("commit");
 
        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
                mContext, statusReceiver, sessionId,
                isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);
        mRemoteObserver = adapter.getBinder();
    ...
    return true;
}

MSG_COMMIT在handler中进行处理,进入handleCommit()

public boolean handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_COMMIT:
            handleCommit();
            break;
    }
}
 
private void handleCommit() {
    ...
    List<PackageInstallerSession> childSessions = getChildSessions();
 
    try {
        synchronized (mLock) {
            //最终调用installStage(),进入PKMS
            commitNonStagedLocked(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);
    }
}

commitNonStagedLocked()中首先 调用了PackageInstallObserver的 onPackageInstalled方法,将 Complete 方法出现的PackageManagerException的异常信息回调给

PackageInstallObserverAdapter。

最终调用installStage(),进入PKMS

private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
        throws PackageManagerException {
    if (isMultiPackage()) {
        ...
        if (!success) {
            try {
                mRemoteObserver.onPackageInstalled(
                        null, failure.error, failure.getLocalizedMessage(), null);
            } catch (RemoteException ignored) {
            }
            return;
        }
        mPm.installStage(activeChildSessions);
    } else {
        mPm.installStage(committingSession);
    }
}

进入PKMS的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());
        }
    }
    //1.创建了类型为INIT_COPY的消息
    final Message msg = mHandler.obtainMessage(INIT_COPY);
 
    //2.创建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));
 
    //3.将InstallParams通过消息发送出去。
    mHandler.sendMessage(msg);
}
 
对INIT_COPY的消息的处理
[PackageManagerService.java]
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");
                //执行APK拷贝动作
                params.startCopy();
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            break;
        }
    }
}

startCopy调用handleStartCopy

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

1.首先检查文件和cid是否已生成,如生成则设置installFlags。

2.检查空间大小,如果空间不够则释放无用空间。

3.覆盖原有安装位置的文件,并根据返回结果来确定函数的返回值,并设置installFlags

4.确定是否有任何已安装的包验证器,如有,则延迟检测。主要分三步:首先新建一个验证Intent,然后设置相关的信息,之后获取验证器列表,最后向每个验证器发送验证Intent。

 
[PackageManagerService.java]
public void handleStartCopy() {
    // 1.决定是安装在手机内还是sdcard中,设置对应标志位
    if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
        } else {
            throw new IllegalStateException("Invalid stage location");
        }
    }
    ...
    //2.检查空间大小,如果空间不够则释放无用空间。
    if (!origin.staged && pkgLite.recommendedInstallLocation
            == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
        // TODO: focus freeing disk space on the target device
        final StorageManager storage = StorageManager.from(mContext);
        final long lowThreshold = storage.getStorageLowBytes(
                Environment.getDataDirectory());
 
        final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
                origin.resolvedPath, packageAbiOverride);
        if (sizeBytes >= 0) {
            try {
                mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
                pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                        origin.resolvedPath, installFlags, packageAbiOverride);
            } catch (InstallerException e) {
                Slog.w(TAG, "Failed to free cache", e);
            }
        }
        if (pkgLite.recommendedInstallLocation
                == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
            pkgLite.recommendedInstallLocation
                    = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
        }
    }
 
    ...
    //3.覆盖原有安装位置的文件,并根据返回结果来确定函数的返回值,并设置installFlags。
    {
        // 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;
            }
        }
    }
    ...
    //4.确定是否有任何已安装的包验证器,如有,则延迟检测。主要分三步:首先新建一个验证Intent,然后设置相关的信息,
    //之后获取验证器列表,最后向每个验证器发送验证Intent。
    //4.1构造验证Intent
    final Intent verification = new Intent(
            Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
    verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
            PACKAGE_MIME_TYPE);
    verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
    final PackageVerificationState verificationState = new PackageVerificationState(
            requiredUid, this);
 
    mPendingVerification.append(verificationId, verificationState);
     //4.2获取验证器列表
    final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
            receivers, verificationState);
 
    DeviceIdleController.LocalService idleController = getDeviceIdleController();
    final long idleDuration = getVerificationTimeout();
 
    if (sufficientVerifiers != null) {
        final int N = sufficientVerifiers.size();
        if (N == 0) {
            Slog.i(TAG, "Additional verifiers required, but none installed.");
            ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
        } else {
            for (int i = 0; i < N; i++) {
                final ComponentName verifierComponent = sufficientVerifiers.get(i);
                idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
                        verifierComponent.getPackageName(), idleDuration,
                        verifierUser.getIdentifier(), false, "package verifier");
                //4.3向每个验证器发送验证Intent
                final Intent sufficientIntent = new Intent(verification);
                sufficientIntent.setComponent(verifierComponent);
                mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
            }
        }
    }
    ...
}

向验证器客户端发送intent,只有当验证成功之后才会开启copy工作。如果没有任何验证器则直接拷贝

再回到startCopy的handleReturnCode方法

在此方法中最重要的步骤是copyApk和processPendingInstall

调用copyApk 进行apk的拷贝动作

void handleReturnCode() {
    if (mVerificationCompleted && mEnableRollbackCompleted) {
        if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
            String packageName = "";
            try {
                PackageLite packageInfo =
                        new PackageParser().parsePackageLite(origin.file, 0);
                packageName = packageInfo.packageName;
            } catch (PackageParserException e) {
                Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(), e);
            }
            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);
    }
}

APK 拷贝调用栈如下:

copyApk

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

doCopyApk 21行和33行,分别copyApk和so文件

最重要的是

1.复制 baseApk到/data/app/com.xx.xx-XXXxxxXXXxx==/这个目录

2.复制so文件到data下面的目录,/data/data/com.xx.xx/lib /data/app/com.xx.xx-xxx/lib /data/user/0/com.xx.xxx/lib

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(),就是/data/app/com.xx.xx-XXXxxxXXXxx==/这个目录
    //默认文件名为base.apk
    int ret = PackageManagerServiceUtils.copyPackage(
            origin.file.getAbsolutePath(), codeFile);
    if (ret != PackageManager.INSTALL_SUCCEEDED) {
        Slog.e(TAG, "Failed to copy package");
        return ret;
    }

    final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
    NativeLibraryHelper.Handle handle = null;
    try {
        handle = NativeLibraryHelper.Handle.create(codeFile);
        //复制so文件到
        ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                abiOverride);
    } catch (IOException e) {
        Slog.e(TAG, "Copying native libraries failed", e);
        ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    } finally {
        IoUtils.closeQuietly(handle);
    }

    return ret;
}

copyPackage调用copyFile通过文件流的操作,把APK拷贝到/data/app等目录

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

APK拷贝完成后,进入真正的安装,即装载代码流程,流程如下:

就是上面的processPendingInstall 函数

private void processPendingInstall(final InstallArgs args, final int currentStatus) {
    if (args.mMultiPackageInstallParams != null) {
        args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
    } else {
        //1.设置安装参数
        PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
        //2.创建一个新线程,处理安装参数,进行安装
        processInstallRequestsAsync(
                res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                Collections.singletonList(new InstallRequest(args, res)));
    }
}
 
private void processInstallRequestsAsync(boolean success,
        List<InstallRequest> installRequests) {
    mHandler.post(() -> {
        if (success) {
            for (InstallRequest request : installRequests) {
                 //1、预安装,检查包状态,确保环境ok,如果环境不ok,那么会清理拷贝的文件
                 //主要是删除之前安装失败产生的文件,
                  // 预安装阶段,主要是检查安装包的状态,确保安装环境正常,如果安装环境有问题会清理拷贝文件
                request.args.doPreInstall(request.installResult.returnCode);
            }
            synchronized (mInstallLock) {
                //2. installPackagesTracedLI 是安装阶段 再调用installPackagesLI 进行安装。
                installPackagesTracedLI(installRequests);
            }
            for (InstallRequest request : installRequests) {
                //3.安装收尾,如果之前安装失败,清除无用信息
                request.args.doPostInstall(
                        request.installResult.returnCode, request.installResult.uid);
            }
        }
        for (InstallRequest request : installRequests) {
            restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
                    new PostInstallData(request.args, request.installResult, null));
        }
    });
}

直接看installPackagesTracedLI

    @GuardedBy({"mInstallLock", "mPackages"})
    private void installPackagesTracedLI(List<InstallRequest> requests) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
            installPackagesLI(requests);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

进入installPackagesLI方法

该方法分为4个阶段

1.准备阶段,分析任何当前安装状态,解析包并对其进行初始验证

2.扫描阶段,根据准备阶段中收集的上下文对包进行解析,主要用于生成PackageSetting数据结构。调用

scanPackageTracedLI()扫描包内容,此时其实已经解析过一次包内容,在这里能直接获得缓存

3.整合验证阶段 ,还需要对多个安装apk结果进行调和,一般在 install-multi-package的时候会同时安装多个apk。调用方法是

reconcilePackagesLocked():

4.确认提交阶段  提交之后该应用就算完整发布

上面4个步骤,每一步内容都非常多,参考Android APK安装流程(4)--APK加载 - 简书

 @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");
                	 //准备阶段检查包并分析完整性;检查SDK版本、静态库等;检查签名;设置权限;
                    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 {
                 	 //扫描apk
                    final List<ScanResult> scanResults = scanPackageTracedLI(
                            prepareResult.packageToScan, prepareResult.parseFlags,
                            prepareResult.scanFlags, System.currentTimeMillis(),
                            request.args.user);
                               }
                        }
                ...
                synchronized (mPackages) {
                Map<String, ReconciledPackage> reconciledPackages;
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                    //整合验证阶段
                    //在2-2生成PackageSetting和PackageParser.Package数据结构后,
                    //还需要对多个安装apk结果进行调和,一般在 install-multi-package的时候会同时安装多个apk。
                    //调用方法是reconcilePackagesLocked()
                    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");
                    //确认提交阶段,
                    //提交所有扫描的包并更新系统状态。这是唯一可以在安装流中修改系统状态的地方,
                    //必须在此阶段之前确定所有可预测的错误。经过上述几个步骤,两个核心数据结构已经生成,
                    //但是并没有添加到容器中去(PackageManagerService.mPackages 和 PackageManagerService.mSettings.mPackage), 
                    //所以该包里面的组件等还不能查询到,也不能启动。 所以需要调用
    		 //commitPackagesLocked()来进行提交
                    commitRequest = new CommitRequest(reconciledPackages,
                            sUserManager.getUserIds());
                    commitPackagesLocked(commitRequest);
                    success = true;
                } finally {
                    for (PrepareResult result : prepareResults.values()) {
                        if (result.freezer != null) {
                            result.freezer.close();
                        }
                    }
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
            }
                 //执行完成Apk安装
                 executePostCommitSteps(commitRequest);
                 ...        
    }

这里得核心方法就是executePostCommitSteps(commitRequest);

安装然后准备 APP 数据

preparePackageLI方法很长,摘要一部分

               PackageParser pp = new PackageParser();
                final PackageParser.Package pkg;
                // 1. parsePackage
                pkg = pp.parsePackage(tmpPackageFile, parseFlags);
                
                // 2. 校验安装包签名
                final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
                    if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
                        throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                + pkg.packageName + " upgrade keys do not match the "
                                + "previously installed version");
                    }
                } else {
                    try {
                        final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
                        final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
                        // We don't care about disabledPkgSetting on install for now.
                        final boolean compatMatch = verifySignatures(
                                signatureCheckPs, null, pkg.mSigningDetails, compareCompat,
                                compareRecover);
                        // The new KeySets will be re-added later in the scanning process.
                        if (compatMatch) {
                            synchronized (mPackages) {
                                ksms.removeAppKeySetDataLPw(pkg.packageName);
                            }
                        }
                    } catch (PackageManagerException e) {
                        throw new PrepareFailure(e.error, e.getMessage());
                    }
                }
            // 3. 设置相关权限,生成、移植权限   
            int N = pkg.permissions.size();
            for (int i = N - 1; i >= 0; i--) {
                final PackageParser.Permission perm = pkg.permissions.get(i);
                final BasePermission bp =
                        (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);

                // Don't allow anyone but the system to define ephemeral permissions.
                if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
                        && !systemApp) {
                    Slog.w(TAG, "Non-System package " + pkg.packageName
                            + " attempting to delcare ephemeral permission "
                            + perm.info.name + "; Removing ephemeral.");
                    perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
                }

                // Check whether the newly-scanned package wants to define an already-defined perm
                if (bp != null) {
                    // If the defining package is signed with our cert, it's okay.  This
                    // also includes the "updating the same package" case, of course.
                    // "updating same package" could also involve key-rotation.
                    final boolean sigsOk;
                    final String sourcePackageName = bp.getSourcePackageName();
                    final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
                    final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                    if (sourcePackageName.equals(pkg.packageName)
                            && (ksms.shouldCheckUpgradeKeySetLocked(
                            sourcePackageSetting, scanFlags))) {
                        sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
                    } else {

                        // in the event of signing certificate rotation, we need to see if the
                        // package's certificate has rotated from the current one, or if it is an
                        // older certificate with which the current is ok with sharing permissions
                        if (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
                                pkg.mSigningDetails,
                                PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
                            sigsOk = true;
                        } else if (pkg.mSigningDetails.checkCapability(
                                sourcePackageSetting.signatures.mSigningDetails,
                                PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {

                            // the scanned package checks out, has signing certificate rotation
                            // history, and is newer; bring it over
                            sourcePackageSetting.signatures.mSigningDetails = pkg.mSigningDetails;
                            sigsOk = true;
                        } else {
                            sigsOk = false;
                        }
                    }
                    if (!sigsOk) {
                        // If the owning package is the system itself, we log but allow
                        // install to proceed; we fail the install on all other permission
                        // redefinitions.
                        if (!sourcePackageName.equals("android")) {
                            throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
                                    + pkg.packageName
                                    + " attempting to redeclare permission "
                                    + perm.info.name + " already owned by "
                                    + sourcePackageName)
                                    .conflictsWithExistingPermission(perm.info.name,
                                            sourcePackageName);
                        } else {
                            Slog.w(TAG, "Package " + pkg.packageName
                                    + " attempting to redeclare system permission "
                                    + perm.info.name + "; ignoring new declaration");
                            pkg.permissions.remove(i);
                        }
                    } else if (!PLATFORM_PACKAGE_NAME.equals(pkg.packageName)) {
                        // Prevent apps to change protection level to dangerous from any other
                        // type as this would allow a privilege escalation where an app adds a
                        // normal/signature permission in other app's group and later redefines
                        // it as dangerous leading to the group auto-grant.
                        if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                                == PermissionInfo.PROTECTION_DANGEROUS) {
                            if (bp != null && !bp.isRuntime()) {
                                Slog.w(TAG, "Package " + pkg.packageName + " trying to change a "
                                        + "non-runtime permission " + perm.info.name
                                        + " to runtime; keeping old protection level");
                                perm.info.protectionLevel = bp.getProtectionLevel();
                            }
                        }
                    }
                }
                	//4.生成安装包Abi(Application binary interface,应用二进制接口)
                    try {
                        String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
                                args.abiOverride : pkg.cpuAbiOverride);
                        final boolean extractNativeLibs = !pkg.isLibrary();
                        derivePackageAbi(pkg, abiOverride, extractNativeLibs);
                    } catch (PackageManagerException pme) {
                        Slog.e(TAG, "Error deriving application ABI", pme);
                        throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
                                "Error deriving application ABI");
                    }   
                   //冻结apk 
        		final PackageFreezer freezer =
              	freezePackageForInstall(pkgName, installFlags, "installPackageLI");                                  
            }

最后执行executePostCommitSteps(commitRequest)方法

主要做2件事

1.安装 然后准备APP数据prepareAppDataAfterInstallLIF

2.dexopt优化

/**
 * On successful install, executes remaining steps after commit completes and the package lock
 * is released. These are typically more expensive or require calls to installd, which often
 * locks on {@link #mPackages}.
 */
private void executePostCommitSteps(CommitRequest commitRequest) {
    for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
        final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
                        & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
        final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
        final String packageName = pkg.packageName;
        //***44***
        prepareAppDataAfterInstallLIF(pkg);
        if (reconciledPkg.prepareResult.clearCodeCache) {
            clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
                    | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
        }
        if (reconciledPkg.prepareResult.replace) {
            mDexManager.notifyPackageUpdated(pkg.packageName,
                    pkg.baseCodePath, pkg.splitCodePaths);
        }

        // Prepare the application profiles for the new code paths.
        // This needs to be done before invoking dexopt so that any install-time profile
        // can be used for optimizations.
        //***45***
        mArtManagerService.prepareAppProfiles(
                pkg,
                resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),
                /* updateReferenceProfileContent= */ true);

        // Check whether we need to dexopt the app.
        //
        // NOTE: it is IMPORTANT to call dexopt:
        //   - after doRename which will sync the package data from PackageParser.Package and
        //     its corresponding ApplicationInfo.
        //   - after installNewPackageLIF or replacePackageLIF which will update result with the
        //     uid of the application (pkg.applicationInfo.uid).
        //     This update happens in place!
        //
        // We only need to dexopt if the package meets ALL of the following conditions:
        //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
        //   2) it is not debuggable.
        //
        // Note that we do not dexopt instant apps by default. dexopt can take some time to
        // complete, so we skip this step during installation. Instead, we'll take extra time
        // the first time the instant app starts. It's preferred to do it this way to provide
        // continuous progress to the useur instead of mysteriously blocking somewhere in the
        // middle of running an instant app. The default behaviour can be overridden
        // via gservices.
        final boolean performDexopt =
                (!instantApp || Global.getInt(mContext.getContentResolver(),
                Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);

        if (performDexopt) {
            // Compile the layout resources.
            if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
                //***46***
                mViewCompiler.compileLayouts(pkg);
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }

            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
            // Do not run PackageDexOptimizer through the local performDexOpt
            // method because `pkg` may not be in `mPackages` yet.
            //
            // Also, don't fail application installs if the dexopt step fails.
            DexoptOptions dexoptOptions = new DexoptOptions(packageName,
                    REASON_INSTALL,
                    DexoptOptions.DEXOPT_BOOT_COMPLETE
                            | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
            //***47***
            mPackageDexOptimizer.performDexOpt(pkg,
                    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 blacklist.
        // TODO: Layering violation
        //***49***
        BackgroundDexOptService.notifyPackageChanged(packageName);
    }
}

 prepareAppDataAfterInstallLIF 还会有一系列的调用

   prepareAppDataAfterInstallLIF()
-> prepareAppDataLIF()
-> prepareAppDataLeafLIF()
-> mInstaller.createAppData(...)

final Installer mInstaller;
private void prepareAppDataLeafLIF(...) {
   // 最终调用 系统服务 Installer 安装
   ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                    appId, seInfo, app.targetSdkVersion);     
}   

public class Installer extends SystemService {
   ...
}

至此整个 apk 的安装过程结束,实际上安装成功之后,还会发送一个 App 安装成功的广播 ACTION_PACKAGE_ADDED。手机桌面应用注册了这个广播,当接收到应用安装成功之后,就将 apk 的启动 icon 显示在桌面上。

  • 点击 APK 安装,会启动 PackageInstallerActivity,再进入 InstallInstalling 这两个 Activity 显示应用信息
  • 点击页面上的安装,将 APK 信息存入 PackageInstaller.Session 传到 PMS
  • PMS会做两件事,拷贝安装包和装载代码
  • 在拷贝安装包过程中会开启 Service 来 copyAPK 、检查apk安装路径,包的状态
  • 拷贝完成以 base.apk 形式存在/data/app包名下
  • 装载代码过程中,会继续解析 APK,把清单文件内容存放于 PMS
  • 对 apk 进行签名校验
  • 安装成功后,更新应用设置权限,发送广播通知桌面显示APP图标,安装失败则删除安装包和各种缓存文件
  • 执行 dex2oat 优化

感谢:

Android 10.0 PackageManagerService(四)APK安装流程-[Android取经之路]_IngresGe 的专栏-CSDN博客

Android APK安装流程(4)--APK加载 - 简书

说一说 APK 的安装流程 - 知乎

APK安装流程概述 - 浪里小白龙呼呼呼 - 博客园

应用程序安装流程 | 风中老狼的博客

Android包管理机制(一)PackageInstaller的初始化 - 简书

APK安装过程 - 简书

Android中APK的安装流程_mockingbirds的专栏-CSDN博客

安装流程 - 简书

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分析APP的安装流程 API29 的相关文章

  • libjvm.so共享库找不到

    redis 64 node 1 ggs ggsci ggsci error while loading shared libraries libjvm so cannot open shared object file No such fi
  • 如何修改新建脚本模板-ScriptTemplates(Unity3D开发之十五)

    猴子原创 xff0c 欢迎转载 转载请注明 xff1a 转载自Cocos2Der CSDN xff0c 谢谢 xff01 原文地址 http blog csdn net cocos2der article details 44957631
  • linux简易书单

    1 Linux程序设计 xff08 第4版 xff09 万千读者推荐的Linux经典入门书 程序设计实战型图书 xff0c 以简单易懂 内容全面和示例丰富而受到广泛好评 如果你没有Linux基础 xff0c 可以先读这本 话说回来 xff0
  • Spring Boot与RabbitMQ的整合消息确认

    strong span style font size 18px 消息生产者和消费者 span strong import com rabbitmq client Channel import org slf4j Logger import
  • tomcat7下载安装

    进入apache官网下载tomcat7在最左边的找到download下的tomcat 7并点击 选择 32 bit 64 bit Windows Server Installer 下载 下载完成后 xff0c 将安装文件移动到本地硬盘D 安
  • MPU9250调试笔记(融合磁力计计算Yaw)

    底部 附源码 2022 10 5 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 产品需要一个姿态传感器 xff0c 使用了MPU9250 xff0c 主要是算法库不太好找
  • 在sql语句中,like所有的用法

    在sql结构化查询语言中 xff0c like语句有着至关重要的作用 like语句的语法格式是 xff1a select from 表名 where 字段名 like 对应值 xff08 子串 xff09 xff0c 它主要是针对字符型字段
  • 解决当使用element table fix column 固定列时,滚动条在固定列下方无法滚动

    解决当使用element table fix column 固定列时 xff0c 滚动条在固定列下方无法滚动 在全局css中添加这行代码 xff0c 提高滚动条在z轴的坐标 建议使用下面的代码 xff0c 使用参考文章里面的代码会导致 右f
  • 使用funcraft管理阿里云函数计算

    Fun 是一个用于支持Serverless应用部署的工具 xff0c 能帮助您便捷地管理函数计算 API 网关和日志服务等资源 它通过一个资源配置文件 xff08 template yml xff09 xff0c 协助您进行开发 构建和部署
  • 阿里云 ServerLess:Todo list 应用安装

    安装Node js环境 执行如下命令 xff0c 下载Node js安装包 xff1a wget https npm taobao org mirrors node v12 4 0 node v12 4 0 linux x64 tar xz
  • Maven中pom.xml的scope

    一 compile xff1a 编译范围 compile是默认的范围 xff1b 如果没有提供一个范围 xff0c 编译范围依赖在所有的classpath 中可用 xff0c 同时它们也会被打包 而且这些dependency会传递到依赖的项
  • 如何通过CSS绘制三角形和小箭头

    1 绘制三角形的作用 xff0c 主要是提供指示性 xff0c 如下图 2 那么如何画出三角形呢 xff1f 我们先看下border的用法 lt div class 61 34 box 34 gt lt div gt box box siz
  • 利用python pil 实现给图片上添加文字

    最近的一个工程项目是讲文字添加到图像上 使用了opencv xff0c 结果发现利用opencv给图像添加文字有局限 xff08 1 xff09 可利用的字体类型比较少 xff0c 需要安装Freetype扩展 xff0c 比较复杂 xff
  • esp8266 error: espcomm_open failed

    gpi0 没有接地 xff0c 接上地就可以了注意和usb分出的gnd接口接的是同一个地
  • 微信小程序quickstart项目中有关userInfoReadyCallback的解释

    userInfoReadyCallback 在index js中定义 xff0c 在app js中使用 附app js代码 xff1a app js App onLaunch function 展示本地存储能力 var logs 61 wx
  • 微信小程序引入模块中wxml、wxss、js

    先描述下目录结构 xff0c 见下图 UI页面见下图 其中ok按键是引入的log模块 xff0c log模块非page页 indexButton是index页本身拥有的组件 xff0c index页直接导入Log模块中的组件 xff0c c
  • PHP解析错误 PHP Parse error: syntax error, unexpected '[' in

    之前做了一次php后台代码在不同服务器的迁移 xff0c 代码迁移到新服务器上 xff0c 代码运行不了 xff0c 返回500内部错误 500 内部错误 xff0c 就查看服务器上 var log php fpm www error lo
  • 微信小程序使用video组件时的一些坑

    xff08 1 xff09 mp4视频用video组件播放会卡顿 绿屏 尤其是在快进 拖动时间条时 在video组件上 xff0c 试试加个custom cache 61 false 属性 xff0c 可以完美解决卡顿 绿屏的现象 xff0
  • java中JFrame中函数removeAll的用法

    解答链接 用baidu搜了半天搜不出来 xff0c 用google一下就出来了 下面用自己的代码来解释下removeAll xff08 xff09 的用法 注意一定要在getContentPane xff08 xff09 中用removeA
  • C#基础教程(十三)消息队列——MSMQ

    Net使用消息队列 xff0c 借助windows组件来存储要完成的一系列任务 xff0c 不用程序使用同一个队列 xff0c 方便不同程序之间的数据共享和协作 队列分事务性队列和非事务性队列 xff0c 默认创建的是非事务性队列 那么什么

随机推荐

  • 创建vue项目时报错Failed to download,解决办法

    当我们想要通过vue脚手架 xff0c 创建一个vue项目时 xff0c 正常的步骤是 xff0c 通过执行下面命令 xff1a vue init webpack 项目名 然后根据自己的实际情况 xff0c 选择需要安装哪些东西 xff0c
  • Linux下生产者与消费者实现的问题详解

    生产者与消费者问题 什么是生产者消费者问题 xff1f 背景问题的核心解决思路 单生产者和单消费者同步信号的使用伪代码 xff08 逻辑 xff09 解释上述伪代码 多生产者和多消费者与单生产者和单消费者的区别理解多生产者和多消费者解决方法
  • 参加中国移动开发者大会有感

    作为我校CSDN俱乐部的主席 xff0c 我很荣幸受到CSDN的邀请参加中国移动开发者大会 xff0c 下面写点自己的感悟 xff1a 移动互联网应用大时代已经到来 xff0c 所有的IT应用都将移动化 xff0c 所有的信息服务都将移动化
  • ESXI 安装并部署主机

    一 ESXI的相关知识 ESXI是VMware的企业虚拟化产品 xff0c 可视为虚拟化的平台基础 xff0c 部署于实体服务器 不同于VMware Workstation VMware Server xff0c ESXI采用的是 xff0
  • html中的简单实例演示(checkbox)的使用

    演示一个小小的例子 xff1a 在购物车里面 xff0c 我们能够勾选自己所选的商品 xff0c 然后能够显示出相应的价格 1 xff0c 首先显示出相应的界面 xff1a 相关 代码 xff1a lt body gt 商品列表 xff1a
  • CAS4.0配置Mysql数据库,认证失败

    CAS4 0配置Mysql数据库 xff0c 认证失败 真崩溃啊 xff0c 看别人的视频都能成功 xff0c 自己研究一下午了 xff0c 也还是不行 问题出在哪儿呢 xff1f 请大神指教 三个jar包 xff1a 代码片 span c
  • FtpURLConnection 图片下载编码问题

    写道 问题 xff1a 1 xff09 Web项目中下载图片 存在下载不全 xff0c 丢失部份图片 2 xff09 FTP路径存在中文图片名称或中文路径 3 xff09 直接运行Main方法可以下载图片 xff0c 放在Web项目中下载失
  • nginx http跳转到https总结

    查找了一些资料 在这里终结一下 提供给大家参考 Nginx环境下强制http转https设置方法 方法一 下面代码照搬过去就行 无需做任何修改 春哥技术博客推荐此种方法 非常简单 改完以后实时生效 不用重启服务器 if scheme htt
  • SpringBoot 整合Thymeleaf教程及使用

    Thymeleaf 是一款用于渲染 XML XHTML HTML5 内容的模板引擎 它与 JSP xff0c Velocity xff0c FreeMaker 等模板引擎类似 xff0c 也可以轻易地与 Spring MVC 等 Web 框
  • Window2012R2 忘记密码解决办法

    推荐 xff1a ISO映像修复法 xff08 步骤复杂但方法管用 xff09 以WMware虚拟机中WindowServer2008R2忘记开机密码为例 1 xff09 配置虚拟主机的CD ROM 启动VMware虚拟机程序 选中Wind
  • linux下查找文件及查找包含指定内容的文件常用命令。

    xfeff xfeff 每一种操作系统都是由成千上万个不同种类的文件所组成的 其中有系统本身自带的文件 xff0c 用户自己的文件 xff0c 还有共享文件等等 我们有时候经常忘记某份文件放在硬盘中的哪个地方 在微软的Windows操作系统
  • 知识图谱构建技术

    知识图谱的构建技术包括知识抽取 知识融合 知识加工和知识更新等 图1 知识图谱构建技术流程图 1 1 知识抽取 知识抽取就是自动化或半自动化的从原始数据中获得实体 关系及属性等可用知识单元 早期是基于规则的知识抽取 xff0c 通过人为预先
  • 答“我们的团队项目是否有大泥球?”

    总结了一下 xff0c 产生大泥球的主要原因有下面这些原因 xff1a xff08 1 xff09 一次性代码 xff08 2 xff09 碎片式增长 xff08 3 xff09 为了让软件不出问题 xff08 4 xff09 Copy p
  • JSON是什么

    提起 JSON xff0c 作为如今最受欢迎的数据交换格式 xff0c 可以说是无人不知 无人不晓了 JSON 全称 JavaScript Object Notation xff08 JS 对象简谱 xff09 xff0c 自诞生之初的小目
  • <操作系统> 售票员司机问题(信号量) C语言实现

    问题描述 xff1a 思路 xff1a 代码 xff1a span class token macro property span class token directive keyword include span span class
  • 展锐T7520(ANDROID 11) boot.img解包

    1 make unpack bootimg 或者 prebuilts build tools linux x86 bin ninja f out combined ninja unpack bootimg 2 export PATH 61
  • 连通图(求桥的数量)

    桥 xff1a 连通图中存在的必经之路 xff0c 我们成为桥 xff0c 如果把此路断开 xff0c 连通图便会变成两个图 判断是否是桥的方式 low v gt dfn u 题目链接 AC代码 xff1a include lt bits
  • Lotti引发的java.lang.StackOverflowError

    在项目中将lottie从2 8 0版本进行升级至4 2 0版本后 xff0c 突然出现堆栈内存错误 xff0c 最后查找原因是因为在onAnimationEnd 回调方法中调用resumeAnimation 方法 xff0c 而在resum
  • Ubuntu 服务器操作笔记 之 安装SSH

    1 gt sudo apt get install openssh server 安装SSH 2 gt sudo ps e grep ssh 查询 SSH是否启动 3 gt sudo etc init d ssh start 如果没有 则启
  • 分析APP的安装流程 API29

    先总结一下安装流程 xff0c 以及比较重要的类 PackageInstallerActivity java xff1a 在文件管理器里点击apk后就会调用该类 xff0c 主要用于显示要安装的apk的一些权限信息 InstallAppPr