Merge "Fail to enable rollback if enable rollback times out" into qt-dev

This commit is contained in:
Shafik Nassar
2019-05-10 10:23:53 +00:00
committed by Android (Google) Code Review
6 changed files with 122 additions and 5 deletions

View File

@@ -2444,6 +2444,18 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_ENABLE_ROLLBACK =
"android.intent.action.PACKAGE_ENABLE_ROLLBACK";
/**
* Broadcast Action: Sent to the system rollback manager when the rollback for a certain
* package needs to be cancelled.
*
* <p class="note">This intent is sent by PackageManagerService to notify RollbackManager
* that enabling a specific rollback has timed out.
*
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CANCEL_ENABLE_ROLLBACK =
"android.intent.action.CANCEL_ENABLE_ROLLBACK";
/**
* Broadcast Action: A rollback has been committed.
*

View File

@@ -40,7 +40,7 @@ import java.util.List;
* used to initiate rollback of those packages for a limited time period after
* upgrade.
*
* @see PackageInstaller.SessionParams#setEnableRollback()
* @see PackageInstaller.SessionParams#setEnableRollback(boolean)
* @hide
*/
@SystemApi @TestApi
@@ -52,7 +52,7 @@ public final class RollbackManager {
/**
* Lifetime duration of rollback packages in millis. A rollback will be available for
* at most that duration of time after a package is installed with
* {@link PackageInstaller.SessionParams#setEnableRollback()}.
* {@link PackageInstaller.SessionParams#setEnableRollback(boolean)}.
*
* <p>If flag value is negative, the default value will be assigned.
*

View File

@@ -43,6 +43,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />
<protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />
<protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />

View File

@@ -1746,6 +1746,15 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
params.handleRollbackEnabled();
Intent rollbackTimeoutIntent = new Intent(
Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
rollbackTimeoutIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
enableRollbackToken);
rollbackTimeoutIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
}
break;
}

View File

@@ -184,7 +184,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
getHandler().post(() -> {
boolean success = enableRollback(installFlags, newPackageCodePath,
installedUsers, user);
installedUsers, user, token);
int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
if (!success) {
ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
@@ -203,6 +203,27 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}, enableRollbackFilter, null, getHandler());
IntentFilter enableRollbackTimedOutFilter = new IntentFilter();
enableRollbackTimedOutFilter.addAction(Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
int token = intent.getIntExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
synchronized (mLock) {
for (NewRollback rollback : mNewRollbacks) {
if (rollback.hasToken(token)) {
rollback.isCancelled = true;
return;
}
}
}
}
}
}, enableRollbackTimedOutFilter, null, getHandler());
registerTimeChangeReceiver();
}
@@ -818,10 +839,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* @param newPackageCodePath path to the package about to be installed.
* @param installedUsers the set of users for which a given package is installed.
* @param user the user that owns the install session to enable rollback on.
* @param token the distinct rollback token sent by package manager.
* @return true if enabling the rollback succeeds, false otherwise.
*/
private boolean enableRollback(int installFlags, File newPackageCodePath,
int[] installedUsers, @UserIdInt int user) {
int[] installedUsers, @UserIdInt int user, int token) {
// Find the session id associated with this install.
// TODO: It would be nice if package manager or package installer told
@@ -914,6 +936,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
mNewRollbacks.add(newRollback);
}
}
newRollback.addToken(token);
return enableRollbackForPackageSession(newRollback.data, packageSession,
installedUsers, /* snapshotUserData*/ true);
@@ -1016,7 +1039,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
public void restoreUserData(String packageName, int[] userIds, int appId, long ceDataInode,
String seInfo, int token) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("restoureUserData may only be called by the system.");
throw new SecurityException("restoreUserData may only be called by the system.");
}
getHandler().post(() -> {
@@ -1272,6 +1295,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
deleteRollback(data);
return null;
}
if (newRollback.isCancelled) {
Log.e(TAG, "Rollback has been cancelled by PackageManager");
deleteRollback(data);
return null;
}
// It's safe to access data.info outside a synchronized block because
// this is running on the handler thread and all changes to the
@@ -1451,6 +1479,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
private static class NewRollback {
public final RollbackData data;
/**
* This array holds all of the rollback tokens associated with package sessions included
* in this rollback. This is used to identify which rollback should be cancelled in case
* {@link PackageManager} sends an {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
*/
private final IntArray mTokens = new IntArray();
/**
* Session ids for all packages in the install.
* For multi-package sessions, this is the list of child session ids.
@@ -1459,10 +1494,31 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
*/
public final int[] packageSessionIds;
/**
* Flag to determine whether the RollbackData has been cancelled.
*
* <p>RollbackData could be invalidated and cancelled if RollbackManager receives
* {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
*
* <p>The main underlying assumption here is that if enabling the rollback times out, then
* {@link PackageManager} will NOT send
* {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts
* {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}.
*/
public boolean isCancelled = false;
NewRollback(RollbackData data, int[] packageSessionIds) {
this.data = data;
this.packageSessionIds = packageSessionIds;
}
public void addToken(int token) {
mTokens.add(token);
}
public boolean hasToken(int token) {
return mTokens.indexOf(token) != -1;
}
}
NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {

View File

@@ -35,6 +35,7 @@ import android.content.pm.VersionedPackage;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -60,6 +61,12 @@ public class RollbackTest {
private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B";
private static final String INSTRUMENTED_APP = "com.android.tests.rollback";
// copied from PackageManagerService#PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS
// TODO: find a better place for the property so that it can be imported in tests
// maybe android.content.pm.PackageManager?
private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
"enable_rollback_timeout";
/**
* Test basic rollbacks.
*/
@@ -952,6 +959,38 @@ public class RollbackTest {
}
}
@Test
public void testEnableRollbackTimeoutFailsRollback() throws Exception {
try {
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.TEST_MANAGE_ROLLBACKS,
Manifest.permission.MANAGE_ROLLBACKS,
Manifest.permission.WRITE_DEVICE_CONFIG);
//setting the timeout to a very short amount that will definitely be triggered
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
Long.toString(1), false /* makeDefault*/);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
RollbackTestUtils.uninstall(TEST_APP_A);
RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
} finally {
//setting the timeout back to default
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
null, false /* makeDefault*/);
RollbackTestUtils.dropShellPermissionIdentity();
}
}
// Helper function to test that the given rollback info is a rollback for
// the atomic set {A2, B2} -> {A1, B1}.
private void assertRollbackInfoForAandB(RollbackInfo rollback) {