APK installation process analysis

The code for initiating the installation of APK on the application side is generally as follows:

 Intent installintent = new Intent();
 installintent.setAction(Intent.ACTION_VIEW);
 installintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 installintent.setDataAndType(xxx,"application/vnd.android.package-archive");
 context.startActivity(installintent);

Installing the apk is essentially done through the system application packageInstaller.apk. Therefore, what we need to look at is the source code of packageInstaller. Open the AndroidManifest.xml file of PackageInstaller, we will find that the Activity to be started by the above Intent matches InstallStart, which is also the entrance of the PackageInstaller application:

<activity android:name=".InstallStart"
        android:theme="@android:style/Theme.Translucent.NoTitleBar"
        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="content"/>
            <data android:mimeType="application/vnd.android.package-archive"/>
        </intent-filter>
                         
        <intent-filter android:priority="1">
            <action android:name="android.intent.action.INSTALL_PACKAGE"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="package"/>
            <data android:scheme="content"/>
        </intent-filter>
        
        <intent-filter android:priority="1">
            <action android:name="android.content.pm.action.CONFIRM_INSTALL"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

InstallStart.java

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

Jump to InstallStart.java

The main work:

  1. Determine whether a dialog box to confirm the installation needs to be displayed. If so, jump PackageInstallerActivity; if not, jump to 3
final boolean isSessionInstall =PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
  1. Determine whether the "Trust unknown sources" option is checked. If it is not checked, determine whether the version is smaller than Android 8.0, and if so, cancel the installation; otherwise, determine whether the version is greater than Android 8.0 and does not have permission to set, if so, cancel the installation REQUEST_INSTALL_PACKAGES.
isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
  1. Determine the Scheme protocol of the Uri, if content://so, jump InstallStaging, if package://so, jump PackageInstallerActivity, but InstallStagingin reality StagingAsyncTask, the Uri of the content protocol will be converted to the File protocol, and then jump toPackageInstallerActivity

  2. finish current InstallStartinterface

PackageInstallerActivity.java

The main work:

  1. Build confirmation installation dialog
protected void onCreate(Bundle icicle) {
	...
	bindUi();
}
  1. Various objects required for initial installation, such as PackageManager, IPackageManager, AppOpsManager, UserManager, PackageInstaller, etc.

  2. Different processing is performed according to the passed scheme protocol (package/file), mainly to obtain the PackageInfo object. PackageInfo contains information about the Android application, such as application name, permission list, etc., and creates an AppSnippet object through PackageInfo, which contains the icon of the application to be installed. and title

 boolean wasSetUp = processPackageUri(packageUri);
  1. Use checkIfAllowedAndInitiateInstall()to check the APK source and display the "Unknown Source APK Installation" dialog box. When you click the "settings" button, you will jump to the settings page.
  Intent settingsIntent = new Intent();
    settingsIntent.setAction(
    Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
    Uri packageUri = Uri.parse("package:" + argument);
    settingsIntent.setData(packageUri);
    try{
        getActivity().startActivityForResult(settingsIntent, REQUEST_TRUST_EXTERNAL_SOURCE);
    } catch(ActivityNotFoundException exc){
        Log.e(TAG, "Settings activity not found for action: " + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
    }
  1. After turning on the "Allow installation from unknown sources" setting option PackageInstallerActivity, return to the "Allow installation from unknown sources" setting option. In onActivityResult(), call initiatenstall()the check application list to determine whether the application has been installed. If it is installed, it will prompt that the application has been installed. The user will decide whether to replace it, and then display the installation confirmation interface.

  2. In the installation interface, click the OK button to confirm the installation, and it will call to startInstallstart the installation work.

  3. startInstallThe method is used to jump to InstallInstallingand close the currentPackageInstallerActivity

InstallInstalling.java

The main work:

  1. Get , then get the objects PackageManagerin itPackageInstaller

  2. PackageInstallerCreate a package for transmitting the APK installation package and installation information in Sessionand returnSessionId

  3. Create an asynchronous task and use the previously returned open end InstallingAsyncTaskin the asynchronous taskSessionIdPackageInstallerSession

  4. By Sessionpassing the APK installation package and related information

  5. onPostExecuteExecuted in asynchronous task methodsession.commit(pendingIntent.getIntentSender())

  6. InstallEventReceiverListen to the callbacks of successful and failed installations by registering observers and jump to the corresponding result page.

     <receiver android:name=".InstallEventReceiver"
        android:permission="android.permission.INSTALL_PACKAGES"
        android:exported="true">
            <intent-filter android:priority="1">
                <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
            </intent-filter>
     </receiver>

PackageInstallerSession

session.commit()The method is transferred to PackageInstallerSessionthe service through binder across processes.

The interface defined through the aidl file is:

/frameworks/base/core/java/android/content/pm/IPackageInstallerSession.aidl

IPackageInstallerSessionThe implementation class is:

/frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
//PackageInstallerSession.java
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    ...
    dispatchStreamValidateAndCommit();
}

private void dispatchStreamValidateAndCommit() {
    mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}

The main work

  1. Sent MSG_STREAM_VALIDATE_AND_COMMITmessage tomHandler
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;
    }
};
  1. The final execution is handleStreamValidateAndCommit()in , and then a message is sent inside MSG_INSTALL. This execution is in handleInstall():
//PackageInstallerSession.java
    public void handleStreamValidateAndCommit() {
        ...
         mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
    }
    rivate 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 handleInstall() {
        ...
        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();

        installNonStagedLocked(childSessions);
        ...
    }

    private void installNonStagedLocked(List<PackageInstallerSession> childSessions) {
        final PackageManagerService.ActiveInstallSession installingSession = makeSessionActiveLocked();
         ...
         //安装走到了 PMS 中
         mPm.installStage(installingSession);
      ...
    }
  1. Finally, the installation process came to an PMSend installStage().

So far, PackageInstallerthe installation has only been maintained Sessionand the installation package has been written to Session. The real installation process is PMSto be executed.

PackageManagerService.java

The main work

  1. installStage()The object is created InstallParams, messagethe message is passed in; the message is sent INIT_COPYto mHandler.
//PMS
void installStage(ActiveInstallSession activeInstallSession) {
    ...
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final InstallParams params = new InstallParams(activeInstallSession);
    msg.obj = params;
    mHandler.sendMessage(msg);
}

mHandlerYes PackageHandler, this is PackageManagerServicecreated in the constructor.
mHandlerThe processing INIT_COPYis InstallParams.startCopy():

void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                // 得到安装参数  HandlerParams 
                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");
                    // 2 调用  startCopy() 方法
                    params.startCopy();
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                break;
            }
            ...
        } 
}          

HandlerParamsThe method of is called inside startCopy(). HandlerParamsYes PMSinternal abstract class. PMSThe inner class InstallParamsis MultiPackageInstallParamsits implementation class.

//HandlerParams.java
final void startCopy() {
    handleStartCopy();
    handleReturnCode();
}

handleStartCopy()、handleReturnCode()They are all abstract methods. So, which one is the object here? After previous analysis, we know that it is InstallParams.

  1. handleStartCopy() scans the apk's lightweight information, installation space, apk integrity verification, etc. Create FileInstallArgs object, assign value and return code.

  2. handleReturnCode() first calls the copyApk() method of InstallArgs to copy the APK, and then installs the APK;

  void handleReturnCode() {
            if (mVerificationCompleted && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                ...
                //如果前面校验ok,这里执行apk拷贝
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    mRet = mArgs.copyApk();
                }
                //执行安装
                processPendingInstall(mArgs, mRet);
            }
        }
    }

FileInstallArgsInherited InstallArgsclass. copyApk()Called internally doCopyApk();

doCopyApk() first obtains the copy file path: /data/app , uses PackageManagerServiceUtils.copyPackage() to copy the APK, and then copies the .so file. In other words, the APK sent to the Session staging directory data/app-staging is copied to /data/app.

/data/app-stagging Copy the apk from the temporary directory to /data/appthe directory.

//拷贝apk
    int ret = PackageManagerServiceUtils.copyPackage(
            origin.file.getAbsolutePath(), codeFile);
    if (ret != PackageManager.INSTALL_SUCCEEDED) {
        Slog.e(TAG, "Failed to copy package");
        return ret;
    }

Next look at executing the installationprocessPendingInstall(mArgs, mRet)

  1. processPendingInstall()Called processInstallRequestsAsync(), processInstallRequestsAsync()perform pre-installation verification, start installing the APK, and send a broadcast notifying the installation result.
  private void processInstallRequestsAsync(boolean success, List<InstallRequest>installRequests) {
        mHandler.post(() -> {
            if (success) {
                for (InstallRequest request : installRequests) {
                    //安装前检验:returnCode不为INSTALL_SUCCEEDED就移除拷贝的apk等
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    //安装:解析apk
                    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));
            }
        });
    }

Here we first focus on the installation process and installPackagesTracedLI()then go to installPackagesLI():

The installation process is divided into four stages:

  1. Prepare , analyze the current installation status, parse the package and perform initial verification: use PackageParser2.parsePackage() in preparePackageLI() to parse AndroidManifest.xml to obtain the four major components and other information; use ParsingPackageUtils.getSigningDetails() to parse signature information; rename the package final path etc.
    prepareResult = preparePackageLI(request.args, request.installResult);
  1. Scan and further analyze based on the context of the package information parsed in the preparation stage: confirm the authenticity of the package name; verify the validity of the package based on the parsed information (whether there is signature information, etc.); collect apk information - PackageSetting, apk's static library/dynamic library Information etc.
    final List<ScanResult> scanResults = scanPackageTracedLI( prepareResult.packageToScan, prepareResult.parseFlags, prepareResult.scanFlags, System.currentTimeMillis(), request.args.user);
  1. Check and verify the scanned package information to ensure successful installation: mainly covering the signature matching verification of the installation.
    reconciledPackages = reconcilePackagesLocked( reconcileRequest, mSettings.mKeySetManagerService);

4. Submit , submit the scanned package, and update the system status: add PackageSetting to mSettings of PMS, add AndroidPackage to mPackages of PMS, add key set to the system, add application permissions to mPermissionManager, and add four major component information to mComponentResolver. This is the only place where system state can be modified and all predictable errors are detected.
commitRequest = new CommitRequest(reconciledPackages, mUserManager.getUserIds()); commitPackagesLocked(commitRequest);

After the installation is completed, it is called to executePostCommitSteps()prepare app data and perform dex optimization:

  • prepareAppDataAfterInstallLIF(): Provide directory structure /data/user/用户ID/包名/cache(/data/user/user ID/package name/code_cache)
  • mPackageDexOptimizer.performDexOpt(): dexopt is the verification and optimization operation of the dex file. The optimization result of the dex file becomes an odex file. This file is very similar to the dex file, but uses some optimization opcodes (such as optimizing virtual instructions, etc.).

These two operations are ultimately performed using the corresponding methods of Installer . As mentioned earlier, the instance of was passed in when PMS was created Installer, and Installerinherited from SystemServiceis also a system service. Take a look here:

  • In the onStart() method of Installer, the mInstalld instance is obtained through the Binder object of installd. It can be seen that this is an IPC operation, that is, the Installer IPC in the System_Server process to the daemon process with root privileges . Directories like /data/user must be created with root permissions.
  • Many methods of Installer are used in PMS. Installer is the Java API interface provided by the Java layer, and Installd is the Daemon process with root permissions started in the init process.

After the installation is completed here, go back to PMS processInstallRequestsAsync(), and finally call restoreAndPostInstall()for backup, possible rollback, and send the installation completion first to turn off the broadcast:

private void restoreAndPostInstall(
        int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
    ...
    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
    mHandler.sendMessage(msg);
    ...
}

mHandlerStill PackageHandleran object.

mHandlerUse PMS handlePackagePostInstall()method to process POST_INSTALL:

  • Send Intent.ACTION_PACKAGE_ADDEDand other broadcasts based on the installation results. After receiving the broadcast, the desktop Launcher will add the App's Icon to the desktop.
  • Call the () method of the instance PackageInstallSessionsaved in , and finally send a notification of successful installation to be displayed in the notification bar. Send the broadcast defined in , and finally the page displays successful installation/failed installation based on the results.IPackageInstallObserver2onPackageInstalledIntentSenderInstallInstallingInstallInstalling

At this point, the APK installation process has been sorted out. Let’s review:

  • The APK is written to the Session and the package information and APK installation operation are submitted to the PMS;
  • In PMS, first copy the APK to /data/app, then use PackageParser2 to parse the APK to obtain the four major components, collect signatures, PackageSetting and other information, and perform verification to ensure successful installation;
  • Then submit the information package to update the system status and PMS memory data;
  • Then use Installer to prepare the user directory /data/user and perform dexOpt;
  • Finally, the installation result is sent to notify the UI layer.

おすすめ

転載: blog.csdn.net/jxq1994/article/details/132875496