Merge "Verify staged APKs pre-reboot." into qt-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e5e876265c
@@ -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 {}
|
||||
@@ -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
|
||||
|
||||
@@ -15565,6 +15565,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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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.");
|
||||
@@ -343,7 +360,6 @@ public class StagingManager {
|
||||
+ "to the previous state of APEXd.");
|
||||
mPowerManager.reboot(null);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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 */);
|
||||
@@ -408,17 +429,19 @@ public class StagingManager {
|
||||
}
|
||||
|
||||
private boolean commitApkSession(@NonNull PackageInstallerSession apkSession,
|
||||
int originalSessionId) {
|
||||
int originalSessionId, boolean 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.
|
||||
final IRollbackManager rm = IRollbackManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.ROLLBACK_SERVICE));
|
||||
try {
|
||||
rm.notifyStagedApkSession(originalSessionId, apkSession.sessionId);
|
||||
} catch (RemoteException re) {
|
||||
// Cannot happen, the rollback manager is in the same process.
|
||||
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.
|
||||
final IRollbackManager rm = IRollbackManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.ROLLBACK_SERVICE));
|
||||
try {
|
||||
rm.notifyStagedApkSession(originalSessionId, apkSession.sessionId);
|
||||
} catch (RemoteException re) {
|
||||
// Cannot happen, the rollback manager is in the same process.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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(),
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user