Loading services/core/java/com/android/server/pm/PackageInstallerService.java +1 −0 Original line number Original line Diff line number Diff line Loading @@ -1125,6 +1125,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void onStagedSessionChanged(PackageInstallerSession session) { public void onStagedSessionChanged(PackageInstallerSession session) { writeSessionsAsync(); writeSessionsAsync(); // TODO(b/118865310): don't send broadcast if system is not ready. mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); } } Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +23 −68 Original line number Original line Diff line number Diff line Loading @@ -992,8 +992,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Read transfers from the original owner stay open, but as the session's data // Read transfers from the original owner stay open, but as the session's data // cannot be modified anymore, there is no leak of information. For staged sessions, // cannot be modified anymore, there is no leak of information. For staged sessions, // further validation may be performed by the staging manager. // further validation is performed by the staging manager. if (!params.isMultiPackage) { if (!params.isMultiPackage) { if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { // For APEX, validation is done by StagingManager post-commit. return; } final PackageInfo pkgInfo = mPm.getPackageInfo( final PackageInfo pkgInfo = mPm.getPackageInfo( params.appPackageName, PackageManager.GET_SIGNATURES params.appPackageName, PackageManager.GET_SIGNATURES | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); Loading @@ -1001,16 +1005,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { resolveStageDirLocked(); resolveStageDirLocked(); try { try { if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { // TODO(b/118865310): Remove this when APEX validation is done via // StagingManager. validateApexInstallLocked(pkgInfo); } else { // Verify that stage looks sane with respect to existing application. // This currently only ensures packageName, versionCode, and certificate // consistency. validateApkInstallLocked(pkgInfo); validateApkInstallLocked(pkgInfo); } } catch (PackageManagerException e) { } catch (PackageManagerException e) { throw e; throw e; } catch (Throwable e) { } catch (Throwable e) { Loading Loading @@ -1301,54 +1296,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { (params.installFlags & PackageManager.DONT_KILL_APP) != 0; (params.installFlags & PackageManager.DONT_KILL_APP) != 0; } } @GuardedBy("mLock") private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo) throws PackageManagerException { mResolvedStagedFiles.clear(); mResolvedInheritedFiles.clear(); try { resolveStageDirLocked(); } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, "Failed to resolve stage location", e); } final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter); if (ArrayUtils.isEmpty(addedFiles)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); } if (addedFiles.length > 1) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Only one APEX file at a time might be installed"); } File addedFile = addedFiles[0]; final ApkLite apk; try { apk = PackageParser.parseApkLite( addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw PackageManagerException.from(e); } mPackageName = apk.packageName; mVersionCode = apk.getLongVersionCode(); mSigningDetails = apk.signingDetails; mResolvedBaseFile = addedFile; // STOPSHIP: Ensure that we remove the non-staged version of APEX installs in production // because we currently do not verify that signatures are consistent with the previously // installed version in that case. // // When that happens, this hack can be reverted and we can rely on APEXd to map between // APEX files and their package names instead of parsing it out of the AndroidManifest // such as here. if (params.appPackageName == null) { params.appPackageName = mPackageName; } } /** /** * Validate install by confirming that all application packages are have * Validate install by confirming that all application packages are have * consistent package name, version code, and signing certificates. * consistent package name, version code, and signing certificates. Loading Loading @@ -1911,22 +1858,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } @Override @Override public void addChildSessionId(int sessionId) { public void addChildSessionId(int childSessionId) { final PackageInstallerSession session = mSessionProvider.getSession(sessionId); final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId); if (session == null) { if (childSession == null) { throw new RemoteException("Unable to add child.", throw new RemoteException("Unable to add child.", new PackageManagerException("Child session " + sessionId + " does not exist"), new PackageManagerException("Child session " + childSessionId + " does not exist"), false, true).rethrowAsRuntimeException(); } // Session groups must be consistent wrt to isStaged parameter. Non-staging session // cannot be grouped with staging sessions. if (this.params.isStaged ^ childSession.params.isStaged) { throw new RemoteException("Unable to add child.", new PackageManagerException("Child session " + childSessionId + " and parent session " + this.sessionId + " do not have consistent" + " staging session settings."), false, true).rethrowAsRuntimeException(); false, true).rethrowAsRuntimeException(); } } synchronized (mLock) { synchronized (mLock) { final int indexOfSession = mChildSessionIds.indexOfKey(sessionId); final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId); if (indexOfSession >= 0) { if (indexOfSession >= 0) { return; return; } } session.setParentSessionId(this.sessionId); childSession.setParentSessionId(this.sessionId); // TODO: sanity check, if parent session is staged then child session should be addChildSessionIdInternal(childSessionId); // marked as staged. addChildSessionIdInternal(sessionId); } } } } Loading services/core/java/com/android/server/pm/StagingManager.java +104 −26 Original line number Original line Diff line number Diff line Loading @@ -41,7 +41,9 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.List; import java.util.stream.Collectors; /** /** * This class handles staged install sessions, i.e. install sessions that require packages to * This class handles staged install sessions, i.e. install sessions that require packages to Loading Loading @@ -126,12 +128,24 @@ public class StagingManager { return false; return false; } } private static boolean submitSessionToApexService(int sessionId, ApexInfoList apexInfoList) { private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session, List<PackageInstallerSession> childSessions, ApexInfoList apexInfoList) { return sendSubmitStagedSessionRequest( session.sessionId, childSessions != null ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() : new int[]{}, apexInfoList); } private static boolean sendSubmitStagedSessionRequest( int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) { final IApexService apex = IApexService.Stub.asInterface( final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); ServiceManager.getService("apexservice")); boolean success; boolean success; try { try { success = apex.submitStagedSession(sessionId, new int[0], apexInfoList); success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList); } catch (RemoteException re) { } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); Slog.e(TAG, "Unable to contact apexservice", re); return false; return false; Loading @@ -139,22 +153,40 @@ public class StagingManager { return success; return success; } } private static boolean isApexSession(@NonNull PackageInstallerSession session) { return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0; } private void preRebootVerification(@NonNull PackageInstallerSession session) { private void preRebootVerification(@NonNull PackageInstallerSession session) { boolean success = true; boolean success = true; if ((session.params.installFlags & PackageManager.INSTALL_APEX) != 0) { final ApexInfoList apexInfoList = new ApexInfoList(); final ApexInfoList apexInfoList = new ApexInfoList(); // APEX checks. For single-package sessions, check if they contain an APEX. For // multi-package sessions, find all the child sessions that contain an APEX. if (!session.isMultiPackage() && isApexSession(session)) { success = submitSessionToApexService(session, null, apexInfoList); } else if (session.isMultiPackage()) { List<PackageInstallerSession> childSessions = Arrays.stream(session.getChildSessionIds()) // Retrieve cached sessions matching ids. .mapToObj(i -> mStagedSessions.get(i)) // Filter only the ones containing APEX. .filter(childSession -> isApexSession(childSession)) .collect(Collectors.toList()); if (!childSessions.isEmpty()) { success = submitSessionToApexService(session, childSessions, apexInfoList); } // else this is a staged multi-package session with no APEX files. } if (!submitSessionToApexService(session.sessionId, apexInfoList)) { if (success && (apexInfoList.apexInfos.length > 0)) { success = false; } else { // For APEXes, we validate the signature here before we mark the session as ready, // For APEXes, we validate the signature here before we mark the session as ready, // so we fail the session early if there is a signature mismatch. For APKs, the // so we fail the session early if there is a signature mismatch. For APKs, the // signature verification will be done by the package manager at the point at which // signature verification will be done by the package manager at the point at which // it applies the staged install. // it applies the staged install. // // // TODO: Decide whether we want to fail fast by detecting signature mismatches right // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, // away. // right away. for (ApexInfo apexPackage : apexInfoList.apexInfos) { for (ApexInfo apexPackage : apexInfoList.apexInfos) { if (!validateApexSignatureLocked(apexPackage.packagePath, if (!validateApexSignatureLocked(apexPackage.packagePath, apexPackage.packageName)) { apexPackage.packageName)) { Loading @@ -163,7 +195,7 @@ public class StagingManager { } } } } } } } if (success) { if (success) { session.setStagedSessionReady(); session.setStagedSessionReady(); } else { } else { Loading Loading @@ -206,15 +238,59 @@ public class StagingManager { } } } } void abortSession(@NonNull PackageInstallerSession sessionInfo) { void abortSession(@NonNull PackageInstallerSession session) { updateStoredSession(sessionInfo); synchronized (mStagedSessions) { synchronized (mStagedSessions) { mStagedSessions.remove(sessionInfo.sessionId); updateStoredSession(session); mStagedSessions.remove(session.sessionId); } } } } @GuardedBy("mStagedSessions") private boolean isMultiPackageSessionComplete(@NonNull PackageInstallerSession session) { // This method assumes that the argument is either a parent session of a multi-package // i.e. isMultiPackage() returns true, or that it is a child session, i.e. // hasParentSessionId() returns true. if (session.isMultiPackage()) { // Parent session of a multi-package group. Check that we restored all the children. for (int childSession : session.getChildSessionIds()) { if (mStagedSessions.get(childSession) == null) { return false; } } return true; } if (session.hasParentSessionId()) { PackageInstallerSession parent = mStagedSessions.get(session.getParentSessionId()); if (parent == null) { return false; } return isMultiPackageSessionComplete(parent); } Slog.wtf(TAG, "Attempting to restore an invalid multi-package session."); return false; } void restoreSession(@NonNull PackageInstallerSession session) { void restoreSession(@NonNull PackageInstallerSession session) { updateStoredSession(session); PackageInstallerSession sessionToResume = session; synchronized (mStagedSessions) { mStagedSessions.append(session.sessionId, session); // For multi-package sessions, we don't know in which order they will be restored. We // need to wait until we have restored all the session in a group before restoring them. if (session.isMultiPackage() || session.hasParentSessionId()) { if (!isMultiPackageSessionComplete(session)) { // Still haven't recovered all sessions of the group, return. return; } // Group recovered, find the parent if necessary and resume the installation. if (session.hasParentSessionId()) { sessionToResume = mStagedSessions.get(session.getParentSessionId()); } } } checkStateAndResume(sessionToResume); } private void checkStateAndResume(@NonNull PackageInstallerSession session) { // Check the state of the session and decide what to do next. // Check the state of the session and decide what to do next. if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) { if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) { // Final states, nothing to do. // Final states, nothing to do. Loading @@ -227,6 +303,8 @@ public class StagingManager { } else { } else { // Session had already being marked ready. Start the checks to verify if there is any // Session had already being marked ready. Start the checks to verify if there is any // follow-up work. // follow-up work. // TODO(b/118865310): should this be synchronous to ensure it completes before // systemReady() finishes? mBgHandler.post(() -> resumeSession(session)); mBgHandler.post(() -> resumeSession(session)); } } } } Loading Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +1 −0 Original line number Original line Diff line number Diff line Loading @@ -1125,6 +1125,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void onStagedSessionChanged(PackageInstallerSession session) { public void onStagedSessionChanged(PackageInstallerSession session) { writeSessionsAsync(); writeSessionsAsync(); // TODO(b/118865310): don't send broadcast if system is not ready. mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); } } Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +23 −68 Original line number Original line Diff line number Diff line Loading @@ -992,8 +992,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Read transfers from the original owner stay open, but as the session's data // Read transfers from the original owner stay open, but as the session's data // cannot be modified anymore, there is no leak of information. For staged sessions, // cannot be modified anymore, there is no leak of information. For staged sessions, // further validation may be performed by the staging manager. // further validation is performed by the staging manager. if (!params.isMultiPackage) { if (!params.isMultiPackage) { if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { // For APEX, validation is done by StagingManager post-commit. return; } final PackageInfo pkgInfo = mPm.getPackageInfo( final PackageInfo pkgInfo = mPm.getPackageInfo( params.appPackageName, PackageManager.GET_SIGNATURES params.appPackageName, PackageManager.GET_SIGNATURES | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); Loading @@ -1001,16 +1005,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { resolveStageDirLocked(); resolveStageDirLocked(); try { try { if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { // TODO(b/118865310): Remove this when APEX validation is done via // StagingManager. validateApexInstallLocked(pkgInfo); } else { // Verify that stage looks sane with respect to existing application. // This currently only ensures packageName, versionCode, and certificate // consistency. validateApkInstallLocked(pkgInfo); validateApkInstallLocked(pkgInfo); } } catch (PackageManagerException e) { } catch (PackageManagerException e) { throw e; throw e; } catch (Throwable e) { } catch (Throwable e) { Loading Loading @@ -1301,54 +1296,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { (params.installFlags & PackageManager.DONT_KILL_APP) != 0; (params.installFlags & PackageManager.DONT_KILL_APP) != 0; } } @GuardedBy("mLock") private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo) throws PackageManagerException { mResolvedStagedFiles.clear(); mResolvedInheritedFiles.clear(); try { resolveStageDirLocked(); } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, "Failed to resolve stage location", e); } final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter); if (ArrayUtils.isEmpty(addedFiles)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); } if (addedFiles.length > 1) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Only one APEX file at a time might be installed"); } File addedFile = addedFiles[0]; final ApkLite apk; try { apk = PackageParser.parseApkLite( addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw PackageManagerException.from(e); } mPackageName = apk.packageName; mVersionCode = apk.getLongVersionCode(); mSigningDetails = apk.signingDetails; mResolvedBaseFile = addedFile; // STOPSHIP: Ensure that we remove the non-staged version of APEX installs in production // because we currently do not verify that signatures are consistent with the previously // installed version in that case. // // When that happens, this hack can be reverted and we can rely on APEXd to map between // APEX files and their package names instead of parsing it out of the AndroidManifest // such as here. if (params.appPackageName == null) { params.appPackageName = mPackageName; } } /** /** * Validate install by confirming that all application packages are have * Validate install by confirming that all application packages are have * consistent package name, version code, and signing certificates. * consistent package name, version code, and signing certificates. Loading Loading @@ -1911,22 +1858,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } @Override @Override public void addChildSessionId(int sessionId) { public void addChildSessionId(int childSessionId) { final PackageInstallerSession session = mSessionProvider.getSession(sessionId); final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId); if (session == null) { if (childSession == null) { throw new RemoteException("Unable to add child.", throw new RemoteException("Unable to add child.", new PackageManagerException("Child session " + sessionId + " does not exist"), new PackageManagerException("Child session " + childSessionId + " does not exist"), false, true).rethrowAsRuntimeException(); } // Session groups must be consistent wrt to isStaged parameter. Non-staging session // cannot be grouped with staging sessions. if (this.params.isStaged ^ childSession.params.isStaged) { throw new RemoteException("Unable to add child.", new PackageManagerException("Child session " + childSessionId + " and parent session " + this.sessionId + " do not have consistent" + " staging session settings."), false, true).rethrowAsRuntimeException(); false, true).rethrowAsRuntimeException(); } } synchronized (mLock) { synchronized (mLock) { final int indexOfSession = mChildSessionIds.indexOfKey(sessionId); final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId); if (indexOfSession >= 0) { if (indexOfSession >= 0) { return; return; } } session.setParentSessionId(this.sessionId); childSession.setParentSessionId(this.sessionId); // TODO: sanity check, if parent session is staged then child session should be addChildSessionIdInternal(childSessionId); // marked as staged. addChildSessionIdInternal(sessionId); } } } } Loading
services/core/java/com/android/server/pm/StagingManager.java +104 −26 Original line number Original line Diff line number Diff line Loading @@ -41,7 +41,9 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.List; import java.util.stream.Collectors; /** /** * This class handles staged install sessions, i.e. install sessions that require packages to * This class handles staged install sessions, i.e. install sessions that require packages to Loading Loading @@ -126,12 +128,24 @@ public class StagingManager { return false; return false; } } private static boolean submitSessionToApexService(int sessionId, ApexInfoList apexInfoList) { private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session, List<PackageInstallerSession> childSessions, ApexInfoList apexInfoList) { return sendSubmitStagedSessionRequest( session.sessionId, childSessions != null ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() : new int[]{}, apexInfoList); } private static boolean sendSubmitStagedSessionRequest( int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) { final IApexService apex = IApexService.Stub.asInterface( final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); ServiceManager.getService("apexservice")); boolean success; boolean success; try { try { success = apex.submitStagedSession(sessionId, new int[0], apexInfoList); success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList); } catch (RemoteException re) { } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); Slog.e(TAG, "Unable to contact apexservice", re); return false; return false; Loading @@ -139,22 +153,40 @@ public class StagingManager { return success; return success; } } private static boolean isApexSession(@NonNull PackageInstallerSession session) { return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0; } private void preRebootVerification(@NonNull PackageInstallerSession session) { private void preRebootVerification(@NonNull PackageInstallerSession session) { boolean success = true; boolean success = true; if ((session.params.installFlags & PackageManager.INSTALL_APEX) != 0) { final ApexInfoList apexInfoList = new ApexInfoList(); final ApexInfoList apexInfoList = new ApexInfoList(); // APEX checks. For single-package sessions, check if they contain an APEX. For // multi-package sessions, find all the child sessions that contain an APEX. if (!session.isMultiPackage() && isApexSession(session)) { success = submitSessionToApexService(session, null, apexInfoList); } else if (session.isMultiPackage()) { List<PackageInstallerSession> childSessions = Arrays.stream(session.getChildSessionIds()) // Retrieve cached sessions matching ids. .mapToObj(i -> mStagedSessions.get(i)) // Filter only the ones containing APEX. .filter(childSession -> isApexSession(childSession)) .collect(Collectors.toList()); if (!childSessions.isEmpty()) { success = submitSessionToApexService(session, childSessions, apexInfoList); } // else this is a staged multi-package session with no APEX files. } if (!submitSessionToApexService(session.sessionId, apexInfoList)) { if (success && (apexInfoList.apexInfos.length > 0)) { success = false; } else { // For APEXes, we validate the signature here before we mark the session as ready, // For APEXes, we validate the signature here before we mark the session as ready, // so we fail the session early if there is a signature mismatch. For APKs, the // so we fail the session early if there is a signature mismatch. For APKs, the // signature verification will be done by the package manager at the point at which // signature verification will be done by the package manager at the point at which // it applies the staged install. // it applies the staged install. // // // TODO: Decide whether we want to fail fast by detecting signature mismatches right // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, // away. // right away. for (ApexInfo apexPackage : apexInfoList.apexInfos) { for (ApexInfo apexPackage : apexInfoList.apexInfos) { if (!validateApexSignatureLocked(apexPackage.packagePath, if (!validateApexSignatureLocked(apexPackage.packagePath, apexPackage.packageName)) { apexPackage.packageName)) { Loading @@ -163,7 +195,7 @@ public class StagingManager { } } } } } } } if (success) { if (success) { session.setStagedSessionReady(); session.setStagedSessionReady(); } else { } else { Loading Loading @@ -206,15 +238,59 @@ public class StagingManager { } } } } void abortSession(@NonNull PackageInstallerSession sessionInfo) { void abortSession(@NonNull PackageInstallerSession session) { updateStoredSession(sessionInfo); synchronized (mStagedSessions) { synchronized (mStagedSessions) { mStagedSessions.remove(sessionInfo.sessionId); updateStoredSession(session); mStagedSessions.remove(session.sessionId); } } } } @GuardedBy("mStagedSessions") private boolean isMultiPackageSessionComplete(@NonNull PackageInstallerSession session) { // This method assumes that the argument is either a parent session of a multi-package // i.e. isMultiPackage() returns true, or that it is a child session, i.e. // hasParentSessionId() returns true. if (session.isMultiPackage()) { // Parent session of a multi-package group. Check that we restored all the children. for (int childSession : session.getChildSessionIds()) { if (mStagedSessions.get(childSession) == null) { return false; } } return true; } if (session.hasParentSessionId()) { PackageInstallerSession parent = mStagedSessions.get(session.getParentSessionId()); if (parent == null) { return false; } return isMultiPackageSessionComplete(parent); } Slog.wtf(TAG, "Attempting to restore an invalid multi-package session."); return false; } void restoreSession(@NonNull PackageInstallerSession session) { void restoreSession(@NonNull PackageInstallerSession session) { updateStoredSession(session); PackageInstallerSession sessionToResume = session; synchronized (mStagedSessions) { mStagedSessions.append(session.sessionId, session); // For multi-package sessions, we don't know in which order they will be restored. We // need to wait until we have restored all the session in a group before restoring them. if (session.isMultiPackage() || session.hasParentSessionId()) { if (!isMultiPackageSessionComplete(session)) { // Still haven't recovered all sessions of the group, return. return; } // Group recovered, find the parent if necessary and resume the installation. if (session.hasParentSessionId()) { sessionToResume = mStagedSessions.get(session.getParentSessionId()); } } } checkStateAndResume(sessionToResume); } private void checkStateAndResume(@NonNull PackageInstallerSession session) { // Check the state of the session and decide what to do next. // Check the state of the session and decide what to do next. if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) { if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) { // Final states, nothing to do. // Final states, nothing to do. Loading @@ -227,6 +303,8 @@ public class StagingManager { } else { } else { // Session had already being marked ready. Start the checks to verify if there is any // Session had already being marked ready. Start the checks to verify if there is any // follow-up work. // follow-up work. // TODO(b/118865310): should this be synchronous to ensure it completes before // systemReady() finishes? mBgHandler.post(() -> resumeSession(session)); mBgHandler.post(() -> resumeSession(session)); } } } } Loading