Android 10 PMS安装流程

转载
https://juejin.cn/post/7003590026776969253#heading-4


Android 是如何完成apk的安装的?安装时它又做了哪些事情,保存了哪些信息?存储的这些信息又有什么作用?

这篇文章,让我们带着以上问题来一起探讨一下android系统的apk安装流程。


让我们先从apk安装的调用代码开始追溯:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri apkUri =FileProvider.getUriForFile(context, "你的包名.fileProvider", file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent)
复制代码

代码内容很简单,启动一个Action为Intent.ACTION_VIEW,Flag为Intent.FLAG_GRANT_READ_URI_PERMISSION,Type为application/vnd.android.package-archive的Activity。

这个Activity在哪里呢?

Android 10之前,你可以在/packages/apps/PackageInstaller/AndroidManifest.xml中找到它:

<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>
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="file" />
                <data android:scheme="package" />
                <data android:scheme="content" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
复制代码

但是,在Android 10的源码里,谷歌已经将PackageInstaller 这个app删掉了,这意味着使用之前的安装方式会存在一定的风险(ps:国产手机系统在Android 10中都自己添加了PackageInstaller这个app)。Android 10推荐采用一个叫PackageInstaller的类来专门负责apk的安装与卸载工作,你可以在 /samples/ApiDemos/src/com/example/android/apis/content/InstallApkSessionApi.java 中找到此Api的使用示例:

Code 1

PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);

addApkToInstallSession("HelloActivity.apk", session);

// Create an install status receiver.
Context context = InstallApkSessionApi.this;
Intent intent = new Intent(context, InstallApkSessionApi.class);
intent.setAction(PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();

// Commit the session (this will start the installation workflow).
session.commit(statusReceiver);
复制代码


这篇文章,我们探讨一下采用PackageInstaller类来安装Apk的方式,采用PackageInstaller app来安装Apk的方式留个各位读者自行去探讨挖掘。

PackageInstaller

我们还是先从PackageInstaller的定义开始看起,先上官方的说明:

Offers the ability to install, upgrade, and remove applications on the device. This
includes support for apps packaged either as a single "monolithic" APK, or apps packaged
as multiple "split" APKs.

An app is delivered for installation through a PackageInstaller.Session, which any app
can create. Once the session is created, the installer can stream one or more APKs into
place until it decides to either commit or destroy the session. Committing may require
user intervention to complete the installation, unless the caller falls into one of the
following categories, in which case the installation will complete automatically.

  • the device owner
  • the affiliated profile owner

Sessions can install brand new apps, upgrade existing apps, or add new splits into an existing app.

Apps packaged as multiple split APKs always consist of a single "base" APK (with
null split name) and zero or more "split" APKs (with unique split names). Any subset
of these APKs can be installed together, as long as the following constraints are met:

  • All APKs must have the exact same package name, version code, and signing certificates.
  • All APKs must have unique split names.
  • All installations must contain a single base APK.
    复制代码

我来做一下翻译工作,由于最后一段讲的是拆分包的安装,这篇文章不对拆分包的安装做讨论,故直接略过了:

  • 提供在设备上安装、升级和删除应用程序的功能,应用程序既可以是整包也可以是拆分包。
  • 应用程序通过PackageInstaller.Session安装,任何应用程序都可以创建该Session。创建Session后,installer可以将一个或多个APK以流的形式传输到适当的位置,直到它决定提交或销毁Session。提交可能需要用户干预才能完成安装,除非调用方属于设备所有者或文件所有者,在这种情况下,安装将自动完成。
  • Session可以安装全新的应用程序,升级现有应用程序,或将新拆分添加到现有应用程序中。

接下来,我们结合上文Code 1处的PackageInstallApi调用代码来对它进行探讨。

PackageInstaller.SessionParams

PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
复制代码

SessionParams 有以下两个值得关注的参数:

mode

TypeDescription
MODE_FULL_INSTALL安装的Apk完全替换目标程序现有的Apk
MODE_INHERIT_EXISTING继承目标应用程序的任何现有APK,除非它们已被会话显式覆盖(基于拆分名称)

installLocation

TypeDescription
INSTALL_LOCATION_AUTO让系统决定理想的安装位置。
INSTALL_LOCATION_INTERNAL_ONLY默认值,明确要求仅安装在手机内部存储上。
INSTALL_LOCATION_PREFER_EXTERNAL更倾向安装在SD卡上。不能保证系统会遵守这一要求。如果外部存储不可用或太满,应用程序可能会安装在内部存储上。

isStaged

标识此Session是否在重启时安装。


让我们回到PackageInstaller中,继续往下看。

packageInstaller.createSession

public int createSession(@NonNull SessionParams params) throws IOException {
    try {
        final String installerPackage;
        if (params.installerPackageName == null) {
            //这里的mInstallerPackageName指的是安装apk的应用程序包名,而不是目标apk的包名
            installerPackage = mInstallerPackageName;
        } else {
            installerPackage = params.installerPackageName;
        }
        //调用PackageInstallerService里的createSession方法
        return mInstaller.createSession(params, installerPackage, mUserId);
    } catch (RuntimeException e) {
        ExceptionUtils.maybeUnwrapIOException(e);
        throw e;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
复制代码

可以看到,packageInstaller.createSession方法最后将实际调用转交给服务端PackageInstallerService去执行。


PackageInstallerService.createSession

@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
    try {
        return createSessionInternal(params, installerPackageName, userId);
    } catch (IOException e) {
        throw ExceptionUtils.wrap(e);
    }
}
复制代码

createSessionInternal

private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
        throws IOException {
    final int callingUid = Binder.getCallingUid();
    mPermissionManager.enforceCrossUserPermission(
            callingUid, userId, true, true, "createSession");
//检查此用户是否被禁止安装程序
if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
    throw new SecurityException("User restriction prevents installing");
}

//如果安装来自adb 或者 root用户,param里增加 INSTALL_FROM_ADB 的FLAG
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
    params.installFlags |= PackageManager.INSTALL_FROM_ADB;

} else {
    // 只有具有INSTALL_PACKAGES权限的APP才允许以非调用者的身份设置为安装器
    if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) !=
            PackageManager.PERMISSION_GRANTED) {
        mAppOps.checkPackage(callingUid, installerPackageName);
    }
    //移除以下三个FLAG
    params.installFlags &amp;= ~PackageManager.INSTALL_FROM_ADB;
    params.installFlags &amp;= ~PackageManager.INSTALL_ALL_USERS;
    params.installFlags &amp;= ~PackageManager.INSTALL_ALLOW_TEST;
    //如果APP已存在,则替换它
    params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
    if ((params.installFlags &amp; PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
            &amp;&amp; !mPm.isCallerVerifier(callingUid)) {
        params.installFlags &amp;= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
    }
}

if (Build.IS_DEBUGGABLE || isDowngradeAllowedForCaller(callingUid)) {
    //允许降级
    params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else {
    //不允许降级,不允许向低版本升级
    params.installFlags &amp;= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
    params.installFlags &amp;= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
}

...

if (!params.isMultiPackage) {
    //安装时,只有系统组件可以绕过运行时权限。
    if ((params.installFlags &amp; PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
            &amp;&amp; mContext.checkCallingOrSelfPermission(Manifest.permission
            .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
        throw new SecurityException("You need the "
                + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
                + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
    }

    //如果AppIcon过大,则对它进行调整
    if (params.appIcon != null) {
        final ActivityManager am = (ActivityManager) mContext.getSystemService(
                Context.ACTIVITY_SERVICE);
        final int iconSize = am.getLauncherLargeIconSize();
        if ((params.appIcon.getWidth() &gt; iconSize * 2)
                || (params.appIcon.getHeight() &gt; iconSize * 2)) {
            params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
                    true);
        }
    }

    switch (params.mode) {
        case SessionParams.MODE_FULL_INSTALL:
        case SessionParams.MODE_INHERIT_EXISTING:
            break;
        default:
            throw new IllegalArgumentException("Invalid install mode: " + params.mode);
    }

    // If caller requested explicit location, sanity check it, otherwise
    // resolve the best internal or adopted location.
    if ((params.installFlags &amp; PackageManager.INSTALL_INTERNAL) != 0) {
        if (!PackageHelper.fitsOnInternal(mContext, params)) {
            throw new IOException("No suitable internal storage available");
        }
    } else if ((params.installFlags &amp; PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
        // For now, installs to adopted media are treated as internal from
        // an install flag point-of-view.
        params.installFlags |= PackageManager.INSTALL_INTERNAL;
    } else {
        params.installFlags |= PackageManager.INSTALL_INTERNAL;

        // Resolve best location for install, based on combination of
        // requested install flags, delta size, and manifest settings.
        final long ident = Binder.clearCallingIdentity();
        try {
        //根据给定的installLocation计算安装需要的大小,选择要安装应用程
        //序的实际卷。只考虑内部卷和专用卷,并且更偏向将现有包保留在其当前卷上。
        //volumeUuid 指的是 安装所在的卷的fsUuid,如果安装在内部存储上,则返回null
            params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
}

final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
    // 检查安装程序是否正常运行,打开的Installer Session不能超过1024个
    final int activeCount = getSessionCount(mSessions, callingUid);
    if (activeCount &gt;= MAX_ACTIVE_SESSIONS) {
        throw new IllegalStateException(
                "Too many active sessions for UID " + callingUid);
    }
    //历史Session不能超过1048576个
    final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
    if (historicalCount &gt;= MAX_HISTORICAL_SESSIONS) {
        throw new IllegalStateException(
                "Too many historical sessions for UID " + callingUid);
    }
    //随机生成一个不超过Integer.MAX_VALUE的sessionId,并保存在mAllocatedSessions中
    sessionId = allocateSessionIdLocked();
}

final long createdMillis = System.currentTimeMillis();
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
if (!params.isMultiPackage) {
        if ((params.installFlags &amp; PackageManager.INSTALL_INTERNAL) != 0) {
         //安装在内部存储上,volumeUuid 为null
        //复制路径为/data/app/vmdl${sessionId}.tmp
        stageDir = buildSessionDir(sessionId, params);
    } else {
        stageCid = buildExternalStageCid(sessionId);
    }
}
//存储了安装相关的所有信息
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
        mInstallThread.getLooper(), mStagingManager, sessionId, userId,
        installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
        false, false, null, SessionInfo.INVALID_ID, false, false, false,
        SessionInfo.STAGED_SESSION_NO_ERROR, "");

//将sessionId和session绑定起来
synchronized (mSessions) {
    mSessions.put(sessionId, session);
}
if (params.isStaged) {
     //处理分阶段安装会话,即仅在重新启动后才需要安装的会话。
    mStagingManager.createSession(session);
}

if ((session.params.installFlags &amp; PackageManager.INSTALL_DRY_RUN) == 0) {
    //回调通知Session已创建成功
    mCallbacks.notifySessionCreated(session.sessionId, session.userId);
}
//异步将session信息写入install_sessions.xml文件中
writeSessionsAsync();
return sessionId;

}
复制代码

createSessionInternal方法主要是对 初始化apk的安装信息及环境,并创建一个sessionId,将安装Session与sessionId 进行绑定。


packageInstaller.openSession

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;
    }
}
复制代码

我们继续追踪PackageInstallerService中的openSession方法:

PackageInstallerService.openSession

public IPackageInstallerSession openSession(int sessionId) {
    try {
        return openSessionInternal(sessionId);
    } catch (IOException e) {
        throw ExceptionUtils.wrap(e);
    }
}
复制代码

openSessionInternal

private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
    synchronized (mSessions) {
        final PackageInstallerSession session = mSessions.get(sessionId);
        if (session == null || !isCallingUidOwner(session)) {
            throw new SecurityException("Caller has no access to session " + sessionId);
        }
        //如果StageDir不为空,则创建此文件夹
        //stageDir是写入客户端数据的暂存位置
        session.open();
        return session;
    }
}
复制代码

可以看到,openSessionInternal方法只是根据sessionId查找在上一阶段通过createSession创建的PackageInstallerSession对象并将其返回。


安装代码接下来调用了addApkToInstallSession("HelloActivity.apk", session);,这个方法的详细实现如下:

private void addApkToInstallSession(String assetName, PackageInstaller.Session session)
    throws IOException {
        // It's recommended to pass the file size to openWrite(). Otherwise installation may fail
        // if the disk is almost full.
        try (OutputStream packageInSession = session.openWrite("package", 0, -1);
            InputStream is = getAssets().open(assetName)) {
            byte[] buffer = new byte[16384];
            int n;
            while ((n = is.read(buffer)) >= 0) {
                packageInSession.write(buffer, 0, n);
            }
        }
}
复制代码

session.openWrite

openWrite方法的主要作用是打开一个流,将APK文件写入Session。返回的流将开始在文件中请求的偏移量处写入数据,该偏移量可用于恢复部分写入的文件。如果指定了有效的文件长度,系统将预先分配底层磁盘空间,以优化磁盘上的位置。强烈建议在已知的情况下提供有效的文件长度。

您可以将数据写入返回的流,也可以根据需要调用fsync(OutputStream),以确保字节已持久化到磁盘,然后在完成时关闭。在调用commit(IntentSender)之前,必须关闭所有流。

openWrite的源码如下所示:

public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
        long lengthBytes) throws IOException {
    try {
        if (ENABLE_REVOCABLE_FD) {
            return new ParcelFileDescriptor.AutoCloseOutputStream(
                    mSession.openWrite(name, offsetBytes, lengthBytes));
        } else {
            //ENABLE_REVOCABLE_FD默认为false
            final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
                    offsetBytes, lengthBytes);
            return new FileBridge.FileBridgeOutputStream(clientSocket);
        }
    } catch (RuntimeException e) {
        ExceptionUtils.maybeUnwrapIOException(e);
        throw e;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
复制代码

这个方法里我们重点关注这句话 mSession.openWrite(name, offsetBytes, lengthBytes),它意味者最终由 PackageInstallerSession类里的openWrite方法继续来处理。


PackageInstallerSession.openWrite

@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
    try {
        return doWriteInternal(name, offsetBytes, lengthBytes, null);
    } catch (IOException e) {
        throw ExceptionUtils.wrap(e);
    }
}
复制代码

继续看doWriteInternal方法:


doWriteInternal

private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
                                              ParcelFileDescriptor incomingFd) throws IOException {
    //快速检查状态是否正常,并为自己分配一个Pipe。
    //然后,我们在锁之外进行大量的磁盘分配,但是这个打开的Pipe将阻止任何尝试的安装转换
    final RevocableFileDescriptor fd;
    final FileBridge bridge;
    final File stageDir;
    synchronized (mLock) {
        if (PackageInstaller.ENABLE_REVOCABLE_FD) {
            fd = new RevocableFileDescriptor();
            bridge = null;
            mFds.add(fd);
        } else {
            //走这里
            fd = null;
            bridge = new FileBridge();
            mBridges.add(bridge);
        }
         //解析应写入暂存数据的实际位置。
        stageDir = resolveStageDirLocked();
    }
try {
    //先使用安装程序提供的名称;我们之后会重命名
    if (!FileUtils.isValidExtFilename(name)) {
    //Check if given filename is valid for an ext4 filesystem.
        throw new IllegalArgumentException("Invalid name: " + name);
    }
    final File target;
    final long identity = Binder.clearCallingIdentity();
    try {
        target = new File(stageDir, name);
    } finally {
        Binder.restoreCallingIdentity(identity);
    }

    // TODO: this should delegate to DCS so the system process avoids
    // holding open FDs into containers.
    //打开文件并设置权限为644
    final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),
            O_CREAT | O_WRONLY, 0644);
    Os.chmod(target.getAbsolutePath(), 0644);

    // 如果指定了APK大小,先在磁盘中分配空间
    if (stageDir != null &amp;&amp; lengthBytes &gt; 0) {
        mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
                PackageHelper.translateAllocateFlags(params.installFlags));
    }

    if (offsetBytes &gt; 0) {
        Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
    }

   if (incomingFd != null) {
   //如果调用了session.write方法,incomingFd不为null,将会进入此分支
            switch (Binder.getCallingUid()) {
                case android.os.Process.SHELL_UID:
                case android.os.Process.ROOT_UID:
                case android.os.Process.SYSTEM_UID:
                    break;
                default:
                    throw new SecurityException(
                            "Reverse mode only supported from shell or system");
            }

            // In "reverse" mode, we're streaming data ourselves from the
            // incoming FD, which means we never have to hand out our
            // sensitive internal FD. We still rely on a "bridge" being
            // inserted above to hold the session active.
            try {
                final Int64Ref last = new Int64Ref(0);
                //将数据写入暂存文件
                FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null,
                        Runnable::run, (long progress) -&gt; {
                            if (params.sizeBytes &gt; 0) {
                                final long delta = progress - last.value;
                                last.value = progress;
                                addClientProgress((float) delta / (float) params.sizeBytes);
                            }
                        });
            } finally {
                IoUtils.closeQuietly(targetFd);
                IoUtils.closeQuietly(incomingFd);

                //完成传输,关闭file bridge
                synchronized (mLock) {
                    if (PackageInstaller.ENABLE_REVOCABLE_FD) {
                        mFds.remove(fd);
                    } else {
                        bridge.forceClose();
                        mBridges.remove(bridge);
                    }
                }
            }
            return null;
   } 

   
   bridge.setTargetFile(targetFd);
   bridge.start();
   return new ParcelFileDescriptor(bridge.getClientSocket());
   
} catch (ErrnoException e) {
    throw e.rethrowAsIOException();
}

}
复制代码

doWriteInternal这个方法主要是初始化要安装的APK文件的磁盘存储空间(如果指定了大小的话),并创建了一个可以跨进程写文件的FileBridge对象,通过ParcelFileDescriptor类包装称文件输出流提供给应用层写入安装的APK数据。

需要注意的是,后续的session.write方法调用的也是doWriteInternal,不同的是传入的参数incomingFd不为null,这意味着doWriteInternal会将数据写入到incomingFd这个文件描述符所对应的文件,它也是我们在调用openWrite方法时创建的文件。

到这一步为止,我们完成了要安装的Apk的复制工作。


session.commit

现在到了我们最后一步了,将Installer Session提交,去真正执行安装Apk的工作。我们来看一下commit方法:

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

同样的,它会把此方法委托给PackageInstallerSession类去执行。需要注意的是,commit方法里有一个IntentSender参数,这个参数的作用是用来通知安装时Session状态的改变,比如 安装需要用户进一步确认、安装成功、安装失败等事件。


PackageInstallerSession.commit

public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    if (hasParentSessionId()) {
        throw new IllegalStateException(
                "Session " + sessionId + " is a child of multi-package session "
                        + mParentSessionId +  " and may not be committed directly.");
    }
    if (!markAsCommitted(statusReceiver, forTransfer)) {
        return;
    }

...

mHandler.obtainMessage(MSG_COMMIT).sendToTarget();

}
复制代码

commit方法最终向mHandler中发送了一条MSG_COMMIT的消息,而在这个消息里,实际执行的是handleCommit方法。


handleCommit

private void handleCommit() {
    ...
// 返回子Session列表,如果Session不是multipackage,则返回null
List&lt;PackageInstallerSession&gt; childSessions = getChildSessions();

try {
    synchronized (mLock) {
        commitNonStagedLocked(childSessions);
    }
} catch (PackageManagerException e) {
    final String completeMsg = ExceptionUtils.getCompleteMessage(e);
    Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
    destroyInternal();
    dispatchSessionFinished(e.error, completeMsg, null);
}

}
复制代码


commitNonStagedLocked

private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
        throws PackageManagerException {
        //返回一个ActiveInstallSession对象
    final PackageManagerService.ActiveInstallSession committingSession =
            makeSessionActiveLocked();
    if (committingSession == null) {
        return;
    }
    if (isMultiPackage()) {
        List<PackageManagerService.ActiveInstallSession> activeChildSessions =
                new ArrayList<>(childSessions.size());
        boolean success = true;
        PackageManagerException failure = null;
        for (int i = 0; i < childSessions.size(); ++i) {
            final PackageInstallerSession session = childSessions.get(i);
            try {
                final PackageManagerService.ActiveInstallSession activeSession =
                        session.makeSessionActiveLocked();
                if (activeSession != null) {
                    activeChildSessions.add(activeSession);
                }
            } catch (PackageManagerException e) {
                failure = e;
                success = false;
            }
        }
        if (!success) {
            try {
                mRemoteObserver.onPackageInstalled(
                        null, failure.error, failure.getLocalizedMessage(), null);
            } catch (RemoteException ignored) {
            }
            return;
        }
        mPm.installStage(activeChildSessions);
    } else {
       //我们通常执行的安装走这个分支
        mPm.installStage(committingSession);
    }
}
复制代码

commitNonStagedLocked方法的最后,调用PMS的installStage方法,这样代码逻辑就进入了PMS中,让PMS去对要安装的APK文件做内容的解析,完成真正的安装工作。

实际上,PackageInstaller只是负责安装前期的准备工作,它维持了一个安装会话,管理安装的参数,并提供将安装包临时复制到特定路径的功能,但真正实现安装还是得依靠PMS。

posted @ 2022-04-21 16:03  梦过无声  阅读(1229)  评论(0编辑  收藏  举报