Merge "Added more events to user switch journey" into rvc-dev am: 6a468598f0 am: b2c33af782

Change-Id: I31bbde78b7b5df2d51f8010451d44a228267b36c
This commit is contained in:
Mayank Garg
2020-05-21 08:46:43 +00:00
committed by Automerger Merge Worker
2 changed files with 155 additions and 52 deletions

View File

@@ -9677,6 +9677,9 @@ message UserLifecycleEventOccurred {
SWITCH_USER = 1; // Indicates that this is a user switch event
START_USER = 2; // Indicates that this is a user start event
CREATE_USER = 3; // Indicates that this is a user create event
USER_RUNNING_LOCKED = 4; // Indicates that user is running in locked state
UNLOCKING_USER = 5; // Indicates that this is a user unlocking event
UNLOCKED_USER = 6; // Indicates that this is a user unlocked event
}
optional Event event = 3;

View File

@@ -90,7 +90,6 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
@@ -154,6 +153,15 @@ class UserController implements Handler.Callback {
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
static final int START_USER_SWITCH_FG_MSG = 120;
// Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
// the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
// complete within {@link USER_JOURNEY_TIMEOUT}.
private static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200;
// Wait time for completing the user journey. If a user journey is not complete within this
// time, the remaining lifecycle events for the journey would not be logged in statsd.
// Timeout set for 90 seconds.
private static final int USER_JOURNEY_TIMEOUT_MS = 90_000;
// UI thread message constants
static final int START_USER_SWITCH_UI_MSG = 1000;
@@ -194,14 +202,37 @@ class UserController implements Handler.Callback {
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__START_USER;
private static final int USER_LIFECYCLE_EVENT_CREATE_USER =
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER;
private static final int USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED =
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__USER_RUNNING_LOCKED;
private static final int USER_LIFECYCLE_EVENT_UNLOCKING_USER =
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER;
private static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER =
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER;
@IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = {
USER_LIFECYCLE_EVENT_UNKNOWN,
USER_LIFECYCLE_EVENT_SWITCH_USER,
USER_LIFECYCLE_EVENT_START_USER,
USER_LIFECYCLE_EVENT_CREATE_USER,
USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
USER_LIFECYCLE_EVENT_UNLOCKING_USER,
USER_LIFECYCLE_EVENT_UNLOCKED_USER,
})
@interface UserLifecycleEvent {}
// User lifecyle event state, defined in the UserLifecycleEventOccurred atom for statsd
private static final int USER_LIFECYCLE_EVENT_STATE_BEGIN =
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN;
private static final int USER_LIFECYCLE_EVENT_STATE_FINISH =
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH;
private static final int USER_LIFECYCLE_EVENT_STATE_NONE =
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE;
@IntDef(prefix = { "USER_LIFECYCLE_EVENT_STATE" }, value = {
USER_LIFECYCLE_EVENT_STATE_BEGIN,
USER_LIFECYCLE_EVENT_STATE_FINISH,
USER_LIFECYCLE_EVENT_STATE_NONE,
})
@interface UserLifecycleEventState {}
/**
* Maximum number of users we allow to be running at a time, including system user.
*
@@ -311,11 +342,11 @@ class UserController implements Handler.Callback {
private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>();
/**
* A per-user, journey to session id map, used for statsd logging for the
* {@link UserIdInt} to {@link UserJourneySession} mapping used for statsd logging for the
* UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms.
*/
@GuardedBy("mUserJourneyToSessionIdMap")
private final SparseArray<SparseLongArray> mUserJourneyToSessionIdMap = new SparseArray<>();
@GuardedBy("mUserIdToUserJourneyMap")
private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>();
UserController(ActivityManagerService service) {
this(new Injector(service));
@@ -447,6 +478,8 @@ class UserController implements Handler.Callback {
// but we might immediately step into RUNNING below if the user
// storage is already unlocked.
if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
USER_LIFECYCLE_EVENT_STATE_NONE);
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
@@ -503,6 +536,8 @@ class UserController implements Handler.Callback {
private boolean finishUserUnlocking(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKING, userId);
logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
// Only keep marching forward if user is actually unlocked
if (!StorageManager.isUserKeyUnlocked(userId)) return false;
synchronized (mLock) {
@@ -2396,8 +2431,8 @@ class UserController implements Handler.Callback {
case START_USER_SWITCH_FG_MSG:
logUserJourneyInfo(getUserInfo(getCurrentUserId()), getUserInfo(msg.arg1),
USER_JOURNEY_USER_SWITCH_FG);
logUserLifecycleEvent(msg.arg1, USER_JOURNEY_USER_SWITCH_FG,
USER_LIFECYCLE_EVENT_SWITCH_USER, true);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
startUserInForeground(msg.arg1);
break;
case REPORT_USER_SWITCH_MSG:
@@ -2420,14 +2455,14 @@ class UserController implements Handler.Callback {
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(msg.arg1), msg.arg1);
logUserJourneyInfo(null, getUserInfo(msg.arg1), USER_JOURNEY_USER_START);
logUserLifecycleEvent(msg.arg1, USER_JOURNEY_USER_START,
USER_LIFECYCLE_EVENT_START_USER, true);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
mInjector.getSystemServiceManager().startUser(TimingsTraceAndSlog.newAsyncLog(),
msg.arg1);
logUserLifecycleEvent(msg.arg1, USER_JOURNEY_USER_START,
USER_LIFECYCLE_EVENT_START_USER, false);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
USER_LIFECYCLE_EVENT_STATE_FINISH);
clearSessionId(msg.arg1, USER_JOURNEY_USER_START);
break;
case USER_UNLOCK_MSG:
@@ -2437,10 +2472,17 @@ class UserController implements Handler.Callback {
FgThread.getHandler().post(() -> {
mInjector.loadUserRecents(userId);
});
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKING_USER,
USER_LIFECYCLE_EVENT_STATE_FINISH);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
finishUserUnlocked((UserState) msg.obj);
break;
case USER_UNLOCKED_MSG:
mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER,
USER_LIFECYCLE_EVENT_STATE_FINISH);
clearSessionId(msg.arg1);
break;
case USER_CURRENT_MSG:
mInjector.batteryStatsServiceNoteEvent(
@@ -2458,11 +2500,8 @@ class UserController implements Handler.Callback {
case REPORT_USER_SWITCH_COMPLETE_MSG:
dispatchUserSwitchComplete(msg.arg1);
final int currentJourney = mUserSwitchUiEnabled ? USER_JOURNEY_USER_SWITCH_UI
: USER_JOURNEY_USER_SWITCH_FG;
logUserLifecycleEvent(msg.arg1, currentJourney,
USER_LIFECYCLE_EVENT_SWITCH_USER, false);
clearSessionId(msg.arg1, currentJourney);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER,
USER_LIFECYCLE_EVENT_STATE_FINISH);
break;
case REPORT_LOCKED_BOOT_COMPLETE_MSG:
dispatchLockedBootComplete(msg.arg1);
@@ -2471,10 +2510,13 @@ class UserController implements Handler.Callback {
final Pair<UserInfo, UserInfo> fromToUserPair = (Pair<UserInfo, UserInfo>) msg.obj;
logUserJourneyInfo(fromToUserPair.first, fromToUserPair.second,
USER_JOURNEY_USER_SWITCH_UI);
logUserLifecycleEvent(fromToUserPair.second.id, USER_JOURNEY_USER_SWITCH_UI,
USER_LIFECYCLE_EVENT_SWITCH_USER, true);
logUserLifecycleEvent(fromToUserPair.second.id, USER_LIFECYCLE_EVENT_SWITCH_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
showUserSwitchDialog(fromToUserPair);
break;
case CLEAR_USER_JOURNEY_SESSION_MSG:
clearSessionId(msg.arg1);
break;
}
return false;
}
@@ -2482,27 +2524,61 @@ class UserController implements Handler.Callback {
/**
* statsd helper method for logging the start of a user journey via a UserLifecycleEventOccurred
* atom given the originating and targeting users for the journey.
*
* Note: these info atoms are currently logged more than once per journey since there is no
* state associated with the user's ongoing journey - this will be updated in a later CL.
*/
private void logUserJourneyInfo(UserInfo origin, UserInfo target, @UserJourney int journey) {
final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
synchronized (mUserJourneyToSessionIdMap) {
SparseLongArray userSessions = mUserJourneyToSessionIdMap.get(target.id);
if (userSessions == null) {
userSessions = new SparseLongArray();
mUserJourneyToSessionIdMap.put(target.id, userSessions);
}
final long oldSessionId = userSessions.get(journey);
if (oldSessionId != INVALID_SESSION_ID) {
// potentially an incomplete or timed-out session
synchronized (mUserIdToUserJourneyMap) {
UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(target.id);
if (userJourneySession != null) {
// TODO(b/157007231): Move this logic to a separate class/file.
if ((userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_UI
&& journey == USER_JOURNEY_USER_START)
|| (userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG
&& journey == USER_JOURNEY_USER_START)) {
/*
* There is already a user switch journey, and a user start journey for the same
* target user received. User start journey is most likely a part of user switch
* journey so no need to create a new journey for user start.
*/
if (DEBUG_MU) {
Slog.d(TAG, journey + " not logged as it is expected to be part of "
+ userJourneySession.mJourney);
}
return;
}
/*
* Possible reasons for this condition to be true:
* - A user switch journey is received while another user switch journey is in
* process for the same user.
* - A user switch journey is received while user start journey is in process for
* the same user.
* - A user start journey is received while another user start journey is in process
* for the same user.
* In all cases potentially an incomplete, timed-out session or multiple
* simultaneous requests. It is not possible to keep track of multiple sessions for
* the same user, so previous session is abandoned.
*/
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED,
oldSessionId, target.id, USER_LIFECYCLE_EVENT_UNKNOWN,
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
userJourneySession.mSessionId, target.id, USER_LIFECYCLE_EVENT_UNKNOWN,
USER_LIFECYCLE_EVENT_STATE_NONE);
}
// update session id
userSessions.put(journey, newSessionId);
if (DEBUG_MU) {
Slog.d(TAG,
"Starting a new journey: " + journey + " with session id: " + newSessionId);
}
userJourneySession = new UserJourneySession(newSessionId, journey);
mUserIdToUserJourneyMap.put(target.id, userJourneySession);
/*
* User lifecyle journey would be complete when {@code #clearSessionId} is called after
* the last expected lifecycle event for the journey. It may be possible that the last
* event is not called, e.g., user not unlocked after user switching. In such cases user
* journey is cleared after {@link USER_JOURNEY_TIMEOUT}.
*/
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG);
mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG,
target.id), USER_JOURNEY_TIMEOUT_MS);
}
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, newSessionId,
@@ -2511,41 +2587,65 @@ class UserController implements Handler.Callback {
}
/**
* statsd helper method for logging the begin or finish of the given event for the
* UserLifecycleEventOccurred statsd atom.
* Note: This does not clear the user's journey session id - if this event represents the end of
* a particular journey, call {@link #clearSessionId} to indicate that the session is over.
* statsd helper method for logging the given event for the UserLifecycleEventOccurred statsd
* atom.
*/
private void logUserLifecycleEvent(@UserIdInt int userId, @UserJourney int journey,
@UserLifecycleEvent int event, boolean begin) {
private void logUserLifecycleEvent(@UserIdInt int userId, @UserLifecycleEvent int event,
@UserLifecycleEventState int eventState) {
final long sessionId;
synchronized (mUserJourneyToSessionIdMap) {
final SparseLongArray eventToSessionMap = mUserJourneyToSessionIdMap.get(userId);
if (eventToSessionMap == null || eventToSessionMap.size() == 0) {
return;
}
sessionId = eventToSessionMap.get(journey);
if (sessionId == INVALID_SESSION_ID) {
synchronized (mUserIdToUserJourneyMap) {
final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId);
if (userJourneySession == null || userJourneySession.mSessionId == INVALID_SESSION_ID) {
Slog.w(TAG, "UserLifecycleEvent " + event
+ " received without an active userJourneySession.");
return;
}
sessionId = userJourneySession.mSessionId;
}
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
event, begin ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN
: FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH);
event, eventState);
}
/**
* Clears the user's session id associated with the given UserJourney (for statsd).
* Clears the {@link UserJourneySession} for a given {@link UserIdInt} and {@link UserJourney}.
*/
private void clearSessionId(@UserIdInt int userId, @UserJourney int journey) {
synchronized (mUserJourneyToSessionIdMap) {
if (mUserJourneyToSessionIdMap.get(userId) != null) {
mUserJourneyToSessionIdMap.get(userId).delete(journey);
synchronized (mUserIdToUserJourneyMap) {
final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId);
if (userJourneySession != null && userJourneySession.mJourney == journey) {
clearSessionId(userId);
}
}
}
/**
* Clears the {@link UserJourneySession} for a given {@link UserIdInt}.
*/
private void clearSessionId(@UserIdInt int userId) {
synchronized (mUserIdToUserJourneyMap) {
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG);
mUserIdToUserJourneyMap.delete(userId);
}
}
/**
* Helper class to store user journey and session id.
*
* <p> User journey tracks a chain of user lifecycle events occurring during different user
* activities such as user start, user switch, and user creation.
*/
// TODO(b/157007231): Move this class and user journey tracking logic to a separate file.
private static class UserJourneySession {
final long mSessionId;
@UserJourney final int mJourney;
UserJourneySession(long sessionId, @UserJourney int journey) {
mJourney = journey;
mSessionId = sessionId;
}
}
private static class UserProgressListener extends IProgressListener.Stub {
private volatile long mUnlockStarted;
@Override