Analyze the installation process API29 for APP

Posted by 2705ap on Tue, 28 Sep 2021 19:27:06 +0200

First summarize the installation process and the more important classes

PackageInstallerActivity.java:

This class is invoked when an apk is clicked in the file manager, primarily to display some permission information for the apk to be installed.

InstallAppProgress.java:

When you have finished viewing all the permissions, the class is called after a point installation to show the progress of the installation, and PackageManagerService is silently installing the application.

ApplicationPackageManager.java:

This is a subclass of PackageManager, and what we get from mContext.getPackageManager is actually an object of ApplicationPackageManager, whose parent PackageManager is an abstract class whose external methods are defined inside.

PackageParser.java:

Parse the app, mainly parse AndroidManifest.xml in the apk, parse the four components and put permission information into memory, and write to packages.xml and package.list (/data/system).

AssetManager.java:

Take AndroidManifest.xml out of the app and parse it for PackageParser.java.

DefaultContainerService.java:

This service checks the storage status to get the appropriate installation location.

Installer.java:

PackageManagerService calls it to perform the installation, encapsulates the data passed from the PackageManagerService into commands, and then lets the underlying Installer execute.

PackageManagerService.java:

Manage app installation, move, uninstall, query, etc.

Rough steps

a. Call PackageInstallerActivity -- check for applications from unknown sources here

b. Click the Install button to jump to the InstallInstalling interface

c. Stream files to PMS through PackageInstallerSession

d.PMS sends INIT_COPY command ready to copy apk

e.HandlerParams executes the startCopy method (handleStartCopy and handleReturnCode methods)

(e.a) First check that the file and cid are generated, and then set installFlags if they are generated

Check the size of the space and free up unused space if there is not enough space

e.c overwrites the file in the original installation location, determines the return value of the function based on the result, and sets installFlags

(e.e) Determine if there are any installed package validators and, if so, delay detection

e.f handleReturnCode performs a real copy operation--copy APK and so files to/data/directory below

Once the copy of f.APK is complete, go to the APK loading phase core method installPackagesTracedLI

F.A prepareAnalyze any current installation state, analyze the package, and initially validate it -- parse the app, mainly parse AndroidManifest.xml in the apk, parse the four components and put permission information into memory, and finally write to packages.xml and package.list (/data/system)

f.b Scan: Ask for parsed packages, taking into account the context collected in prepare

f.c Reconcile reconciliation: Verify scanned packages in each other's context and current system state to ensure successful installation

f.d Commit Submit: Submit all scanned packages and update the system state. This is the only place in the installation stream where the system state can be modified and all predictable errors must be identified before this stage

f.e Install executePostCommitSteps

g. Finally invoke Installer's createUserData and install methods to connect the underlying Installed service to install

h.dexopt optimization - will use dexopt to optimize the DEX file in base.apk to odex, stored in / data/dalvik-cache, if in ART mode, will use dex2oat to optimize to oat file also stored in this directory

The directories that will be affected by the installation process are all clear

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

Flowchart 1.Click Install to APK Copy

Flowchart 2.Copy Completion-Installation

The following is a detailed analysis process:

Usually start the installation process with the following code

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 are authorities declared in provider s
            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 adds a authorities to properly pull up the PackageInstallerActivity page

Here is an implicit start activity to see where the corresponding acitivty is

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

The code above actually starts the activity InstallStart, which is the entry to the 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();
    }

Although you see the PackageInstallerActivity, when you analyze it, you don't go directly to the code in Note 1

Note 1 identifies whether Intent's Action is CONFIRM_PERMISSIONS, apparently not based on the scenario used in this article, and then looks down.

Note 2 determines whether packageUri is empty or not, and Note 3 determines whether Uri's Schema protocol is content or if it is InstallStaging or PackageInstallerActivity.

In the application scenarios in this article, Android 7.0 and later will use FileProvider to handle URI s. FileProvider will hide the true path of the shared file and convert the path to content://Uri Path, which jumps to 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
        }
    }

Note 1 If mStagedFile of File type is null, mStagedFile is created and used to store temporary data.

Comment 2 starts StagingAsyncTask and passes in the Uri of the content protocol, as shown below. 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();
            }
        }
    }
}

The doInBackground method writes the contents of the packageUri (Uri of the content protocol) into the mStagedFile.

If the write is successful, the onPostExecute method jumps to the PackageInstallerActivity and passes in the mStagedFile.

Circled back to PackageInstallerActivity, where you can see that InstallStaging plays a major role in transforming the Uri of the content protocol into the File protocol.

Then jump to PackageInstallerActivity, which will start the installation process just like previous versions (before Android 7.0)

Finally into 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);
        ...
        //Preprocessing based on Uri's Schema
        boolean wasSetUp = processPackageUri(packageUri);//1
        if (!wasSetUp) {
            return;
        }
        bindUi(R.layout.install_confirm, false);
        //Determine if the application is from an unknown source and initialize the installation directly if the Allow installation from an unknown source option is turned on
        checkIfAllowedAndInitiateInstall();//2
    }

Start by installing the various objects you need, such as PackageManager, IPackageManager, AppOpsManager, UserManager, etc.

Then when you install it, you need to click the Start Installation button to go there directly

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

There may be differences between versions of the code, but the next obvious step is to call the startInstall method

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 method, the main work is to build intent, pass in data, open InstallInstalling.java

InstallInstalling's onCreate method, the main work is

1. Determine if it is the current application

2. If savedInstanceState is not null, get the previously saved mSessionId and mInstallId, where mSessionId is the session id of the installation package and mInstallId is the waiting installation event id

3. Register an observer with InstallEventReceiver based on mInstallId, and launchFinishBasedOnResult receives a callback for the installation event, closing the current activity regardless of success or failure. If savedInstanceState is null, the logic of the code is similar

4. Create SessionParams, which is used to assemble params on behalf of the parameters of the installation session

5. Lightweight parsing of packages (APK s) based on mPackageUri and assigning parsed parameters to SessionParams

6. Register an observer with InstallEventReceiver and return a new mInstallId, where InstallEventReceiver inherits from BroadcastReceiver and receives installation events and calls back to EventResultPersister.

7.PackageInstaller's createSession method uses IPackageInstaller to communicate with PackageInstallerService internally, and ultimately calls PackageInstallerService's createSession method to create and return 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 {
        //Indicates that installing the current application means updating yourself
        //Installing an application with the same name as the application should be faster or an exception will be thrown
            getPackageManager().installExistingPackage(appInfo.packageName);
            launchSuccess();
        } catch (PackageManager.NameNotFoundException e) {
            launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
        }
    } else {
        //Install a new application    	
        //Create a corresponding File from the mPackageURI
        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. If savedInstanceState is not null, get the previously saved mSessionId and mInstallId, where mSessionId is the session id of the installation package and mInstallId is the waiting installation event 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. Register an observer with InstallEventReceiver based on mInstallId and launch FinishBasedOnResult will receive a callback for the installation event.
                //The current Activity(InstallInstalling) is closed regardless of whether the installation succeeds or fails. If savedInstanceState is null, the logic of the code is similar
                InstallEventReceiver.addObserver(this, mInstallId,
                        this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                // Does not happen
            }
        } else {
            //3. Create SessionParams, which is used to assemble params on behalf of the parameters of the installation session
            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. Lightweight parsing of packages (APK s) based on mPackageUri and assigning parsed parameters to 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. Register an observer with InstallEventReceiver and return a new mInstallId.
                //InstallEventReceiver inherits from BroadcastReceiver and receives installation events and calls back to 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's createSession method uses IPackageInstaller to communicate with PackageInstallerService internally.
                //The final call is to the PackageInstallerService's createSession method to create and return the mSessionId
                //The final step is to create an mSessionId by throwing in the assembled parameters
                mSessionId = getPackageManager().getPackageInstaller().createSession(params);
            } catch (IOException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        }
 
        mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
 
        mSessionCallback = new InstallSessionCallback();
    }
}

Then go into its onResume method, call the onPostExecute() method to create an AsyncTask and execute it

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

In this task, the apk is written to the PackageInstaller.Session via the io stream

Then call the commit method of PackageInstaller.Session in onPostExecute() to install it

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;
                            }
    			 //Write APK information into PackageInstaller.Session as an IO stream
                            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);
    	    //Call the commit method of PackageInstaller.Session to install
                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);

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

commit method entering session

[PackageInstaller.Session.java] commit
public void commit(@NonNull IntentSender statusReceiver) {
    try {
        //Call the commit method of PackageInstallerSession to enter the java framework layer
        mSession.commit(statusReceiver, false);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

The type of mSession in commit() is IPackageInstallerSession, which means that to communicate between processes through IPackageInstallerSession, the commit method of PackageInstallerSession is invoked, so the code logic goes to the Java framework level.

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

The markAsCommitted method encapsulates the package information as a PackageInstallObserverAdapter, which is defined in PKMS and returned to commit().

Send a message of type MSG_COMMIT to Handler

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 is processed in handler and enters handleCommit()

public boolean handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_COMMIT:
            handleCommit();
            break;
    }
}
 
private void handleCommit() {
    ...
    List<PackageInstallerSession> childSessions = getChildSessions();
 
    try {
        synchronized (mLock) {
            //Finally, installStage() is called to enter the 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);
    }
}

The onPackageInstalled method of PackageInstallObserver is first called in commitNonStagedLocked(), which calls back the exception information of PackageManagerException that appears in the Complete method

PackageInstallObserverAdapter.

Finally, installStage() is called to enter the 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);
    }
}

Install Stage Method Entering PKMS

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. Created a message of type INIT_COPY
    final Message msg = mHandler.obtainMessage(INIT_COPY);
 
    //2. Create InstallParams, which corresponds to the package's installation data
    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. Send InstallParams by message.
    mHandler.sendMessage(msg);
}
 
Yes INIT_COPY Processing of messages
[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");
                //Perform APK copy action
                params.startCopy();
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            break;
        }
    }
}

startCopy calls handleStartCopy

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

1. First check that the file and cid are generated, and if they are, set installFlags.

2. Check the size of the space and free up unnecessary space if there is not enough space.

3. Overwrite the file in the original installation location, determine the return value of the function based on the return result, and set installFlags

4. Determine if there are any installed package validators and, if so, delay detection. There are three main steps: first create a new validation Intent, then set up the relevant information, then get a list of validators, and finally send a validation Intent to each validator.

 
[PackageManagerService.java]
public void handleStartCopy() {
    // 1. Decide whether to install in your phone or sdcard and set the corresponding flag bit
    if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
        } else {
            throw new IllegalStateException("Invalid stage location");
        }
    }
    ...
    //2. Check the size of the space and free up unnecessary space if there is not enough space.
    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. Overwrite the file in the original installation location, determine the return value of the function based on the results returned, and set 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. Determine if there are any package validators installed and, if so, delay detection. There are three main steps: First create a new validation Intent, and then set up the relevant information.
    //A list of validators is then obtained, and a validation Intent is sent to each validator.
    //4.1 Construction Verification 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 Get a list of validators
    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 Send validation Intent to each validator
                final Intent sufficientIntent = new Intent(verification);
                sufficientIntent.setComponent(verifierComponent);
                mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
            }
        }
    }
    ...
}

Send intent to validator client and copy will not work until validation is successful. If no validator exists, copy directly

Return to the handleReturnCode method of startCopy

The most important steps in this approach are copyApk and processPendingInstall

Call copyApk to copy 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);
    }
}

The APK copy call stack is as follows:

copyApk

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

doCopyApk 21 lines and 33 lines, copyApk and so files, respectively

Most important of all

1. Copy baseApk to / data/app/com.xx.xx-XXXxxXXXx==/this directory

2. Copy the so file to the directory under 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(), which is / data/app/com.xx.xx-XXXxxXXXxx==/this directory
    //The default file name is 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);
        //Copy so file to
        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;
}

CopPackage calls copyFile to copy the APK to directories such as/data/app through file stream operations

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

Once the APK copy is complete, it enters the real installation, which loads the code flow as follows:

This is the processPendingInstall function above

private void processPendingInstall(final InstallArgs args, final int currentStatus) {
    if (args.mMultiPackageInstallParams != null) {
        args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
    } else {
        //1. Set up installation parameters
        PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
        //2. Create a new thread, process installation parameters, and install
        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. Pre-install, check package status, ensure environment ok, if environment is not ok, then clean up copied files
                 //Mainly delete files from previous installation failures.
                  // During the pre-installation phase, check the status of the installation package to ensure that the installation environment is working properly. If there is a problem with the installation environment, clean up the copy files
                request.args.doPreInstall(request.installResult.returnCode);
            }
            synchronized (mInstallLock) {
                //2.Install PackagesTracedLI is a call to install PackagesLI during the installation phase.
                installPackagesTracedLI(installRequests);
            }
            for (InstallRequest request : installRequests) {
                //3. End of installation, clear unnecessary information if previous installation fails
                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));
        }
    });
}

Look directly at 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);
        }
    }

Enter the installPackagesLI method

The method is divided into four phases

1. Prepare phase, analyze any current installation status, resolve packages, and initially validate them

2. Scan phase, parse the package according to the context collected in the preparation phase, mainly to generate the PackageSetting data structure. Call

scanPackageTracedLI() scans the contents of a package, which has actually been parsed once, where you can get the cache directly

3. During the integration validation phase, there is also a need to reconcile the results of multiple install apk s, which are usually installed at the same time as the install-multi-package.

reconcilePackagesLocked():

4. Confirm the submission phase. After submission, the application will be released in full

The above 4 steps, each step is very much, reference Android APK Installation Process (4)--APK Loading - Brief Book

 @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");
                	 //In the preparation phase, check packages and analyze integrity; check SDK versions, static libraries, etc; check signatures; set permissions;
                    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 {
                 	 //Scan 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");
                    //Integration Verification Phase
                    //After 2-2 generates the PackageSetting and PackagageParser.Package data structures,
                    //There is also a need to reconcile the results of multiple installation apks, typically when installing-multi-package, multiple apks are installed at the same time.
                    //The calling method is 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");
                    //Confirm the submission stage,
                    //Submit all scanned packages and update the system state. This is the only place where the system state can be modified in the installation stream.
                    //All predictable errors must be identified before this stage. After these steps, two core data structures have been generated.
                    //It is not added to the container (PackageManagerService.mPackages and PackagageManagerService.mSettings.mPackage). 
                    //So the components inside the package can not be queried or started yet. So we need to call them
    		 //commitPackagesLocked() for submission
                    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);
                }
            }
                 //Perform Apk Installation Complete
                 executePostCommitSteps(commitRequest);
                 ...        
    }

The core approach here is executePostCommitSteps(commitRequest);

Install and prepare APP data

preparePackageLI method is very long, part of the summary

               PackageParser pp = new PackageParser();
                final PackageParser.Package pkg;
                // 1. parsePackage
                pkg = pp.parsePackage(tmpPackageFile, parseFlags);
                
                // 2.Verify installation package signature
                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.Set up related permissions, build, migrate permissions   
            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. Generate installation package Abi(Application binary interface, 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");
                    }   
                   //Freeze apk 
        		final PackageFreezer freezer =
              	freezePackageForInstall(pkgName, installFlags, "installPackageLI");                                  
            }

Finally executePostCommitSteps(commitRequest) method is executed

2 main things to do

1. Install and prepare APP data prepareAppDataAfterInstallLIF

2.dexopt optimization

/**
 * 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 also has a series of calls

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

final Installer mInstaller;
private void prepareAppDataLeafLIF(...) {
   // Final invocation of system service Installer installation
   ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                    appId, seInfo, app.targetSdkVersion);     
}   

public class Installer extends SystemService {
   ...
}

At the end of the entire apk installation process, after the actual successful installation, an App installation broadcast ACTION_PACKAGE_ADDED will be sent. The mobile desktop application registers this broadcast and displays the apk startup icon on the desktop when the App installation is successful.

  • Clicking on APK Installation launches the PackageInstaller Activity and enters InstallInstalling to display application information
  • Click Install on the page to save the APK information into PackageInstaller.Session and pass it to PMS
  • PMS does two things, copying installation packages and loading code
  • During copying the installation package, Service will be opened to copy the APK, check the installation path of the apk, and the status of the package.
  • Copy completion exists as base.apk under / data/app package name
  • During code loading, the APK will continue to be parsed and the contents of the manifest file will be stored in the PMS
  • Signature verification of apk
  • After successful installation, update the application settings permissions, send broadcast notifications to the desktop to display the APP icon, and delete installation packages and various cache files if installation fails
  • Perform dex2oat optimization

Thank:

Android 10.0 PackageManagerService (IV) APK Installation Process - [Path to Android]_Column for IngresGe - CSDN Blog

Android APK Installation Process (4)--APK Loading - Brief Book

Speak about the APK installation process - know

Overview of APK Installation Process - Little White Dragon in Wave - Blog Park

Application Installation Process|The Wolf in the Wind Blog

Android Package Management Mechanism (1) Initialization of PackageInstaller - Brief Book

APK Installation Process - Brief Book

Installation Process of APK in Android_Column of mockingbirds-CSDN Blog

Installation Process-Brief Book

Topics: Java Android