Loading core/java/android/content/pm/PackageManager.java +9 −0 Original line number Diff line number Diff line Loading @@ -727,6 +727,7 @@ public abstract class PackageManager { INSTALL_ENABLE_ROLLBACK, INSTALL_ALLOW_DOWNGRADE, INSTALL_STAGED, INSTALL_DRY_RUN, }) @Retention(RetentionPolicy.SOURCE) public @interface InstallFlags {} Loading Loading @@ -904,6 +905,14 @@ public abstract class PackageManager { */ public static final int INSTALL_STAGED = 0x00200000; /** * Flag parameter for {@link #installPackage} to indicate that package should only be verified * but not installed. * * @hide */ public static final int INSTALL_DRY_RUN = 0x00800000; /** @hide */ @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = { DONT_KILL_APP Loading services/core/java/com/android/server/pm/PackageManagerService.java +16 −0 Original line number Diff line number Diff line Loading @@ -15566,6 +15566,22 @@ public class PackageManagerService extends IPackageManager.Stub @Override void handleReturnCode() { if (mVerificationCompleted && mEnableRollbackCompleted) { if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) { String packageName = ""; try { PackageLite packageInfo = new PackageParser().parsePackageLite(origin.file, 0); packageName = packageInfo.packageName; } catch (PackageParserException e) { Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(), e); } try { observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle()); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } return; } if (mRet == PackageManager.INSTALL_SUCCEEDED) { mRet = mArgs.copyApk(); } services/core/java/com/android/server/pm/StagingManager.java +55 −27 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; /** Loading Loading @@ -201,10 +202,6 @@ public class StagingManager { private void preRebootVerification(@NonNull PackageInstallerSession session) { boolean success = true; // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier. // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, // right away. 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. Loading @@ -230,6 +227,16 @@ public class StagingManager { return; } if (sessionContainsApk(session)) { if (!installApksInSession(session, /* preReboot */ true)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APK verification failed. Check logcat messages for " + "more information."); // TODO(b/118865310): abort the session on apexd. return; } } if (apexInfoList.apexInfos != null && apexInfoList.apexInfos.length > 0) { // 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 Loading Loading @@ -275,21 +282,31 @@ public class StagingManager { } } private boolean sessionContainsApex(@NonNull PackageInstallerSession session) { private boolean sessionContains(@NonNull PackageInstallerSession session, Predicate<PackageInstallerSession> filter) { if (!session.isMultiPackage()) { return isApexSession(session); return filter.test(session); } synchronized (mStagedSessions) { return !(Arrays.stream(session.getChildSessionIds()) // Retrieve cached sessions matching ids. .mapToObj(i -> mStagedSessions.get(i)) // Filter only the ones containing APEX. .filter(childSession -> isApexSession(childSession)) .filter(childSession -> filter.test(childSession)) .collect(Collectors.toList()) .isEmpty()); } } private boolean sessionContainsApex(@NonNull PackageInstallerSession session) { return sessionContains(session, (s) -> isApexSession(s)); } private boolean sessionContainsApk(@NonNull PackageInstallerSession session) { return sessionContains(session, (s) -> !isApexSession(s)); } private void resumeSession(@NonNull PackageInstallerSession session) { boolean hasApex = sessionContainsApex(session); if (hasApex) { Loading Loading @@ -326,7 +343,7 @@ public class StagingManager { } } // The APEX part of the session is activated, proceed with the installation of APKs. if (!installApksInSession(session)) { if (!installApksInSession(session, /* preReboot */ false)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "Staged installation of APKs failed. Check logcat messages for" + "more information."); Loading @@ -343,7 +360,6 @@ public class StagingManager { + "to the previous state of APEXd."); mPowerManager.reboot(null); } return; } Loading @@ -366,7 +382,7 @@ public class StagingManager { } private PackageInstallerSession createAndWriteApkSession( @NonNull PackageInstallerSession originalSession) { @NonNull PackageInstallerSession originalSession, boolean preReboot) { if (originalSession.stageDir == null) { Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir"); return null; Loading @@ -379,9 +395,14 @@ public class StagingManager { PackageInstaller.SessionParams params = originalSession.params.copy(); params.isStaged = false; params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; params.installFlags |= PackageManager.INSTALL_STAGED; // TODO(b/129744602): use the userid from the original session. if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; params.installFlags |= PackageManager.INSTALL_DRY_RUN; } else { params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; } int apkSessionId = mPi.createSession( params, originalSession.getInstallerPackageName(), 0 /* UserHandle.SYSTEM */); Loading @@ -408,8 +429,9 @@ public class StagingManager { } private boolean commitApkSession(@NonNull PackageInstallerSession apkSession, int originalSessionId) { int originalSessionId, boolean preReboot) { if (!preReboot) { if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { // If rollback is available for this session, notify the rollback // manager of the apk session so it can properly enable rollback. Loading @@ -421,6 +443,7 @@ public class StagingManager { // Cannot happen, the rollback manager is in the same process. } } } final LocalIntentReceiver receiver = new LocalIntentReceiver(); apkSession.commit(receiver.getIntentSender(), false); Loading @@ -435,14 +458,15 @@ public class StagingManager { return false; } private boolean installApksInSession(@NonNull PackageInstallerSession session) { private boolean installApksInSession(@NonNull PackageInstallerSession session, boolean preReboot) { if (!session.isMultiPackage() && !isApexSession(session)) { // APK single-packaged staged session. Do a regular install. PackageInstallerSession apkSession = createAndWriteApkSession(session); PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot); if (apkSession == null) { return false; } return commitApkSession(apkSession, session.sessionId); return commitApkSession(apkSession, session.sessionId, preReboot); } else if (session.isMultiPackage()) { // For multi-package staged sessions containing APKs, we identify which child sessions // contain an APK, and with those then create a new multi-package group of sessions, Loading @@ -464,6 +488,9 @@ public class StagingManager { } PackageInstaller.SessionParams params = session.params.copy(); params.isStaged = false; if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; } // TODO(b/129744602): use the userid from the original session. int apkParentSessionId = mPi.createSession( params, session.getInstallerPackageName(), Loading @@ -478,7 +505,8 @@ public class StagingManager { } for (PackageInstallerSession sessionToClone : childSessions) { PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone); PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone, preReboot); if (apkChildSession == null) { return false; } Loading @@ -489,7 +517,7 @@ public class StagingManager { return false; } } return commitApkSession(apkParentSession, session.sessionId); return commitApkSession(apkParentSession, session.sessionId, preReboot); } // APEX single-package staged session, nothing to do. return true; Loading Loading
core/java/android/content/pm/PackageManager.java +9 −0 Original line number Diff line number Diff line Loading @@ -727,6 +727,7 @@ public abstract class PackageManager { INSTALL_ENABLE_ROLLBACK, INSTALL_ALLOW_DOWNGRADE, INSTALL_STAGED, INSTALL_DRY_RUN, }) @Retention(RetentionPolicy.SOURCE) public @interface InstallFlags {} Loading Loading @@ -904,6 +905,14 @@ public abstract class PackageManager { */ public static final int INSTALL_STAGED = 0x00200000; /** * Flag parameter for {@link #installPackage} to indicate that package should only be verified * but not installed. * * @hide */ public static final int INSTALL_DRY_RUN = 0x00800000; /** @hide */ @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = { DONT_KILL_APP Loading
services/core/java/com/android/server/pm/PackageManagerService.java +16 −0 Original line number Diff line number Diff line Loading @@ -15566,6 +15566,22 @@ public class PackageManagerService extends IPackageManager.Stub @Override void handleReturnCode() { if (mVerificationCompleted && mEnableRollbackCompleted) { if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) { String packageName = ""; try { PackageLite packageInfo = new PackageParser().parsePackageLite(origin.file, 0); packageName = packageInfo.packageName; } catch (PackageParserException e) { Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(), e); } try { observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle()); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } return; } if (mRet == PackageManager.INSTALL_SUCCEEDED) { mRet = mArgs.copyApk(); }
services/core/java/com/android/server/pm/StagingManager.java +55 −27 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; /** Loading Loading @@ -201,10 +202,6 @@ public class StagingManager { private void preRebootVerification(@NonNull PackageInstallerSession session) { boolean success = true; // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier. // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, // right away. 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. Loading @@ -230,6 +227,16 @@ public class StagingManager { return; } if (sessionContainsApk(session)) { if (!installApksInSession(session, /* preReboot */ true)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APK verification failed. Check logcat messages for " + "more information."); // TODO(b/118865310): abort the session on apexd. return; } } if (apexInfoList.apexInfos != null && apexInfoList.apexInfos.length > 0) { // 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 Loading Loading @@ -275,21 +282,31 @@ public class StagingManager { } } private boolean sessionContainsApex(@NonNull PackageInstallerSession session) { private boolean sessionContains(@NonNull PackageInstallerSession session, Predicate<PackageInstallerSession> filter) { if (!session.isMultiPackage()) { return isApexSession(session); return filter.test(session); } synchronized (mStagedSessions) { return !(Arrays.stream(session.getChildSessionIds()) // Retrieve cached sessions matching ids. .mapToObj(i -> mStagedSessions.get(i)) // Filter only the ones containing APEX. .filter(childSession -> isApexSession(childSession)) .filter(childSession -> filter.test(childSession)) .collect(Collectors.toList()) .isEmpty()); } } private boolean sessionContainsApex(@NonNull PackageInstallerSession session) { return sessionContains(session, (s) -> isApexSession(s)); } private boolean sessionContainsApk(@NonNull PackageInstallerSession session) { return sessionContains(session, (s) -> !isApexSession(s)); } private void resumeSession(@NonNull PackageInstallerSession session) { boolean hasApex = sessionContainsApex(session); if (hasApex) { Loading Loading @@ -326,7 +343,7 @@ public class StagingManager { } } // The APEX part of the session is activated, proceed with the installation of APKs. if (!installApksInSession(session)) { if (!installApksInSession(session, /* preReboot */ false)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "Staged installation of APKs failed. Check logcat messages for" + "more information."); Loading @@ -343,7 +360,6 @@ public class StagingManager { + "to the previous state of APEXd."); mPowerManager.reboot(null); } return; } Loading @@ -366,7 +382,7 @@ public class StagingManager { } private PackageInstallerSession createAndWriteApkSession( @NonNull PackageInstallerSession originalSession) { @NonNull PackageInstallerSession originalSession, boolean preReboot) { if (originalSession.stageDir == null) { Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir"); return null; Loading @@ -379,9 +395,14 @@ public class StagingManager { PackageInstaller.SessionParams params = originalSession.params.copy(); params.isStaged = false; params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; params.installFlags |= PackageManager.INSTALL_STAGED; // TODO(b/129744602): use the userid from the original session. if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; params.installFlags |= PackageManager.INSTALL_DRY_RUN; } else { params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; } int apkSessionId = mPi.createSession( params, originalSession.getInstallerPackageName(), 0 /* UserHandle.SYSTEM */); Loading @@ -408,8 +429,9 @@ public class StagingManager { } private boolean commitApkSession(@NonNull PackageInstallerSession apkSession, int originalSessionId) { int originalSessionId, boolean preReboot) { if (!preReboot) { if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { // If rollback is available for this session, notify the rollback // manager of the apk session so it can properly enable rollback. Loading @@ -421,6 +443,7 @@ public class StagingManager { // Cannot happen, the rollback manager is in the same process. } } } final LocalIntentReceiver receiver = new LocalIntentReceiver(); apkSession.commit(receiver.getIntentSender(), false); Loading @@ -435,14 +458,15 @@ public class StagingManager { return false; } private boolean installApksInSession(@NonNull PackageInstallerSession session) { private boolean installApksInSession(@NonNull PackageInstallerSession session, boolean preReboot) { if (!session.isMultiPackage() && !isApexSession(session)) { // APK single-packaged staged session. Do a regular install. PackageInstallerSession apkSession = createAndWriteApkSession(session); PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot); if (apkSession == null) { return false; } return commitApkSession(apkSession, session.sessionId); return commitApkSession(apkSession, session.sessionId, preReboot); } else if (session.isMultiPackage()) { // For multi-package staged sessions containing APKs, we identify which child sessions // contain an APK, and with those then create a new multi-package group of sessions, Loading @@ -464,6 +488,9 @@ public class StagingManager { } PackageInstaller.SessionParams params = session.params.copy(); params.isStaged = false; if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; } // TODO(b/129744602): use the userid from the original session. int apkParentSessionId = mPi.createSession( params, session.getInstallerPackageName(), Loading @@ -478,7 +505,8 @@ public class StagingManager { } for (PackageInstallerSession sessionToClone : childSessions) { PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone); PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone, preReboot); if (apkChildSession == null) { return false; } Loading @@ -489,7 +517,7 @@ public class StagingManager { return false; } } return commitApkSession(apkParentSession, session.sessionId); return commitApkSession(apkParentSession, session.sessionId, preReboot); } // APEX single-package staged session, nothing to do. return true; Loading