Merge changes I5062993d,I74063a62,Id95e9dd6,Ie8496976

* changes:
  RollbackManager: Check for applied staged sessions on boot.
  Handle enable rollback for the apk session part of a staged install
  Track the apkSessionId associated with a staged install.
  Notify RollbackManager of the apk session for a staged install.
This commit is contained in:
Richard Uhler
2019-02-06 11:44:58 +00:00
committed by Android (Google) Code Review
6 changed files with 149 additions and 0 deletions

View File

@@ -47,4 +47,8 @@ interface IRollbackManager {
//
// NOTE: This call is synchronous.
boolean notifyStagedSession(int sessionId);
// Used by the staging manager to notify the RollbackManager of the apk
// session for a staged session.
void notifyStagedApkSession(int originalSessionId, int apkSessionId);
}

View File

@@ -380,6 +380,19 @@ public class StagingManager {
private boolean commitApkSession(@NonNull PackageInstallerSession apkSession,
int originalSessionId) {
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.
}
}
final LocalIntentReceiver receiver = new LocalIntentReceiver();
apkSession.commit(receiver.getIntentSender(), false);
final Intent result = receiver.getResult();

View File

@@ -63,6 +63,11 @@ class RollbackData {
*/
public boolean isAvailable;
/**
* The id of the post-reboot apk session for a staged install, if any.
*/
public int apkSessionId = -1;
/**
* Whether this Rollback is currently in progress. This field is true from the point
* we commit a {@code PackageInstaller} session containing these packages to the point the

View File

@@ -44,4 +44,11 @@ public final class RollbackManagerService extends SystemService {
public void onUnlockUser(int user) {
mService.onUnlockUser(user);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_BOOT_COMPLETED) {
mService.onBootCompleted();
}
}
}

View File

@@ -486,6 +486,47 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
});
}
void onBootCompleted() {
getHandler().post(() -> {
// Check to see if any staged sessions with rollback enabled have
// been applied.
List<RollbackData> staged = new ArrayList<>();
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
for (RollbackData data : mAvailableRollbacks) {
if (data.stagedSessionId != -1) {
staged.add(data);
}
}
}
for (RollbackData data : staged) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo session = installer.getSessionInfo(
data.stagedSessionId);
if (session != null) {
if (session.isSessionApplied()) {
synchronized (mLock) {
data.isAvailable = true;
}
try {
mRollbackStore.saveAvailableRollback(data);
} catch (IOException ioe) {
Log.e(TAG, "Unable to save rollback info for : "
+ data.rollbackId, ioe);
}
} else if (session.isSessionFailed()) {
// TODO: Do we need to remove this from
// mAvailableRollbacks, or is it okay to leave as
// unavailable until the next reboot when it will go
// away on its own?
mRollbackStore.deleteAvailableRollback(data);
}
}
}
});
}
/**
* Load rollback data from storage if it has not already been loaded.
* After calling this funciton, mAvailableRollbacks and
@@ -725,6 +766,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// ourselves.
PackageInstaller.SessionInfo session = null;
int parentSessionId = -1;
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
if (info.isMultiPackage()) {
@@ -732,12 +774,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
// TODO: Check we only have one matching session?
parentSessionId = info.getSessionId();
session = child;
break;
}
}
} else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
// TODO: Check we only have one matching session?
parentSessionId = info.getSessionId();
session = info;
break;
}
@@ -748,6 +792,54 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return false;
}
// Check to see if this is the apk session for a staged session with
// rollback enabled.
// TODO: This check could be made more efficient.
RollbackData rd = null;
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
RollbackData data = mAvailableRollbacks.get(i);
if (data.apkSessionId == parentSessionId) {
rd = data;
break;
}
}
}
if (rd != null) {
// This is the apk session for a staged session. We have already
// backed up the apks, we just need to do user data backup.
PackageParser.PackageLite newPackage = null;
try {
newPackage = PackageParser.parsePackageLite(
new File(session.resolvedBaseCodePath), 0);
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "Unable to parse new package", e);
return false;
}
String packageName = newPackage.packageName;
for (PackageRollbackInfo info : rd.packages) {
if (info.getPackageName().equals(packageName)) {
IntArray pendingBackups = mUserdataHelper.snapshotAppData(
packageName, installedUsers);
info.getPendingBackups().addAll(pendingBackups);
try {
mRollbackStore.saveAvailableRollback(rd);
} catch (IOException ioe) {
// TODO: Hopefully this is okay because we will try
// again to save the rollback when the staged session
// is applied. Just so long as the device doesn't
// reboot before then.
Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe);
}
return true;
}
}
Log.e(TAG, "Unable to find package in apk session");
return false;
}
return enableRollbackForSession(session, installedUsers, true);
}
@@ -928,6 +1020,32 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}
@Override
public void notifyStagedApkSession(int originalSessionId, int apkSessionId) {
getHandler().post(() -> {
RollbackData rd = null;
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
RollbackData data = mAvailableRollbacks.get(i);
if (data.stagedSessionId == originalSessionId) {
data.apkSessionId = apkSessionId;
rd = data;
break;
}
}
}
if (rd != null) {
try {
mRollbackStore.saveAvailableRollback(rd);
} catch (IOException ioe) {
Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe);
}
}
});
}
/**
* Gets the version of the package currently installed.
* Returns null if the package is not currently installed.

View File

@@ -248,6 +248,7 @@ class RollbackStore {
dataJson.put("timestamp", data.timestamp.toString());
dataJson.put("stagedSessionId", data.stagedSessionId);
dataJson.put("isAvailable", data.isAvailable);
dataJson.put("apkSessionId", data.apkSessionId);
PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json"));
pw.println(dataJson.toString());
@@ -313,6 +314,7 @@ class RollbackStore {
stagedSessionId, isAvailable);
data.packages.addAll(packageRollbackInfosFromJson(dataJson.getJSONArray("packages")));
data.timestamp = Instant.parse(dataJson.getString("timestamp"));
data.apkSessionId = dataJson.getInt("apkSessionId");
return data;
} catch (JSONException | DateTimeParseException e) {
throw new IOException(e);