Framework side of issue #7302511: GCM client needs to use new framework API...
...to fix background->shutdown delivery race
Add ACTION_STOPPING and ACTION_STARTING to allow such apps to keep track of
which users are started/stopped, and be involved in the process of stopping
a user.
Also get rid of the scale part of the wallpaper transitions, since it seems
like I have still failed at getting the user switch to wait until the new
wallpaper is displayed.
Change-Id: If7e8fdae3544a9d7987a1b9274dc8b49022f6f62
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b1a2a2a..c2aa3a5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3739,8 +3739,7 @@
private void forceStopUserLocked(int userId) {
forceStopPackageLocked(null, -1, false, false, true, false, userId);
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
@@ -14128,6 +14127,19 @@
final UserStartedState uss = mStartedUsers.get(userId);
+ // Make sure user is in the started state. If it is currently
+ // stopping, we need to knock that off.
+ if (uss.mState == UserStartedState.STATE_STOPPING) {
+ // If we are stopping, we haven't sent ACTION_SHUTDOWN,
+ // so we can just fairly silently bring the user back from
+ // the almost-dead.
+ uss.mState = UserStartedState.STATE_RUNNING;
+ } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) {
+ // This means ACTION_SHUTDOWN has been sent, so we will
+ // need to treat this as a new boot of the user.
+ uss.mState = UserStartedState.STATE_BOOTING;
+ }
+
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -14205,6 +14217,19 @@
null, null, 0, null, null,
android.Manifest.permission.MANAGE_USERS,
false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+ intent = new Intent(Intent.ACTION_USER_STARTING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ broadcastIntentLocked(null, null, intent,
+ null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+ throws RemoteException {
+ }
+ }, 0, null, null,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -14293,7 +14318,8 @@
void finishUserSwitch(UserStartedState uss) {
synchronized (this) {
- if (uss.mState == UserStartedState.STATE_BOOTING
+ if ((uss.mState == UserStartedState.STATE_BOOTING
+ || uss.mState == UserStartedState.STATE_SHUTDOWN)
&& mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
uss.mState = UserStartedState.STATE_RUNNING;
final int userId = uss.mHandle.getIdentifier();
@@ -14315,7 +14341,8 @@
num--;
continue;
}
- if (oldUss.mState == UserStartedState.STATE_STOPPING) {
+ if (oldUss.mState == UserStartedState.STATE_STOPPING
+ || oldUss.mState == UserStartedState.STATE_SHUTDOWN) {
// This user is already stopping, doesn't count.
num--;
i++;
@@ -14380,23 +14407,50 @@
uss.mStopCallbacks.add(callback);
}
- if (uss.mState != UserStartedState.STATE_STOPPING) {
+ if (uss.mState != UserStartedState.STATE_STOPPING
+ && uss.mState != UserStartedState.STATE_SHUTDOWN) {
uss.mState = UserStartedState.STATE_STOPPING;
long ident = Binder.clearCallingIdentity();
try {
- // Inform of user switch
- Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
- final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() {
+ // We are going to broadcast ACTION_USER_STOPPING and then
+ // once that is down send a final ACTION_SHUTDOWN and then
+ // stop the user.
+ final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING);
+ stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
+ // This is the result receiver for the final shutdown broadcast.
+ final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
finishUserStop(uss);
}
};
- broadcastIntentLocked(null, null, intent,
- null, resultReceiver, 0, null, null, null,
- true, false, MY_PID, Process.SYSTEM_UID, userId);
+ // This is the result receiver for the initial stopping broadcast.
+ final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ // On to the next.
+ synchronized (ActivityManagerService.this) {
+ if (uss.mState != UserStartedState.STATE_STOPPING) {
+ // Whoops, we are being started back up. Abort, abort!
+ return;
+ }
+ uss.mState = UserStartedState.STATE_SHUTDOWN;
+ }
+ broadcastIntentLocked(null, null, shutdownIntent,
+ null, shutdownReceiver, 0, null, null, null,
+ true, false, MY_PID, Process.SYSTEM_UID, userId);
+ }
+ };
+ // Kick things off.
+ broadcastIntentLocked(null, null, stoppingIntent,
+ null, stoppingReceiver, 0, null, null,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -14411,8 +14465,9 @@
ArrayList<IStopUserCallback> callbacks;
synchronized (this) {
callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
- if (uss.mState != UserStartedState.STATE_STOPPING
- || mStartedUsers.get(userId) != uss) {
+ if (mStartedUsers.get(userId) != uss) {
+ stopped = false;
+ } else if (uss.mState != UserStartedState.STATE_SHUTDOWN) {
stopped = false;
} else {
stopped = true;
@@ -14476,7 +14531,8 @@
boolean isUserRunningLocked(int userId) {
UserStartedState state = mStartedUsers.get(userId);
- return state != null && state.mState != UserStartedState.STATE_STOPPING;
+ return state != null && state.mState != UserStartedState.STATE_STOPPING
+ && state.mState != UserStartedState.STATE_SHUTDOWN;
}
@Override