Flesh out user locked/unlocked lifecycle.
When a user is first started, we assume that they're "locked" meaning
that credential-encrypted data is unavailable. Once credentials have
been supplied, we can transition the user to a fully running state.
To facilitate this lifecycle, UserState now has two separate
RUNNING_LOCKED and RUNNING states. To ensure consistent events are
sent on all devices, we always step through RUNNING_LOCKED before
arriving at RUNNING. This consistency means that apps processing
data based on the new ACTION_LOCKED_BOOT_COMPLETED broadcast and
system services using the new onUnlockUser() event will be less
bug-prone over time.
If the user storage is unlocked (which is the case on the majority
of legacy devices), we immediately transition from the RUNNING_LOCKED
into the RUNNING state.
Add logging for all state transitions.
When we "recover" a user in the process of being shut down, return
to the last known state.
Bug: 25943941
Change-Id: I5fec980f10b0d0fb2c272a662d193dc15136f9b9
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index b5b5c1d..7b18a17 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -19,28 +19,37 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
import android.app.IStopUserCallback;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.Slog;
public final class UserState {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "UserState" : TAG_AM;
+
// User is first coming up.
public final static int STATE_BOOTING = 0;
+ // User is in the locked running state.
+ public final static int STATE_RUNNING_LOCKED = 1;
// User is in the normal running state.
- public final static int STATE_RUNNING = 1;
+ public final static int STATE_RUNNING = 2;
// User is in the initial process of being stopped.
- public final static int STATE_STOPPING = 2;
+ public final static int STATE_STOPPING = 3;
// User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN.
- public final static int STATE_SHUTDOWN = 3;
+ public final static int STATE_SHUTDOWN = 4;
public final UserHandle mHandle;
public final ArrayList<IStopUserCallback> mStopCallbacks
= new ArrayList<IStopUserCallback>();
- public int mState = STATE_BOOTING;
+ public int state = STATE_BOOTING;
+ public int lastState = STATE_BOOTING;
public boolean switching;
public boolean initializing;
- public boolean unlocked;
/**
* The last time that a provider was reported to usage stats as being brought to important
@@ -52,22 +61,32 @@
mHandle = handle;
}
- void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mState=");
- switch (mState) {
- case STATE_BOOTING: pw.print("BOOTING"); break;
- case STATE_RUNNING: pw.print("RUNNING"); break;
- case STATE_STOPPING: pw.print("STOPPING"); break;
- case STATE_SHUTDOWN: pw.print("SHUTDOWN"); break;
- default: pw.print(mState); break;
+ public void setState(int newState) {
+ if (DEBUG_MU) {
+ Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from "
+ + stateToString(state) + " to " + stateToString(newState));
}
+ lastState = state;
+ state = newState;
+ }
+
+ private static String stateToString(int state) {
+ switch (state) {
+ case STATE_BOOTING: return "BOOTING";
+ case STATE_RUNNING_LOCKED: return "RUNNING_LOCKED";
+ case STATE_RUNNING: return "RUNNING";
+ case STATE_STOPPING: return "STOPPING";
+ case STATE_SHUTDOWN: return "SHUTDOWN";
+ default: return Integer.toString(state);
+ }
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("state="); pw.print(stateToString(state));
+ pw.print(" lastState="); pw.print(stateToString(lastState));
if (switching) pw.print(" SWITCHING");
if (initializing) pw.print(" INITIALIZING");
- if (unlocked) {
- pw.print(" UNLOCKED");
- } else {
- pw.print(" LOCKED");
- }
pw.println();
}
}