Android中apk安装过程源码解析

news2024/11/14 13:51:30

本文中使用的Android源码基于Android 14

1 三方应用安装apk调用方法

public void installApk() {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    /*
	 * 自Android N开始,是通过FileProvider共享相关文件,但是Android Q对公
	 * 有目录 File API进行了限制,只能通过Uri来操作
	 */
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
        // filePath是通过ContentResolver得到的
        intent.setDataAndType(Uri.parse(filePath),
                              "application/vnd.android.package-archive");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Uri contentUri = FileProvider.getUriForFile(mContext,
                                "com.test.file.fileProvider", file);
        intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
    } else {
        intent.setDataAndType(Uri.fromFile(file), 
                              "application/vnd.android.package-archive");
    }
    startActivity(intent);
}

然后再AndroidManifest.xml中声明权限

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> 

2 PackageInstaller流程

2.1弹出安装对话框

进入PackageInstaller后,首先会拉起PackageInstallerActivity。

//framework/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@Override
protected void onResume() {
    super.onResume();

    if (mLocalLOGV) Log.i(TAG, "onResume(): mAppSnippet=" + mAppSnippet);

    if (mAppSnippet != null) {
        // load dummy layout with OK button disabled until we override this layout in
        // startInstallConfirm
        bindUi();
        checkIfAllowedAndInitiateInstall();
    }

    if (mOk != null) {
        mOk.setEnabled(mEnableOk);
    }
}

PackageInstallerActivity首先创建Ui,然后检查权限。

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) {
                                 setActivityResult(RESULT_OK);
                                 finish();
                             } else {
                                 startInstall();
                             }
                         }
                     }, null);
    mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
                     (ignored, ignored2) -> {
                         // Cancel and finish
                         setActivityResult(RESULT_CANCELED);
                         finish();
                     }, null);
    setupAlert();

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

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

/**
 * Check if it is allowed to install the package and initiate install if allowed.
 */
private void checkIfAllowedAndInitiateInstall() {
    if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
        if (mLocalLOGV) Log.i(TAG, "install allowed");
        initiateInstall();
    } else {
        handleUnknownSources();
    }
}

在checkIfAllowedAndInitiateInstall里面,狐妖是检查是否有安装来自未知来源的权限,如果有权限,那么在initiateInstall中就设置确定按钮为enable和visible。

在bindUi里面,当点击确认时,开始安装流程。

private void startInstall() {
    String installerPackageName = getIntent().getStringExtra(
        Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    int stagedSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);

    // 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);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != Process.INVALID_UID) {
        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);
    }
    if (stagedSessionId > 0) {
        newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
    }
    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
    startActivity(newIntent);
    finish();
}

这里startActivity启动的是InstallInstalling。

2.2 安装进度界面显示InstallInstalling

继续看InstallInstalling的启动。首先我们看下InstallInstalling在perfetto中的流程。
在这里插入图片描述

对应代码为:

//framework/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    if (savedInstanceState != null) {
        ...
    } else {
        try {
            mInstallId = InstallEventReceiver
                .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                             this::launchFinishBasedOnResult);
        } catch (EventResultPersister.OutOfIdsException e) {
            launchFailure(PackageInstaller.STATUS_FAILURE,
                          PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
        }
}

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

    // This is the first onResume in a single life of the activity
    if (mInstallingTask == null) {
        PackageInstaller installer = getPackageManager().getPackageInstaller();
        PackageInstaller.SessionInfo sessionInfo =
            installer.getSessionInfo(mSessionId);

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

首先在onCreate中会创建安装中的Ui界面,然后注册InstallEventReceiver来监听安装结果,并在launchFinishBasedOnResult中将接收到的安装结果进行处理。然后在onResume中开启安装session。

/**
 * Send the package to the package installer and then register a event result observer that
 * will call {@link #launchFinishBasedOnResult(int, int, String, int)}
 */
private final class InstallingAsyncTask extends AsyncTask<Void, 
	Void, PackageInstaller.Session> {
    volatile boolean isDone;

    @Override
    protected PackageInstaller.Session doInBackground(Void... params) {
        try {
            return getPackageManager().getPackageInstaller().openSession(mSessionId);
        } catch (IOException e) {
            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 | PendingIntent.FLAG_MUTABLE);

            session.commit(pendingIntent.getIntentSender());
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        } else {
            getPackageManager().getPackageInstaller().abandonSession(mSessionId);

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

3 建立安装的Session

3.1 建立Session

在doInBackground中进行PackageInstaller#openSession,然后在onPostExecute中提交会话结果,来看PackageInstaller#openSession。

//framework/base/core/java/android/content/pm/PackageInstaller.java
/**
 * Open an existing session to actively perform work. To succeed, the caller
 * must be the owner of the install session.
 *
 * @throws IOException if parameters were unsatisfiable, such as lack of
 *             disk space or unavailable media.
 * @throws SecurityException when the caller does not own the session, or
 *             the session is invalid.
 */
public @NonNull Session openSession(int sessionId) throws IOException {
    try {
        try {
            return new Session(mInstaller.openSession(sessionId));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (RuntimeException e) {
        ExceptionUtils.maybeUnwrapIOException(e);
        throw e;
    }
}

这里调用了mInstaller.openSession(sessionId),并new了一个Session。

public static class Session implements Closeable {}

mInstaller是IPackageInstaller,也就是PackageInstaller的服务端PackageInstallerService。

//framework/base/services/core/java/com/android/server/pm/PackageInstallerService.java
@Override
public IPackageInstallerSession openSession(int sessionId) {
    try {
        return openSessionInternal(sessionId);
    } catch (IOException e) {
        throw ExceptionUtils.wrap(e);
    }
}

private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
    synchronized (mSessions) {
        final PackageInstallerSession session = mSessions.get(sessionId);
        if (!checkOpenSessionAccess(session)) {
            throw new SecurityException("Caller has no access to session " + sessionId);
        }
        session.open();
        return session;
    }
}

checkOpenSessionAccess进行简单校验之后,直接调用PackageInstallerSession的open()。

//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void open() throws IOException {
    activate();
    boolean wasPrepared;
    synchronized (mLock) {
        wasPrepared = mPrepared;
        if (!mPrepared) {
            if (stageDir != null) {
                prepareStageDir(stageDir);
            } else if (params.isMultiPackage) {
                // it's all ok
            } else {
                throw new IllegalArgumentException("stageDir must be set");
            }

            mPrepared = true;
        }
    }

    if (!wasPrepared) {
        mCallback.onSessionPrepared(this);
    }
}

这里也很简单,在prepareStageDir里面进行目录创建和selinux相关配置。

这里有一个疑问,stageDir是什么?

/** Staging location where client data is written. */
final File stageDir;

它是一个file,那它的路径是哪里呢?继续找

public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
            Context context, PackageManagerService pm,
            PackageSessionProvider sessionProvider,
            SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager,
            int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
            SessionParams params, long createdMillis, long committedMillis,
            File stageDir, String stageCid, InstallationFile[] files,
            ArrayMap<String, PerFileChecksum> checksums,
            boolean prepared, boolean committed, boolean destroyed, boolean sealed,
            @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
            boolean isFailed, boolean isApplied, int sessionErrorCode,
            String sessionErrorMessage) {
    
}

public static PackageInstallerSession readFromXml(@NonNull TypedXmlPullParser in,
            @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
            @NonNull PackageManagerService pm, Looper installerThread,
            @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
            @NonNull PackageSessionProvider sessionProvider,
            @NonNull SilentUpdatePolicy silentUpdatePolicy)
            throws IOException, XmlPullParserException {
    final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
    final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
    
    InstallSource installSource = InstallSource.create(installInitiatingPackageName,
           	installOriginatingPackageName, installerPackageName, installPackageUid,
            updateOwnerPackageName, installerAttributionTag, params.packageSource);
    return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 silentUpdatePolicy, installerThread, stagingManager, sessionId,
                 userId,installerUid, installSource, params, createdMillis, 
                 committedMillis, stageDir,stageCid, fileArray, checksumsMap,
                 prepared, committed, destroyed, sealed,childSessionIdsArray,
                 parentSessionId, isReady, isFailed, isApplied,
                 sessionErrorCode, sessionErrorMessage);
}

private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
//framework/base/services/core/java/com/android/server/pm/PackageInstallerService.java
private void readSessionsLocked() {
    while ((type = in.next()) != END_DOCUMENT) {
        if (type == START_TAG) {
            final String tag = in.getName();
            if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
                final PackageInstallerSession session;
                try {
                    session = PackageInstallerSession.readFromXml(in,
                               mInternalCallback, mContext, mPm,
                               mInstallThread.getLooper(), mStagingManager,
                               mSessionsDir, this, mSilentUpdatePolicy);
                } catch (Exception e) {
                    Slog.e(TAG, "Could not read session", e);
                    continue;
                }
                mSessions.put(session.sessionId, session);
                mAllocatedSessions.put(session.sessionId, true);
            }
        }
    }
    // After reboot housekeeping.
    for (int i = 0; i < mSessions.size(); ++i) {
        PackageInstallerSession session = mSessions.valueAt(i);
        session.onAfterSessionRead(mSessions);
    }
}

public PackageInstallerService(Context context, PackageManagerService pm,
            Supplier<PackageParser2> apexParserSupplier) {
    ...
    mSessionsFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), 
                 "install_sessions.xml"), "package-session");
    ...
}

public void systemReady() {
    synchronized (mSessions) {
        readSessionsLocked();
        ...
    }
}

//framework/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void systemReady() {
    mInstallerService.systemReady();
}

终于理清楚了,首先在PKMS启动之后的systemReady中,通知PackageInstallerService的systemReady,然后systemReady中读取保存的Sessions,读取的地方是/data/system/install_sessions.xml中的package-session的tag。然后在该tag下读取sessionStageDir即是stageDir。

3.2 预检验apk

在readSessionsLocked最后,会走session.onAfterSessionRead(mSessions)。

//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@AnyThread
void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
    synchronized (mLock) {
        ...
        if (root != null && !root.isStagedAndInTerminalState()) {
            if (isApexSession()) {
                validateApexInstallLocked();
            } else {
                validateApkInstallLocked();
            }
        }
        ...
    }
}

/**
 * Validate install by confirming that all application packages are have
 * consistent package name, version code, and signing certificates.
 * <p>
 * Clears and populates {@link #mResolvedBaseFile},
 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
 * <p>
 * Renames package files in stage to match split names defined inside.
 * <p>
 * Note that upgrade compatibility is still performed by
 * {@link PackageManagerService}.
 * @return a {@link PackageLite} representation of the validated APK(s).
 */
@GuardedBy("mLock")
private PackageLite validateApkInstallLocked() throws PackageManagerException {
    for (File addedFile : addedFiles) {
            final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(
                input.reset(), addedFile,
                ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
    }
}

parseApkLite中传入的flag是PARSE_COLLECT_CERTIFICATES。

//framework/base/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
/**
 * Utility method that retrieves lightweight details about a single APK
 * file, including package name, split name, and install location.
 *
 * @param apkFile path to a single APK
 * @param flags optional parse flags, such as
 *            {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
 */
public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile,
                                                int flags) {
    return parseApkLiteInner(input, apkFile, null, null, flags);
}

private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
                File apkFile, FileDescriptor fd, String debugPathName, int flags) {
    final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();

    XmlResourceParser parser = null;
    ApkAssets apkAssets = null;
    try {
        try {
            apkAssets = fd != null
                ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
                : ApkAssets.loadFromPath(apkPath);
        } catch (IOException e) {
            return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
                               "Failed to parse " + apkPath, e);
        }

        parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);

        final SigningDetails signingDetails;
        if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
            final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
            try {
                final ParseResult<SigningDetails> result =
                    FrameworkParsingPackageUtils.getSigningDetails(input,
                                                                   apkFile.getAbsolutePath(),
                                                                   skipVerify, /* isStaticSharedLibrary */ false,
                                                                   SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
                if (result.isError()) {
                    return input.error(result);
                }
                signingDetails = result.getResult();
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        } else {
            signingDetails = SigningDetails.UNKNOWN;
        }

        return parseApkLite(input, apkPath, parser, signingDetails, flags);
    } catch (XmlPullParserException | IOException | RuntimeException e) {
        Slog.w(TAG, "Failed to parse " + apkPath, e);
        return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                           "Failed to parse " + apkPath, e);
    } finally {
        IoUtils.closeQuietly(parser);
        if (apkAssets != null) {
            try {
                apkAssets.close();
            } catch (Throwable ignored) {
            }
        }
        // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
    }
}

parseApkLiteInner中首先会加载ApkAssets,对应perfetto中加载ApkAssets的load阶段。
在这里插入图片描述

private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
                  File apkFile, FileDescriptor fd, String debugPathName, int flags) {
    parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);

    final SigningDetails signingDetails;
    if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
        final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
        try {
            final ParseResult<SigningDetails> result =
                FrameworkParsingPackageUtils.getSigningDetails(input,                                          apkFile.getAbsolutePath(),skipVerify
                         , /*isStaticSharedLibrary */ false,                      
                         SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
            if (result.isError()) {
                return input.error(result);
            }
            signingDetails = result.getResult();
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    } else {
        signingDetails = SigningDetails.UNKNOWN;
    }
    return parseApkLite(input, apkPath, parser, signingDetails, flags);
}

FrameworkParsingPackageUtils.getSigningDetails中主要进行签名校验,在perfetto中的表现:

在这里插入图片描述

最后parseApkLite会返回在AndroidManifest.xml中解析的信息。

继续看前面InstallingAsyncTask在onPostExecute中session.commit(pendingIntent.getIntentSender())

//framework/base/core/java/android/content/pm/PackageInstaller.java
public void commit(@NonNull IntentSender statusReceiver) {
    try {
        mSession.commit(statusReceiver, false);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    assertNotChild("commit");

    if (!markAsSealed(statusReceiver, forTransfer)) {
        return;
    }
    if (isMultiPackage()) {
        synchronized (mLock) {
            boolean sealFailed = false;
            for (int i = mChildSessions.size() - 1; i >= 0; --i) {
                // seal all children, regardless if any of them fail; we'll throw/return
                // as appropriate once all children have been processed
                if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) {
                    sealFailed = true;
                }
            }
            if (sealFailed) {
                return;
            }
        }
    }

    File appMetadataFile = getStagedAppMetadataFile();
    if (appMetadataFile != null) {
        long sizeLimit = getAppMetadataSizeLimit();
        if (appMetadataFile.length() > sizeLimit) {
            appMetadataFile.delete();
            throw new IllegalArgumentException(
                "App metadata size exceeds the maximum allowed limit of " + sizeLimit);
        }
        if (isIncrementalInstallation()) {
            // Incremental requires stageDir to be empty so move the app metadata file to a
            // temporary location and move back after commit.
            appMetadataFile.renameTo(getTmpAppMetadataFile());
        }
    }

    dispatchSessionSealed();
}

private void dispatchSessionSealed() {
    mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}

private void handleSessionSealed() {
        assertSealed("dispatchSessionSealed");
        // Persist the fact that we've sealed ourselves to prevent
        // mutations of any hard links we create.
        mCallback.onSessionSealedBlocking(this);
        dispatchStreamValidateAndCommit();
    }

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

@WorkerThread
private void handleStreamValidateAndCommit() {
    try {
        // This will track whether the session and any children were validated and are ready to
        // progress to the next phase of install
        boolean allSessionsReady = true;
        for (PackageInstallerSession child : getChildSessions()) {
            allSessionsReady &= child.streamValidateAndCommit();
        }
        if (allSessionsReady && streamValidateAndCommit()) {
            mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
        }
    } catch (PackageManagerException e) {
        destroy();
        String msg = ExceptionUtils.getCompleteMessage(e);
        dispatchSessionFinished(e.error, msg, null);
        maybeFinishChildSessions(e.error, msg);
    }
}

@WorkerThread
private void handleInstall() {
    if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
        DevicePolicyEventLogger
            .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
            .setAdmin(getInstallSource().mInstallerPackageName)
            .write();
    }

    /**
         * Stops the installation of the whole session set if one session needs user action
         * in its belong session set. When the user answers the yes,
         * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
         * handled to come back here to check again.
         *
         * {@code mUserActionRequired} is used to track when user action is required for an
         * install. Since control may come back here more than 1 time, we must ensure that it's
         * value is not overwritten.
         */
    boolean wasUserActionIntentSent = sendPendingUserActionIntentIfNeeded();
    if (mUserActionRequired == null) {
        mUserActionRequired = wasUserActionIntentSent;
    }
    if (wasUserActionIntentSent) {
        // Commit was keeping session marked as active until now; release
        // that extra refcount so session appears idle.
        deactivate();
        return;
    } else if (mUserActionRequired) {
        // If user action is required, control comes back here when the user allows
        // the installation. At this point, the session is marked active once again,
        // since installation is in progress.
        activate();
    }

    if (mVerificationInProgress) {
        Slog.w(TAG, "Verification is already in progress for session " + sessionId);
        return;
    }
    mVerificationInProgress = true;

    if (params.isStaged) {
        mStagedSession.verifySession();
    } else {
        verify();
    }
}

private void verify() {
    try {
        List<PackageInstallerSession> children = getChildSessions();
        if (isMultiPackage()) {
            for (PackageInstallerSession child : children) {
                child.prepareInheritedFiles();
                child.parseApkAndExtractNativeLibraries();
            }
        } else {
            prepareInheritedFiles();
            parseApkAndExtractNativeLibraries();
        }
        verifyNonStaged();
    } catch (PackageManagerException e) {
        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
        final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
        setSessionFailed(e.error, errorMsg);
        onSessionVerificationFailure(e.error, errorMsg);
    }
}

verify中会调用prepareInheritedFiles()在/data/app/的app安装目录下创建oat的目录和/lib/arm64,最后调用verifyNonStaged。

private void verifyNonStaged()
    throws PackageManagerException {
    synchronized (mLock) {
        markStageDirInUseLocked();
    }
    mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
        mHandler.post(() -> {
            if (dispatchPendingAbandonCallback()) {
                // No need to continue if abandoned
                return;
            }
            if (error == INSTALL_SUCCEEDED) {
                onVerificationComplete();
            } else {
                onSessionVerificationFailure(error, msg);
            }
        });
    });
}

mSessionProvider.getSessionVerifier().verify调用的是PackageSessionVerifier的verify。

//framework/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
/**
 * Runs verifications that are common to both staged and non-staged sessions.
 */
public void verify(PackageInstallerSession session, Callback callback) {
    mHandler.post(() -> {
        try {
            storeSession(session.mStagedSession);
            if (session.isMultiPackage()) {
                for (PackageInstallerSession child : session.getChildSessions()) {
                    checkApexUpdateAllowed(child);
                    checkRebootlessApex(child);
                    checkApexSignature(child);
                }
            } else {
                checkApexUpdateAllowed(session);
                checkRebootlessApex(session);
                checkApexSignature(session);
            }
            verifyAPK(session, callback);
        } catch (PackageManagerException e) {
            String errorMessage = PackageManager.installStatusToString(e.error, e.getMessage());
            session.setSessionFailed(e.error, errorMessage);
            callback.onResult(e.error, e.getMessage());
        }
    });
}

/**
 * Runs verifications particular to APK. This includes APEX sessions since an APEX can also
 * be treated as APK.
 */
private void verifyAPK(PackageInstallerSession session, Callback callback)
    throws PackageManagerException {
    final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
        @Override
        public void onUserActionRequired(Intent intent) {
            throw new IllegalStateException();
        }
        @Override
        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                                       Bundle extras) {
            if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
                // Continue verification for staged sessions
                verifyStaged(session.mStagedSession, callback);
                return;
            }
            if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
                String errorMessage = PackageManager.installStatusToString(returnCode, msg);
                session.setSessionFailed(returnCode, errorMessage);
                callback.onResult(returnCode, msg);
            } else {
                session.setSessionReady();
                callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
            }
        }
    };
    final VerifyingSession verifyingSession = createVerifyingSession(session, observer);
    if (session.isMultiPackage()) {
        final List<PackageInstallerSession> childSessions = session.getChildSessions();
        List<VerifyingSession> verifyingChildSessions = new ArrayList<>(childSessions.size());
        for (PackageInstallerSession child : childSessions) {
            verifyingChildSessions.add(createVerifyingSession(child, null));
        }
        verifyingSession.verifyStage(verifyingChildSessions);
    } else {
        verifyingSession.verifyStage();
    }
}

private VerifyingSession createVerifyingSession(
    PackageInstallerSession session, IPackageInstallObserver2 observer) {
    final UserHandle user;
    if ((session.params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
        user = UserHandle.ALL;
    } else {
        user = new UserHandle(session.userId);
    }
    return new VerifyingSession(user, session.stageDir, observer, session.params,
                  session.getInstallSource(), session.getInstallerUid(),
                  session.getSigningDetails(),session.sessionId,
                  session.getPackageLite(), session.getUserActionRequired(), mPm);
}

verify调用verifyAPK,verifyAPK中会创建一个observer来接收安装进度结果,然后把observer传入VerifyingSession中,最后调动VerifyingSession的verifyStage()。

//framework/base/services/core/java/com/android/server/pm/VerifyingSession.java
public void verifyStage() {
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueVerify",
                          System.identityHashCode(this));
    mPm.mHandler.post(this::start);
}

对应perfetto中的部分如下:

在这里插入图片描述

queueVerify结束之后,就开始start流程,对应perfetto中如下部分:

在这里插入图片描述

private void start() {
    if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueVerify",
                        System.identityHashCode(this));
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "start");
    handleStartVerify();
    handleReturnCode();
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

public void handleStartVerify() {
    PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(
        mPm.mContext, mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
        mPackageAbiOverride);

    Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
        pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
    setReturnCode(ret.first, ret.second);
    if (mRet != INSTALL_SUCCEEDED) {
        return;
    }

    // Perform package verification and enable rollback (unless we are simply moving the
    // package).
    if (!mOriginInfo.mExisting) {
        if (!isApex()) {
            // TODO(b/182426975): treat APEX as APK when APK verification is concerned
            sendApkVerificationRequest(pkgLite);
        }
        if ((mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
            sendEnableRollbackRequest();
        }
    }
}

void handleReturnCode() {
    if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
        || mWaitForEnableRollbackToComplete) {
        return;
    }
    sendVerificationCompleteNotification();
    if (mRet != INSTALL_SUCCEEDED) {
        PackageMetrics.onVerificationFailed(this);
    }
}

private void sendVerificationCompleteNotification() {
    if (mParentVerifyingSession != null) {
        mParentVerifyingSession.trySendVerificationCompleteNotification(this);
    } else {
        try {
            mObserver.onPackageInstalled(null, mRet, mErrorMessage,
                                         new Bundle());
        } catch (RemoteException e) {
            Slog.i(TAG, "Observer no longer exists.");
        }
    }
}

首先在handleStartVerify中进行一些校验,然后通知mObserver进行onPackageInstalled。

PackageSessionVerifier中收到onPackageInstalled后,做了下面两件事。

session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);

setSessionReady中,主要进行一些变量的赋值,

void setSessionReady() {
    synchronized (mLock) {
        // Do not allow destroyed/failed session to change state
        if (mDestroyed || mSessionFailed) return;
        mSessionReady = true;
        mSessionApplied = false;
        mSessionFailed = false;
        mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
        mSessionErrorMessage = "";
    }
    mCallback.onSessionChanged(this);
}

而callback.onResult(PackageManager.INSTALL_SUCCEEDED, null)中主要是调用onVerificationComplete()。

4 apk的install流程

@WorkerThread
private void onVerificationComplete() {
    if (isStaged()) {
        mStagingManager.commitSession(mStagedSession);
        sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
        return;
    }
    install();
}

/**
 * Stages installs and do cleanup accordingly depending on whether the installation is
 * successful or not.
 *
 * @return a future that will be completed when the whole process is completed.
 */
private CompletableFuture<Void> install() {
    List<CompletableFuture<InstallResult>> futures = installNonStaged();
    CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
    return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
        if (t == null) {
            setSessionApplied();
            for (CompletableFuture<InstallResult> f : futures) {
                InstallResult result = f.join();
                result.session.dispatchSessionFinished(
                    INSTALL_SUCCEEDED, "Session installed", result.extras);
            }
        } else {
            PackageManagerException e = (PackageManagerException) t.getCause();
            setSessionFailed(e.error,
                             PackageManager.installStatusToString(e.error, e.getMessage()));
            dispatchSessionFinished(e.error, e.getMessage(), null);
            maybeFinishChildSessions(e.error, e.getMessage());
        }
    });
}

/**
 * Stages sessions (including child sessions if any) for install.
 *
 * @return a list of futures to indicate the install results of each session.
 */
private List<CompletableFuture<InstallResult>> installNonStaged() {
	final InstallingSession installingSession = createInstallingSession(future);
    installingSession.installStage();
    return futures;
}

public void installStage() {
        setTraceMethod("installStage").setTraceCookie(System.identityHashCode(this));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
                System.identityHashCode(this));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(this));
        mPm.mHandler.post(this::start);
    }

install最后走到了installStage,对应的perfetto如下:

在这里插入图片描述

private void start() {
    if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                        System.identityHashCode(this));
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startInstall");
    InstallRequest installRequest = new InstallRequest(this);
    handleStartCopy(installRequest);
    handleReturnCode(installRequest);
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

installStage会走到mPm.mHandler.post(this::start), 对应的perfetto如下:

在这里插入图片描述

handleStartCopy中主要是获取安装目录信息。再来看handleReturnCode

private void handleReturnCode(InstallRequest installRequest) {
    processPendingInstall(installRequest);
}

private void processPendingInstall(InstallRequest installRequest) {
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        mRet = copyApk(installRequest);
    }
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        F2fsUtils.releaseCompressedBlocks(
            mPm.mContext.getContentResolver(), new File(installRequest.getCodePath()));
    }
    installRequest.setReturnCode(mRet);
    if (mParentInstallingSession != null) {
        mParentInstallingSession.tryProcessInstallRequest(installRequest);
    } else {
        // Queue up an async operation since the package installation may take a little while.
        mPm.mHandler.post(() -> processInstallRequests(
            mRet == PackageManager.INSTALL_SUCCEEDED /* success */,
            Collections.singletonList(installRequest)));
    }
}

4.1 apk和二进制拷贝

首先拷贝apk。

private int copyApk(InstallRequest request) {
    if (mMoveInfo == null) {
        return copyApkForFileInstall(request);
    } else {
        return copyApkForMoveInstall(request);
    }
}

private int copyApkForFileInstall(InstallRequest request) {
    ret = PackageManagerServiceUtils.copyPackage(
        mOriginInfo.mFile.getAbsolutePath(), request.getCodeFile());
    ...
    final boolean isIncremental = isIncrementalPath(
        request.getCodeFile().getAbsolutePath());
    final File libraryRoot = new File(request.getCodeFile(), LIB_DIR_NAME);
    NativeLibraryHelper.Handle handle = null;
    try {
        handle = NativeLibraryHelper.Handle.create(request.getCodeFile());
        ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                                                                 request.getAbiOverride(), isIncremental);
        if (ret != PackageManager.INSTALL_SUCCEEDED) {
            final String errorMessage = "Failed to copy native libraries";
            request.setError(ret, errorMessage);
        }
    }
}

copyApkForFileInstall主要做了两件事:拷贝apk、常见nativelib目录。

先来看拷贝apk

/**
 * Copy package to the target location.
 *
 * @param packagePath absolute path to the package to be copied. Can be
 *                    a single monolithic APK file or a cluster directory
 *                    containing one or more APKs.
 * @return returns status code according to those in
 *         {@link PackageManager}
 */
public static int copyPackage(String packagePath, File targetDir) {
	copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
}

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

copyPackage把apk拷贝到apk的安装目录,可以看到就是我们看到的/data/app下面的那个base.apk。

然后handle = NativeLibraryHelper.Handle.create(request.getCodeFile());创建目录,然后拷贝NativeBinaries,也就是/data/app下面的lib64。

4.2 installPackages流程

继续看processPendingInstall,之后会走到processInstallRequests。

private void processInstallRequests(boolean success, List<InstallRequest> installRequests) {
    processApkInstallRequests(success, installRequests);
}

private void processApkInstallRequests(boolean success, List<InstallRequest> installRequests) {
    mInstallPackageHelper.installPackagesTraced(installRequests);

    for (InstallRequest request : installRequests) {
        request.onInstallCompleted();
        doPostInstall(request);
    }
    for (InstallRequest request : installRequests) {
        mInstallPackageHelper.restoreAndPostInstall(request);
    }
}

void installPackagesTraced(List<InstallRequest> requests) {
    synchronized (mPm.mInstallLock) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
            installPackagesLI(requests);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
}

processInstallRequests最后会开启installPackagesLI流程

5 installPackagesLI

先看perfetto中的流程。

在这里插入图片描述

 /**
 * Installs one or more packages atomically. This operation is broken up into four phases:
 * <ul>
 *     <li><b>Prepare</b>
 *         <br/>Analyzes any current install state, parses the package and does initial
 *         validation on it.</li>
 *     <li><b>Scan</b>
 *         <br/>Interrogates the parsed packages given the context collected in prepare.</li>
 *     <li><b>Reconcile</b>
 *         <br/>Validates scanned packages in the context of each other and the current system
 *         state to ensure that the install will be successful.
 *     <li><b>Commit</b>
 *         <br/>Commits all scanned packages and updates system state. This is the only place
 *         that system state may be modified in the install flow and all predictable errors
 *         must be determined before this phase.</li>
 * </ul>
 *
 * Failure at any phase will result in a full failure to install all packages.
 */
@GuardedBy("mPm.mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
	Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
    //1.preparePackageLI
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
    request.onPrepareStarted();
    preparePackageLI(request);
    //2、scanPackageTracedLI
    final ScanResult scanResult = scanPackageTracedLI(request.getParsedPackage(),
                            request.getParseFlags(), request.getScanFlags(),
                            System.currentTimeMillis(), request.getUser(),
                            request.getAbiOverride());
    //3、reconcilePackages
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                    reconciledPackages = ReconcilePackageUtils.reconcilePackages(
                            requests, Collections.unmodifiableMap(mPm.mPackages),
                            versionInfos, mSharedLibraries,
                        mPm.mSettings.getKeySetManagerService(),
                            mPm.mSettings);
    //4、commitPackagesLocked
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
    commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
    
    //5、executePostCommitStepsLIF
    executePostCommitStepsLIF(reconciledPackages);
    
    //6、broadcastPackageVerified
    if (success) {
        VerificationUtils.broadcastPackageVerified(verificationId, originUri,
                            PackageManager.VERIFICATION_ALLOW, rootHashString,
                            request.getDataLoaderType(), request.getUser(),
                            mContext);
    }
}

从代码看,installPackagesLI主要进行了6步操作,下面一步步的看。

5.1 preparePackageLI-准备阶段

@GuardedBy("mPm.mInstallLock")
private void preparePackageLI(InstallRequest request) throws PrepareFailure {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final ParsedPackage parsedPackage;
    try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
        parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
        AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
    } catch (PackageManagerException e) {
        throw new PrepareFailure("Failed parse during installPackageLI", e);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

@GuardedBy("mPm.mInstallLock")
private void preparePackageLI(InstallRequest request) throws PrepareFailure {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final ParsedPackage parsedPackage;
    try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
        parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
        AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
    }
    ...
    final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
    final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
        input, parsedPackage, false /*skipVerify*/);
    parsedPackage.setSigningDetails(result.getResult());
    ...
    final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
    derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage, systemApp,
                        isUpdatedSystemAppFromExistingSetting ||
                        isUpdatedSystemAppInferred,
                        abiOverride, ScanPackageUtils.getAppLib32InstallDir());
    ...
    doRenameLI(request, parsedPackage);
}

Prepare准备:分析任何当前安装状态,分析包并对其进行初始验证。

在这一阶段首先是将apk文件解析出来,解析它的AndroidManifest.xml文件,将结果记录起来。我们平时在清单文件中声明的Activity等组件就是在这一步被记录到Framework中的,后续才能通过startActivity等方式启动来,然后是对签名信息进行验证。

总结:分析当前安装包的状态,解析安装包并对其做初始化验证

5.2 scanPackageTracedLI-扫描阶段

@GuardedBy("mPm.mInstallLock")
private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
                   final @ParsingPackageUtils.ParseFlags int parseFlags,
                   @PackageManagerService.ScanFlags int scanFlags, long currentTime,
                   @Nullable UserHandle user, String cpuAbiOverride) 
    throws PackageManagerException {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
    try {
        return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime,
                                user, cpuAbiOverride);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

@GuardedBy("mPm.mInstallLock")
private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
                     final @ParsingPackageUtils.ParseFlags int parseFlags,
                     @PackageManagerService.ScanFlags int scanFlags, 
                     long currentTime, @Nullable UserHandle user, 
                     String cpuAbiOverride) throws PackageManagerException {
    synchronized (mPm.mLock) {
        assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
        final ScanRequest request = new ScanRequest(parsedPackage,
                 initialScanRequest.mOldSharedUserSetting,
                 initialScanRequest.mOldPkg, installedPkgSetting,
                 initialScanRequest.mSharedUserSetting, disabledPkgSetting,
                 initialScanRequest.mOriginalPkgSetting,
                 initialScanRequest.mRealPkgName,parseFlags, scanFlags, 
                 initialScanRequest.mIsPlatformPackage, user, cpuAbiOverride);
        return ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector,
                                  mPm.mFactoryTest, currentTime);
    }
}

/**
 * Just scans the package without any side effects.
 *
 * @param injector injector for acquiring dependencies
 * @param request Information about the package to be scanned
 * @param isUnderFactoryTest Whether or not the device is under factory test
 * @param currentTime The current time, in millis
 * @return The results of the scan
 */
@GuardedBy("mPm.mInstallLock")
@VisibleForTesting
@NonNull
public static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
                PackageManagerServiceInjector injector, boolean isUnderFactoryTest,
                long currentTime) throws PackageManagerException {
    ...
    final boolean createNewPackage = (pkgSetting == null);
    if (createNewPackage) {
        pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
                       originalPkgSetting, disabledPkgSetting, realPkgName,
                       sharedUserSetting, destCodeFile,
                       parsedPackage.getNativeLibraryRootDir(),
                       AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),                              AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
                       parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags,
                       user,true /*allowInstall*/, instantApp, virtualPreload, 
                       sStoppedSystemApp, UserManagerService.getInstance(),
                       usesSdkLibraries,
                       parsedPackage.getUsesSdkLibrariesVersionsMajor(),
                       usesStaticLibraries,
                       parsedPackage.getUsesStaticLibrariesVersions(),
                       parsedPackage.getMimeGroups(), newDomainSetId);
    }
  
    pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils
        .getRawPrimaryCpuAbi(parsedPackage))
        .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
        .setCpuAbiOverride(cpuAbiOverride);
	return new ScanResult(request, pkgSetting, changedAbiCodePath,
                !createNewPackage /* existingSettingCopied */,
                Process.INVALID_UID /* previousAppId */ , sdkLibraryInfo,
                staticSharedLibraryInfo, dynamicSharedLibraryInfos);
}

scanPackageTracedLI主要工作是把第一步preparePackageLI中解析的apk信息保存到一个PackageSetting对象中

总结:根据prepare阶段中收集的安装包状态信息去扫描解析出来的包

5.3 reconcilePackages-协调阶段

public static List<ReconciledPackage> reconcilePackages(
            List<InstallRequest> installRequests,
            Map<String, AndroidPackage> allPackages,
            Map<String, Settings.VersionInfo> versionInfos,
            SharedLibrariesImpl sharedLibraries,
            KeySetManagerService ksms, Settings settings)
            throws ReconcileFailure {
    final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());

    // make a copy of the existing set of packages so we can combine them with incoming packages
    final ArrayMap<String, AndroidPackage> combinedPackages =
        new ArrayMap<>(allPackages.size() + installRequests.size());

    combinedPackages.putAll(allPackages);

    final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
        new ArrayMap<>();
}

这里combinedPackages是当前安装包大小加上要安装包的大小,接着它把当前的安装包对象都放到combinedPackages中。allPackages.allPackages中是当前的安装包对象。combinedPackages是当前安装包大小加上要安装包的大小,接着它把当前的安装包对象都放到combinedPackages中。

incomingSharedLibraries对象是安装包对象中声明的即将安装的库。

先把每个安装包的解析包对象放入combinedPackages中。因为它是ArrayMap类型,所以它也可能覆盖之前的解析包对象。下面是处理共享库信息。

final Settings.VersionInfo versionInfo = versionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
final boolean isRollback = installRequest.isRollback();
final boolean compatMatch =
    PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
                                                sharedUserSetting, disabledPkgSetting,
                                                signingDetails, compareCompat,
                                                compareRecover, isRollback);

该段代码主要是用来验证安装包的签名。

final ReconciledPackage reconciledPackage = new ReconciledPackage(installRequests, 
                               allPackages, installRequest,deletePackageAction,
                               allowedSharedLibInfos, signingDetails,         
                               sharedUserSignaturesChanged, removeAppKeySetData);

如果之前的都没什么问题,会将相关信息封装到ReconciledPackage对象中,然后添加到结果result中。封装到ReconciledPackage对象的包括ReconcileRequest对象request、安装参数installArgs、新生成的PackageSettingscanResult.pkgSetting、安装信息res、准备结果对象、浏览结果对象、待删除包行动兑对象deletePackageAction、允许添加的包中的共享库信息、解析包签名信息对象、共享用户签名是否改变、是否删除签名信息removeAppKeySetData。

这段代码是得到每个安装包的共享库信息,在系统启动或者是系统解析包的情况下,是不进行该项操作的。

reconciledPackage.mCollectedSharedLibraryInfos =
    sharedLibraries.collectSharedLibraryInfos(
    installRequest.getParsedPackage(), combinedPackages,
    incomingSharedLibraries);

主要是调用collectSharedLibraryInfos()方法来得到。collectSharedLibraryInfos主要是对解析包对象的getUsesLibraries()、getUsesStaticLibraries()、getUsesOptionalLibraries()、getUsesNativeLibraries()、getUsesOptionalNativeLibraries()进行处理,它们分别是从Manifest文件中配置的"uses-library"、“uses-static-library”、“uses-library”、“uses-native-library”、"uses-native-library"标签中解析出来的。其中getUsesLibraries()与getUsesOptionalLibraries()是根据标签中的属性"required"来区分。getUsesNativeLibraries()、getUsesOptionalNativeLibraries()也是根据标签中的属性"required"来区分。

它们又都调用了另一个同名collectSharedLibraryInfos()方法来实现。同名collectSharedLibraryInfos()方法的实现具体来说就是根据existingLibraries(存在的库), newLibraries(新加的库),如果新加的库不在它们两个中,根据参数required,如果它为false,可以跳过,如果它为true,则会抛出PackageManagerException异常。如果需要判断版本和摘要(根据参数),传递的参数摘要和对应的签名信息经过计算得出的相符,则通过。

这样最后,将结果result返回。

总结: 验证scan阶段扫描到的Package信息以及当前系统状态,确保apk的正确安装。

​ 对普通APP替换安装的生成一个删除包行为对象。
​ 对新生成的PackageSetting对象,验证签名。验证通过之后,将相关信息封装成ReconciledPackage对象,放到返回结果中。
​ 对每一个安装应用包,收集共享库信息。

5.4 commitPackagesLocked

首先来看下perfetto中的commitPackages

在这里插入图片描述

 commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
@GuardedBy("mPm.mLock")
private void commitPackagesLocked(List<ReconciledPackage> reconciledPackages,
                                  @NonNull int[] allUsers) {
	AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, allUsers);
    	updateSettingsLI(pkg, allUsers, installRequest);
}

@GuardedBy("mPm.mLock")
public AndroidPackage commitReconciledScanResultLocked(
    @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
	mPm.mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
    commitPackageSettings(pkg, pkgSetting, oldPkgSetting, reconciledPkg);
}

这里面也是安装应用和扫描应用同时都会调用的方法,先只看扫描的情况

  1. 处理PackageSetting,这部分逻辑是和安装应用是混在一起的,主要包括

    1. SharedUserSetting, 这个一般只有覆盖安装的时候如果sharedUserId变了,要重新赋值
    2. 重命名包名的逻辑,安装重命名包名的时候会更新packages.xml文件
  2. 将PackageSetting写入package-restrictions.xml,里面主要存储的是disabled和enabled四大组件

  3. 最后调用commitPackageSettings()进行最后一步处理.

/**
 * Adds a scanned package to the system. When this method is finished, the package will
 * be available for query, resolution, etc...
 */
private void commitPackageSettings(@NonNull AndroidPackage pkg,
                                   @NonNull PackageSetting pkgSetting, 
                                   @Nullable PackageSetting oldPkgSetting,
                                   ReconciledPackage reconciledPkg) {
    // writer
    ArrayList<AndroidPackage> clientLibPkgs =
        mSharedLibraries.commitSharedLibraryChanges(pkg, pkgSetting,
        reconciledPkg.mAllowedSharedLibraryInfos,
        reconciledPkg.getCombinedAvailablePackages(), scanFlags);
    // writer
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
    synchronized (mPm.mLock) {
        // We don't expect installation to fail beyond this point
        // Add the new setting to mSettings
        mPm.mSettings.insertPackageSettingLPw(pkgSetting, pkg);
        // Add the new setting to mPackages
        mPm.mPackages.put(pkg.getPackageName(), pkg);
        if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
            mApexManager.registerApkInApex(pkg);
        }
        final Computer snapshot = mPm.snapshotComputer();
        mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage, snapshot);
        mPm.mAppsFilter.addPackage(snapshot, pkgSetting, isReplace,
                                   (scanFlags & SCAN_DONT_KILL_APP) != 0 /* retainImplicitGrantOnReplace */);
        mPm.addAllPackageProperties(pkg);
        mPm.mPermissionManager.onPackageAdded(pkgSetting,
                    (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
    }
}
  1. 调用mSettings.insertPackageSettingLPw(),这里并非是更新packages.xml的地方,只是将pkgSetting,放到Settings.mPackages中
  2. 将AndroidPackage放到mPackages中
  3. 更新KeySetManagerService的应用信息,这是一个管理签名的服务
  4. 添加自定义的Permission和PermissionGroup到PermissionManagerService中
private void updateSettingsLI(AndroidPackage newPackage,
            int[] allUsers, InstallRequest installRequest) {
    updateSettingsInternalLI(newPackage, allUsers, installRequest);
}

private void updateSettingsInternalLI(AndroidPackage pkg,
            int[] allUsers, InstallRequest installRequest) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
    mPm.mSettings.addInstallerPackageNames(ps.getInstallSource());
    ...
    mPm.mSettings.writeKernelMappingLPr(ps);
    ...
    installRequest.setName(pkgName);
            installRequest.setAppId(pkg.getUid());
            installRequest.setPkg(pkg);
            installRequest.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
            //to update install status
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
            mPm.writeSettingsLPrTEMP();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

1、addInstallerPackageNames向mInstallerPackages中加入ps.getInstallSource()

2、writeKernelMappingLPr来写/config/sdcardfs/,如向该目录下appid写入appid file

3、将settings写入packages.xml

@SuppressWarnings("GuardedBy")
void writeSettingsLPrTEMP(boolean sync) {
snapshotComputer(false);
mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
mSettings.writeLPr(mLiveComputer, sync);
}

总结:提交所有扫描的包并更新系统状态。这是唯一可以在安装流程和所有可预测错误中修改系统状态的地方.

5.5 executePostCommitStepsLIF

/**
 * 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 com.android.server.pm.PackageManagerService.mLock}.
 */
@GuardedBy("mPm.mInstallLock")
private void executePostCommitStepsLIF(List<ReconciledPackage> reconciledPackages) {
    final boolean performDexopt =
        (!instantApp || android.provider.Settings.Global.getInt(
            mContext.getContentResolver(),
            android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
        && !pkg.isDebuggable()
        && (!onIncremental)
        && dexoptOptions.isCompilationEnabled()
        && !isApex;
    if (performDexopt) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
        if (useArtService()) {
            PackageManagerLocal packageManagerLocal =
                LocalManagerRegistry.getManager(PackageManagerLocal.class);
            try (PackageManagerLocal.FilteredSnapshot snapshot =
                 packageManagerLocal.withFilteredSnapshot()) {
                DexoptParams params =
                    dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
                DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
                    snapshot, packageName, params);
                installRequest.onDexoptFinished(dexOptResult);
            }
        }
    }
    PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
        incrementalStorages);
}

调用了executePostCommitStepsLIF完成apk的安装,执行dex优化等操作。

检测是否需要进行dex优化:同时满足下面几种情况:

​ 1.不是一个即时应用app或者instant_app_dexopt_enabled属性不是0

​ 2.debuggable为false

​ 3.不在增量文件系统上

4. 不是Apex
5. 编译选项没有skip

最后放出perfetto

在这里插入图片描述

5.6 broadcastPackageVerified

public static void broadcastPackageVerified(int verificationId, Uri packageUri,
            int verificationCode, @Nullable String rootHashString, 
            int dataLoaderType, UserHandle user, Context context) {
    final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
    intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
    intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
    if (rootHashString != null) {
        intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
    }
    intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);

    context.sendBroadcastAsUser(intent, user,
                                android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
}

发送PACKAGE_VERIFIED广播。

6 返回结果

最后通知VerifyingSession#handleVerificationFinished

if (verifyingSession != null) {
    verifyingSession.handleVerificationFinished();
}

void handleVerificationFinished() {
    mWaitForVerificationToComplete = false;
    handleReturnCode();
}

private void sendVerificationCompleteNotification() {
    if (mParentVerifyingSession != null) {
        mParentVerifyingSession.trySendVerificationCompleteNotification(this);
    } else {
        try {
            mObserver.onPackageInstalled(null, mRet, mErrorMessage,
                                         new Bundle());
        } catch (RemoteException e) {
            Slog.i(TAG, "Observer no longer exists.");
        }
    }
}

这里是mObserver.onPackageInstalled

//framework/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
    @Override
    public void onPackageInstalled(String basePackageName, int returnCode, 
                           String msg, Bundle extras) {
        if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
            // Continue verification for staged sessions
            verifyStaged(session.mStagedSession, callback);
            return;
        }
        if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
            String errorMessage = PackageManager.installStatusToString(
                returnCode, msg);
            session.setSessionFailed(returnCode, errorMessage);
            callback.onResult(returnCode, msg);
        } else {
            session.setSessionReady();
            callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
        }
    }
}

//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private void verifyNonStaged() throws PackageManagerException {
    mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
        mHandler.post(() -> {
            if (dispatchPendingAbandonCallback()) {
                // No need to continue if abandoned
                return;
            }
            if (error == INSTALL_SUCCEEDED) {
                onVerificationComplete();
            } else {
                onSessionVerificationFailure(error, msg);
            }
        });
    });
}

@WorkerThread
private void onVerificationComplete() {
    if (isStaged()) {
        mStagingManager.commitSession(mStagedSession);
        sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
        return;
    }
    install();
}

这里走isStaged

private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
    final IntentSender statusReceiver = getRemoteStatusReceiver();
    if (statusReceiver != null) {
        // Execute observer.onPackageInstalled on different thread as we don't want callers
        // inside the system server have to worry about catching the callbacks while they are
        // calling into the session
        final SomeArgs args = SomeArgs.obtain();
        args.arg1 = getPackageName();
        args.arg2 = msg;
        args.arg3 = extras;
        args.arg4 = statusReceiver;
        args.argi1 = returnCode;
        args.argi2 = isPreapprovalRequested() && !isCommitted() ? 1 : 0;
        mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
    }
}

case MSG_ON_PACKAGE_INSTALLED:
final SomeArgs args = (SomeArgs) msg.obj;
final String packageName = (String) args.arg1;
final String message = (String) args.arg2;
final Bundle extras = (Bundle) args.arg3;
final IntentSender statusReceiver = (IntentSender) args.arg4;
final int returnCode = args.argi1;
final boolean isPreapproval = args.argi2 == 1;
args.recycle();

sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                       isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
                       packageName, returnCode, isPreapproval, message, extras);

break;

sendOnPackageInstalled中先发送安装成功的通知,然后向IntentSender发送sendIntent。

从第2节中可知道IntentSender是在PackageInstaller的apk传入的。

PendingIntent pendingIntent = PendingIntent.getBroadcast(
                InstallInstalling.this,
                mInstallId,
                broadcastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);

全文完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2075443.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NTU发布即插即用的嵌入式SLAM新SOTA!

导读&#xff1a; 当前的SLAM技术正逐渐向轻量化和高效化发展&#xff0c;在保证定位精度的同时&#xff0c;研究者们越来越倾向于简化冗余的框架&#xff0c;以在效率和性能之间找到最佳平衡点。 一个优秀的SLAM系统不仅需要提供精确的定位数据&#xff0c;还必须能够在不同平…

【PyQt】切换界面的实现

前言一、介绍二、代码2.1 QStackedWidget2.1.1 代码2.1.2 运行结果展示 2.2 QTabWidget2.2.1 代码2.2.2 运行结果展示 2.3 QDockWidget2.3.1 代码2.3.2 运行结果展示 PULSE结果 总结 前言 实现几个界面的切换展示的效果。 一、介绍 在 PyQt 中实现一个主界面包含其他子界面的…

#mark# ViteSvgIconsPlugin svgoOptions选项

svgoOptions 设置为false时 &#xff0c; 矢量svg图标 stroke线条不正常 找了半天 哪影响的 &#xff0c; 线条异常图如下 svgoOptions 改为true后 &#xff0c;显示正常了

并查集优化策略及其正确性证明:基于路径压缩与按秩合并

并查集优化策略及其正确性证明:基于路径压缩与按秩合并 前言优化策略算法伪代码C语言实现归纳法证明基础情况归纳步骤结论前言 引理:对于所有的结点x, 有 x.rank≤x.p.rank, 如 果x≠x.p, 则此式是严格不等 式。x.rank 的初始值为0,并且随时间而增加,直到x≠x.p; 从此以后,…

安卓13 背光反向 亮度反向 android13 backlight reverse

总纲 android13 rom 开发总纲说明 目录 1.前言 2.问题分析 3.代码分析 4.代码修改 5.彩蛋 1.前言 有些设备,在调整背光的时候,会发现,背光调大,显示亮度反而变暗,背光调小,亮度变亮。这是由于PWM背光本身并没有一个标准去决定怎么样算是高亮度,怎么算是低亮度。因…

C语言-02 算法-程序的灵魂

算法-程序的灵魂 1、程序&#xff1d;算法数据结构2、什么是算法3、简单的算法举例4、算法的特性5、怎样表示一个算法5.1 用自然语言表示算法5.2 用流程图表示算法5.3 三种基本结构和改进的流程图5.4 用N-S流程图表示算法5.5 用伪代码表示算法5.6 用计算机语言表示算法 6、结构…

秋冬春夏,纪念在CSDN的第365天

目录 时光 收获 工作 生活 憧憬 时光 再次收到创作纪念日的消息时&#xff0c;已在CSDN创作和度过了一年的时光。创作&#xff0c;成了自己的第二工作空间&#xff0c;成为了日常的一种习惯。 每当看到第1篇文章的提醒消息&#xff0c;都会想起当时创作的初衷和情景。是一…

学了PMP能给我们带来什么变化?

一、PMP是什么 PMP(Project Management Professional)是一个在项目管理领域具有高级认证的资格。该认证由PMI在全球200多个国家和地区广泛推广&#xff0c;被认为是项目管理领域内最具含金量的认证之一。 持有PMP证书的项目经理不仅可以提升其项目管理能力&#xff0c;同时也…

LLaMA代码笔记 --基于lit-llama

代码来自&#xff1a;lit-llama modelscope模型下载 &#xff1a;llama-7b 下载后的模型需要转换为lit-llama使用的格式&#xff0c;详见 howto 文件夹下的 download_weights.md 文中代码为了方便说明&#xff0c;删减了一些内容&#xff0c;详细代码请查看源码。 generate …

磁场强度H和磁感应强度B,磁化强度M和磁极化强度J

磁场强度H、磁感应强度B、磁化强度M和磁极化强度J是四个非常重要的磁学基本概念&#xff0c;他们之间关联但有时又很容易混淆。分清这四个概念对于磁材行业从业者是非常重要的&#xff0c;今天我们就为大家细说一下它们的概念和关系。 磁场强度H 磁场强度H其实是一个没有实际…

自动化巨头施耐德电气,部分业务被其供应商收购:之前还收购过霍尼韦尔

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 供应商逆袭&#xff1a;小鱼吃大鱼的商业奇迹 英国电气行业掀起一阵惊涛骇浪。斯塔福德郡的中型企业Goodfish Group竟然收购了全球巨头施耐德电气…

AppInventor2 现已全面支持安卓14!

//重磅升级&#xff1a;支持安卓14// MIT于2024/08/19升级Android SDK至34&#xff08;安卓14&#xff09;&#xff0c;因为在 2024 年 8 月 31 日之后&#xff0c;在 Google Play 商店中添加或更新应用时必须执行此更新。 国内估计也会跟进&#xff0c;因此使用最新版本的 s…

CleanMyMac如何帮助用户清空DNS缓存,Mac清除dns缓存命令

什么是DNS缓存&#xff1f;这个缓存有什么危害&#xff1f;相信大家平时使用浏览器时&#xff0c;有时候会遇到一个很奇怪的问题&#xff0c;就是Mac打开许多网站如百度网站&#xff0c;都是可以访问的&#xff0c;但是在打开某个特定网站时&#xff0c;却发现浏览器提示检测不…

网上商城|基于SprinBoot+vue的分布式架构网上商城系统(源码+数据库+文档)

分布式架构网上商城系统 目录 基于SprinBootvue的分布式架构网上商城系统 一、前言 二、系统设计 三、系统功能设计 5.1系统功能模块 5.2管理员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍…

Halcon根据灰度特征值选择区域

Halcon根据灰度特征值选择区域 与select_shape算子类似&#xff0c;灰度值图像也可以快捷地根据特征值选择符合设定条件的区域。select_gray算子用于实现这一功能&#xff0c;该算子能接受一组区域作为输入&#xff0c;然后根据选定的特征计算其是否满足特定的条件。当所有区域…

网络安全 DVWA通关指南 DVWA File Upload(文件上传)

DVWA File Upload&#xff08;文件上传&#xff09; 文章目录 DVWA File Upload&#xff08;文件上传&#xff09;修复建议 LowMediumHighImpossible 修复建议 1、使用白名单限制可以上传的文件扩展名 2、注意0x00截断攻击&#xff08;PHP更新到最新版本&#xff09; 3、对上传…

【系统安全】Kernel Streaming WOW Thunk 服务驱动程序特权提升漏洞(CVE-2024-38054)

文章目录 前言一、漏洞概述二、影响范围三、漏洞复现四、修复方法前言 安全研究员 “Frost” 发布了CVE-2024-38054漏洞的概念验证漏洞利用代码,这加剧了人们对最近修补的 Windows 安全漏洞的担忧。内核流 WOW Thunk 服务驱动程序中的这个高严重性漏洞可能使本地攻击者能够通…

【赵渝强老师】使用Docker Machine远程管理Docker

Docker Machine是Docker官方提供的一个远程管理工具。通过使用Docker Machine&#xff0c;可以帮助开发人员在远程主机上安装Docker&#xff1b;或者在远程的虚拟主机上直接安装虚拟机并在虚拟机中安装Docker。Docker Machine还提供了相应的命令来管理这些远程的Docker环境和虚…

四川财谷通信息技术有限公司引领新风尚

在数字经济蓬勃发展的今天&#xff0c;电子商务已成为推动经济增长的重要引擎之一。而在这股浪潮中&#xff0c;短视频平台抖音凭借其庞大的用户基数和高度活跃的社区氛围&#xff0c;为无数小微企业和个人创业者提供了前所未有的发展机遇。四川财谷通信息技术有限公司&#xf…

如何防止图纸外泄?图纸安全管理措施有哪些(必备清单)

当今数字化和信息化的时代&#xff0c;图纸作为企业设计、制造等环节中的重要资料&#xff0c;其安全性尤为重要。图纸的泄露不仅可能导致企业的技术秘密被竞争对手获取&#xff0c;还可能造成巨大的经济损失和法律纠纷。因此&#xff0c;建立健全的图纸安全管理措施是每个企业…