fix: Refactor User Lifecycle Event and Report metrics logging
Improvements described in [this doc](https://docs.google.com/document/d/1X_f-kdVNnptKJnF9e5KO4WzbWqLNucJxPcnutk7cCgM/edit?resourcekey=0-LdjBpCMjyUxilDQlnyh6kg#) Bug: 265927093 Test: atest UserJourneyLoggerTest Change-Id: Id2632415b66834fe99fb627f9c9ed35d12ba207d Merged-In: Id2632415b66834fe99fb627f9c9ed35d12ba207d Merged-In: I102ed93f5a88a065f259b5cbaea42cc01c4a9ca1
This commit is contained in:
@@ -65,7 +65,6 @@ import android.util.Log;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -2532,38 +2531,6 @@ public class UserManager {
|
||||
return USER_TYPE_PROFILE_CLONE.equals(userType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to the
|
||||
* user type.
|
||||
* @hide
|
||||
*/
|
||||
public static int getUserTypeForStatsd(@NonNull String userType) {
|
||||
switch (userType) {
|
||||
case USER_TYPE_FULL_SYSTEM:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SYSTEM;
|
||||
case USER_TYPE_FULL_SECONDARY:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY;
|
||||
case USER_TYPE_FULL_GUEST:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_GUEST;
|
||||
case USER_TYPE_FULL_DEMO:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_DEMO;
|
||||
case USER_TYPE_FULL_RESTRICTED:
|
||||
return FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_RESTRICTED;
|
||||
case USER_TYPE_PROFILE_MANAGED:
|
||||
return FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_MANAGED;
|
||||
case USER_TYPE_SYSTEM_HEADLESS:
|
||||
return FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS;
|
||||
case USER_TYPE_PROFILE_CLONE:
|
||||
return FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE;
|
||||
default:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated Use {@link #isRestrictedProfile()}
|
||||
|
||||
@@ -67,7 +67,6 @@ import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.EventLog;
|
||||
import android.util.IndentingPrintWriter;
|
||||
@@ -78,6 +77,7 @@ import android.util.proto.ProtoOutputStream;
|
||||
import com.android.internal.os.TimeoutRecord;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.pm.UserJourneyLogger;
|
||||
import com.android.server.pm.UserManagerInternal;
|
||||
|
||||
import dalvik.annotation.optimization.NeverCompile;
|
||||
@@ -1518,7 +1518,7 @@ public class BroadcastQueueImpl extends BroadcastQueue {
|
||||
final UserInfo userInfo =
|
||||
(umInternal != null) ? umInternal.getUserInfo(r.userId) : null;
|
||||
if (userInfo != null) {
|
||||
userType = UserManager.getUserTypeForStatsd(userInfo.userType);
|
||||
userType = UserJourneyLogger.getUserTypeForStatsd(userInfo.userType);
|
||||
}
|
||||
Slog.i(TAG_BROADCAST,
|
||||
"BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
|
||||
|
||||
@@ -46,13 +46,24 @@ import static com.android.server.am.UserState.STATE_BOOTING;
|
||||
import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
|
||||
import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED;
|
||||
import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INVALID_SESSION_ID;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_BEGIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_FINISH;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_NONE;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_STOP;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKED_USER;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKING_USER;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED;
|
||||
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
|
||||
import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND_VISIBLE;
|
||||
import static com.android.server.pm.UserManagerInternal.USER_START_MODE_FOREGROUND;
|
||||
import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString;
|
||||
import static com.android.server.pm.UserManagerInternal.userStartModeToString;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
@@ -123,6 +134,8 @@ import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService.UserCompletedEventType;
|
||||
import com.android.server.SystemServiceManager;
|
||||
import com.android.server.am.UserState.KeyEvictedCallback;
|
||||
import com.android.server.pm.UserJourneyLogger;
|
||||
import com.android.server.pm.UserJourneyLogger.UserJourneySession;
|
||||
import com.android.server.pm.UserManagerInternal;
|
||||
import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
|
||||
import com.android.server.pm.UserManagerInternal.UserStartMode;
|
||||
@@ -138,7 +151,6 @@ import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
@@ -221,75 +233,6 @@ class UserController implements Handler.Callback {
|
||||
// TODO(b/197344658): Increase to 10s or 15s once we have a switch-UX-is-done invocation too.
|
||||
private static final int USER_COMPLETED_EVENT_DELAY_MS = 5 * 1000;
|
||||
|
||||
// Used for statsd logging with UserLifecycleJourneyReported + UserLifecycleEventOccurred atoms
|
||||
private static final long INVALID_SESSION_ID = 0;
|
||||
|
||||
// The various user journeys, defined in the UserLifecycleJourneyReported atom for statsd
|
||||
private static final int USER_JOURNEY_UNKNOWN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__UNKNOWN;
|
||||
private static final int USER_JOURNEY_USER_SWITCH_FG =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_FG;
|
||||
private static final int USER_JOURNEY_USER_SWITCH_UI =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_UI;
|
||||
private static final int USER_JOURNEY_USER_START =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START;
|
||||
private static final int USER_JOURNEY_USER_CREATE =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE;
|
||||
private static final int USER_JOURNEY_USER_STOP =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP;
|
||||
@IntDef(prefix = { "USER_JOURNEY" }, value = {
|
||||
USER_JOURNEY_UNKNOWN,
|
||||
USER_JOURNEY_USER_SWITCH_FG,
|
||||
USER_JOURNEY_USER_SWITCH_UI,
|
||||
USER_JOURNEY_USER_START,
|
||||
USER_JOURNEY_USER_CREATE,
|
||||
USER_JOURNEY_USER_STOP
|
||||
})
|
||||
@interface UserJourney {}
|
||||
|
||||
// The various user lifecycle events, defined in the UserLifecycleEventOccurred atom for statsd
|
||||
private static final int USER_LIFECYCLE_EVENT_UNKNOWN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN;
|
||||
private static final int USER_LIFECYCLE_EVENT_SWITCH_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__SWITCH_USER;
|
||||
private static final int USER_LIFECYCLE_EVENT_START_USER =
|
||||
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;
|
||||
private static final int USER_LIFECYCLE_EVENT_STOP_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_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,
|
||||
USER_LIFECYCLE_EVENT_STOP_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.
|
||||
*
|
||||
@@ -419,13 +362,6 @@ class UserController implements Handler.Callback {
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* {@link UserIdInt} to {@link UserJourneySession} mapping used for statsd logging for the
|
||||
* UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms.
|
||||
*/
|
||||
@GuardedBy("mUserIdToUserJourneyMap")
|
||||
private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Map of userId to {@link UserCompletedEventType} event flags, indicating which as-yet-
|
||||
* unreported user-starting events have transpired for the given user.
|
||||
@@ -621,8 +557,9 @@ 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.getUserJourneyLogger()
|
||||
.logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
|
||||
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
|
||||
@@ -694,8 +631,9 @@ 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);
|
||||
mInjector.getUserJourneyLogger()
|
||||
.logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER,
|
||||
EVENT_STATE_BEGIN);
|
||||
// If the user key hasn't been unlocked yet, we cannot proceed.
|
||||
if (!StorageManager.isUserKeyUnlocked(userId)) return false;
|
||||
synchronized (mLock) {
|
||||
@@ -1073,9 +1011,7 @@ class UserController implements Handler.Callback {
|
||||
return;
|
||||
}
|
||||
|
||||
logUserJourneyInfo(null, getUserInfo(userId), USER_JOURNEY_USER_STOP);
|
||||
logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_BEGIN);
|
||||
logUserJourneyBegin(userId, USER_JOURNEY_USER_STOP);
|
||||
|
||||
if (stopUserCallback != null) {
|
||||
uss.mStopCallbacks.add(stopUserCallback);
|
||||
@@ -1138,9 +1074,16 @@ class UserController implements Handler.Callback {
|
||||
synchronized (mLock) {
|
||||
if (uss.state != UserState.STATE_STOPPING) {
|
||||
// Whoops, we are being started back up. Abort, abort!
|
||||
logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_NONE);
|
||||
clearSessionId(userId);
|
||||
UserJourneySession session = mInjector.getUserJourneyLogger()
|
||||
.logUserJourneyFinishWithError(-1, getUserInfo(userId),
|
||||
USER_JOURNEY_USER_STOP, ERROR_CODE_ABORTED);
|
||||
if (session != null) {
|
||||
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
|
||||
} else {
|
||||
mInjector.getUserJourneyLogger()
|
||||
.logUserJourneyFinishWithError(-1, getUserInfo(userId),
|
||||
USER_JOURNEY_USER_STOP, ERROR_CODE_INVALID_SESSION_ID);
|
||||
}
|
||||
return;
|
||||
}
|
||||
uss.setState(UserState.STATE_SHUTDOWN);
|
||||
@@ -1247,9 +1190,11 @@ class UserController implements Handler.Callback {
|
||||
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
|
||||
}
|
||||
|
||||
logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_FINISH);
|
||||
clearSessionId(userId);
|
||||
UserJourneySession session = mInjector.getUserJourneyLogger()
|
||||
.logUserJourneyFinish(-1, userInfo, USER_JOURNEY_USER_STOP);
|
||||
if (session != null) {
|
||||
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
|
||||
}
|
||||
|
||||
if (lockUser) {
|
||||
dispatchUserLocking(userIdToLock, keyEvictedCallbacks);
|
||||
@@ -1259,9 +1204,11 @@ class UserController implements Handler.Callback {
|
||||
// which was paused while the SHUTDOWN flow of the user was in progress.
|
||||
resumePendingUserStarts(userId);
|
||||
} else {
|
||||
logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_NONE);
|
||||
clearSessionId(userId);
|
||||
UserJourneySession session = mInjector.getUserJourneyLogger()
|
||||
.finishAndClearIncompleteUserJourney(userId, USER_JOURNEY_USER_STOP);
|
||||
if (session != null) {
|
||||
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3135,10 +3082,7 @@ class UserController implements Handler.Callback {
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case START_USER_SWITCH_FG_MSG:
|
||||
logUserJourneyInfo(getUserInfo(getCurrentUserId()), getUserInfo(msg.arg1),
|
||||
USER_JOURNEY_USER_SWITCH_FG);
|
||||
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_BEGIN);
|
||||
logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_SWITCH_FG);
|
||||
startUserInForeground(msg.arg1);
|
||||
break;
|
||||
case REPORT_USER_SWITCH_MSG:
|
||||
@@ -3160,18 +3104,15 @@ class UserController implements Handler.Callback {
|
||||
mInjector.batteryStatsServiceNoteEvent(
|
||||
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_LIFECYCLE_EVENT_START_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_BEGIN);
|
||||
logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_START);
|
||||
|
||||
mInjector.onUserStarting(/* userId= */ msg.arg1);
|
||||
scheduleOnUserCompletedEvent(msg.arg1,
|
||||
UserCompletedEventType.EVENT_TYPE_USER_STARTING,
|
||||
USER_COMPLETED_EVENT_DELAY_MS);
|
||||
|
||||
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_FINISH);
|
||||
clearSessionId(msg.arg1, USER_JOURNEY_USER_START);
|
||||
mInjector.getUserJourneyLogger().logUserJourneyFinish(-1 , getUserInfo(msg.arg1),
|
||||
USER_JOURNEY_USER_START);
|
||||
break;
|
||||
case USER_UNLOCK_MSG:
|
||||
final int userId = msg.arg1;
|
||||
@@ -3180,10 +3121,11 @@ 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);
|
||||
|
||||
mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1,
|
||||
USER_LIFECYCLE_EVENT_UNLOCKING_USER, EVENT_STATE_FINISH);
|
||||
mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1,
|
||||
USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_BEGIN);
|
||||
|
||||
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
|
||||
t.traceBegin("finishUserUnlocked-" + userId);
|
||||
@@ -3199,9 +3141,9 @@ class UserController implements Handler.Callback {
|
||||
// (No need to acquire lock to read mCurrentUserId since it is volatile.)
|
||||
// TODO: Find something to wait for in the case of a profile.
|
||||
mCurrentUserId == msg.arg1 ? USER_COMPLETED_EVENT_DELAY_MS : 1000);
|
||||
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_FINISH);
|
||||
clearSessionId(msg.arg1);
|
||||
mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1,
|
||||
USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_FINISH);
|
||||
// Unlocking user is not a journey no need to clear sessionId
|
||||
break;
|
||||
case USER_CURRENT_MSG:
|
||||
mInjector.batteryStatsServiceNoteEvent(
|
||||
@@ -3224,22 +3166,24 @@ class UserController implements Handler.Callback {
|
||||
break;
|
||||
case REPORT_USER_SWITCH_COMPLETE_MSG:
|
||||
dispatchUserSwitchComplete(msg.arg1, msg.arg2);
|
||||
logUserLifecycleEvent(msg.arg2, USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_FINISH);
|
||||
UserJourneySession session = mInjector.getUserJourneyLogger()
|
||||
.logUserSwitchJourneyFinish(msg.arg1, getUserInfo(msg.arg2));
|
||||
if (session != null) {
|
||||
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
|
||||
}
|
||||
break;
|
||||
case REPORT_LOCKED_BOOT_COMPLETE_MSG:
|
||||
dispatchLockedBootComplete(msg.arg1);
|
||||
break;
|
||||
case START_USER_SWITCH_UI_MSG:
|
||||
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_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_BEGIN);
|
||||
logUserJourneyBegin(fromToUserPair.second.id, USER_JOURNEY_USER_SWITCH_UI);
|
||||
showUserSwitchDialog(fromToUserPair);
|
||||
break;
|
||||
case CLEAR_USER_JOURNEY_SESSION_MSG:
|
||||
logAndClearSessionId(msg.arg1);
|
||||
mInjector.getUserJourneyLogger()
|
||||
.finishAndClearIncompleteUserJourney(msg.arg1, msg.arg2);
|
||||
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, msg.obj);
|
||||
break;
|
||||
case COMPLETE_USER_SWITCH_MSG:
|
||||
completeUserSwitch(msg.arg1, msg.arg2);
|
||||
@@ -3317,123 +3261,29 @@ 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.
|
||||
*/
|
||||
private void logUserJourneyInfo(UserInfo origin, UserInfo target, @UserJourney int journey) {
|
||||
final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
|
||||
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
|
||||
|| userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG)
|
||||
&& (journey == USER_JOURNEY_USER_START
|
||||
|| journey == USER_JOURNEY_USER_STOP)) {
|
||||
/*
|
||||
* There is already a user switch journey, and a user start or stop journey for
|
||||
* the same target user received. New journey is most likely a part of user
|
||||
* switch journey so no need to create a new journey.
|
||||
*/
|
||||
if (DEBUG_MU) {
|
||||
Slogf.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,
|
||||
userJourneySession.mSessionId, target.id, USER_LIFECYCLE_EVENT_UNKNOWN,
|
||||
USER_LIFECYCLE_EVENT_STATE_NONE);
|
||||
}
|
||||
|
||||
private void logUserJourneyBegin(int targetId,
|
||||
@UserJourneyLogger.UserJourney int journey) {
|
||||
UserJourneySession oldSession = mInjector.getUserJourneyLogger()
|
||||
.finishAndClearIncompleteUserJourney(targetId, journey);
|
||||
if (oldSession != null) {
|
||||
if (DEBUG_MU) {
|
||||
Slogf.d(TAG,
|
||||
"Starting a new journey: " + journey + " with session id: " + newSessionId);
|
||||
"Starting a new journey: " + journey + " with session id: "
|
||||
+ oldSession);
|
||||
}
|
||||
|
||||
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}.
|
||||
* User lifecycle 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, /* arg2= */ 0), USER_JOURNEY_TIMEOUT_MS);
|
||||
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, oldSession);
|
||||
}
|
||||
UserJourneySession newSession = mInjector.getUserJourneyLogger()
|
||||
.logUserJourneyBegin(targetId, journey);
|
||||
mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG,
|
||||
targetId, /* arg2= */ journey, newSession), USER_JOURNEY_TIMEOUT_MS);
|
||||
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, newSessionId,
|
||||
journey, origin != null ? origin.id : -1,
|
||||
target.id, UserManager.getUserTypeForStatsd(target.userType), target.flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* statsd helper method for logging the given event for the UserLifecycleEventOccurred statsd
|
||||
* atom.
|
||||
*/
|
||||
private void logUserLifecycleEvent(@UserIdInt int userId, @UserLifecycleEvent int event,
|
||||
@UserLifecycleEventState int eventState) {
|
||||
final long sessionId;
|
||||
synchronized (mUserIdToUserJourneyMap) {
|
||||
final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId);
|
||||
if (userJourneySession == null || userJourneySession.mSessionId == INVALID_SESSION_ID) {
|
||||
Slogf.w(TAG, "UserLifecycleEvent " + event
|
||||
+ " received without an active userJourneySession.");
|
||||
return;
|
||||
}
|
||||
sessionId = userJourneySession.mSessionId;
|
||||
}
|
||||
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
|
||||
event, eventState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the {@link UserJourneySession} for a given {@link UserIdInt} and {@link UserJourney}.
|
||||
*/
|
||||
private void clearSessionId(@UserIdInt int userId, @UserJourney int 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a final event of the {@link UserJourneySession} and clear it.
|
||||
*/
|
||||
private void logAndClearSessionId(@UserIdInt int userId) {
|
||||
synchronized (mUserIdToUserJourneyMap) {
|
||||
final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId);
|
||||
if (userJourneySession != null) {
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED,
|
||||
userJourneySession.mSessionId, userId, USER_LIFECYCLE_EVENT_UNKNOWN,
|
||||
USER_LIFECYCLE_EVENT_STATE_NONE);
|
||||
}
|
||||
clearSessionId(userId);
|
||||
}
|
||||
}
|
||||
|
||||
private BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
|
||||
@@ -3471,23 +3321,6 @@ class UserController implements Handler.Callback {
|
||||
return mLastUserUnlockingUptime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -3562,6 +3395,10 @@ class UserController implements Handler.Callback {
|
||||
return new Handler(mService.mUiHandler.getLooper(), callback);
|
||||
}
|
||||
|
||||
protected UserJourneyLogger getUserJourneyLogger() {
|
||||
return getUserManager().getUserJourneyLogger();
|
||||
}
|
||||
|
||||
protected Context getContext() {
|
||||
return mService.mContext;
|
||||
}
|
||||
|
||||
552
services/core/java/com/android/server/pm/UserJourneyLogger.java
Normal file
552
services/core/java/com/android/server/pm/UserJourneyLogger.java
Normal file
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import static android.os.UserManager.USER_TYPE_FULL_DEMO;
|
||||
import static android.os.UserManager.USER_TYPE_FULL_GUEST;
|
||||
import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
|
||||
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
|
||||
import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
|
||||
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
|
||||
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
|
||||
import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
|
||||
|
||||
import static com.android.internal.util.FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* This class is logging User Lifecycle statsd events and synchronise User Lifecycle Journeys
|
||||
* by making sure all events are called in correct order and errors are reported in case of
|
||||
* unexpected journeys. This class also makes sure that all user sub-journeys are logged so
|
||||
* for example User Switch also log User Start Journey.
|
||||
*/
|
||||
public class UserJourneyLogger {
|
||||
|
||||
public static final int ERROR_CODE_INVALID_SESSION_ID = 0;
|
||||
public static final int ERROR_CODE_UNSPECIFIED = -1;
|
||||
/*
|
||||
* Possible reasons for ERROR_CODE_INCOMPLETE_OR_TIMEOUT to occur:
|
||||
* - 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.
|
||||
*/
|
||||
public static final int ERROR_CODE_INCOMPLETE_OR_TIMEOUT = 2;
|
||||
public static final int ERROR_CODE_ABORTED = 3;
|
||||
public static final int ERROR_CODE_NULL_USER_INFO = 4;
|
||||
public static final int ERROR_CODE_USER_ALREADY_AN_ADMIN = 5;
|
||||
public static final int ERROR_CODE_USER_IS_NOT_AN_ADMIN = 6;
|
||||
|
||||
@IntDef(prefix = {"ERROR_CODE"}, value = {
|
||||
ERROR_CODE_UNSPECIFIED,
|
||||
ERROR_CODE_INCOMPLETE_OR_TIMEOUT,
|
||||
ERROR_CODE_ABORTED,
|
||||
ERROR_CODE_NULL_USER_INFO,
|
||||
ERROR_CODE_USER_ALREADY_AN_ADMIN,
|
||||
ERROR_CODE_USER_IS_NOT_AN_ADMIN,
|
||||
ERROR_CODE_INVALID_SESSION_ID
|
||||
})
|
||||
public @interface UserJourneyErrorCode {
|
||||
}
|
||||
|
||||
// The various user journeys, defined in the UserLifecycleJourneyReported atom for statsd
|
||||
public static final int USER_JOURNEY_UNKNOWN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__UNKNOWN;
|
||||
public static final int USER_JOURNEY_USER_SWITCH_FG =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_FG;
|
||||
public static final int USER_JOURNEY_USER_SWITCH_UI =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_UI;
|
||||
public static final int USER_JOURNEY_USER_START =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START;
|
||||
public static final int USER_JOURNEY_USER_CREATE =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE;
|
||||
public static final int USER_JOURNEY_USER_STOP =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP;
|
||||
public static final int USER_JOURNEY_USER_REMOVE =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE;
|
||||
public static final int USER_JOURNEY_GRANT_ADMIN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN;
|
||||
public static final int USER_JOURNEY_REVOKE_ADMIN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN;
|
||||
|
||||
@IntDef(prefix = {"USER_JOURNEY"}, value = {
|
||||
USER_JOURNEY_UNKNOWN,
|
||||
USER_JOURNEY_USER_SWITCH_FG,
|
||||
USER_JOURNEY_USER_SWITCH_UI,
|
||||
USER_JOURNEY_USER_START,
|
||||
USER_JOURNEY_USER_STOP,
|
||||
USER_JOURNEY_USER_CREATE,
|
||||
USER_JOURNEY_USER_REMOVE,
|
||||
USER_JOURNEY_GRANT_ADMIN,
|
||||
USER_JOURNEY_REVOKE_ADMIN
|
||||
})
|
||||
public @interface UserJourney {
|
||||
}
|
||||
|
||||
|
||||
// The various user lifecycle events, defined in the UserLifecycleEventOccurred atom for statsd
|
||||
public static final int USER_LIFECYCLE_EVENT_UNKNOWN =
|
||||
USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN;
|
||||
public static final int USER_LIFECYCLE_EVENT_SWITCH_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__SWITCH_USER;
|
||||
public static final int USER_LIFECYCLE_EVENT_START_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__START_USER;
|
||||
public static final int USER_LIFECYCLE_EVENT_CREATE_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER;
|
||||
public static final int USER_LIFECYCLE_EVENT_REMOVE_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
|
||||
public static final int USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__USER_RUNNING_LOCKED;
|
||||
public static final int USER_LIFECYCLE_EVENT_UNLOCKING_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER;
|
||||
public static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER;
|
||||
public static final int USER_LIFECYCLE_EVENT_STOP_USER =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER;
|
||||
public static final int USER_LIFECYCLE_EVENT_GRANT_ADMIN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
|
||||
public static final int USER_LIFECYCLE_EVENT_REVOKE_ADMIN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
|
||||
|
||||
@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_REMOVE_USER,
|
||||
USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
|
||||
USER_LIFECYCLE_EVENT_UNLOCKING_USER,
|
||||
USER_LIFECYCLE_EVENT_UNLOCKED_USER,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
USER_LIFECYCLE_EVENT_GRANT_ADMIN,
|
||||
USER_LIFECYCLE_EVENT_REVOKE_ADMIN
|
||||
})
|
||||
public @interface UserLifecycleEvent {
|
||||
}
|
||||
|
||||
// User lifecycle event state, defined in the UserLifecycleEventOccurred atom for statsd
|
||||
public static final int EVENT_STATE_BEGIN =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN;
|
||||
public static final int EVENT_STATE_FINISH =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH;
|
||||
public static final int EVENT_STATE_NONE =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE;
|
||||
public static final int EVENT_STATE_CANCEL =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__CANCEL;
|
||||
public static final int EVENT_STATE_ERROR =
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR;
|
||||
|
||||
@IntDef(prefix = {"EVENT_STATE"}, value = {
|
||||
EVENT_STATE_BEGIN,
|
||||
EVENT_STATE_FINISH,
|
||||
EVENT_STATE_NONE,
|
||||
EVENT_STATE_CANCEL,
|
||||
EVENT_STATE_ERROR,
|
||||
})
|
||||
public @interface UserLifecycleEventState {
|
||||
}
|
||||
|
||||
private static final int USER_ID_KEY_MULTIPLICATION = 100;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
/**
|
||||
* {@link UserIdInt} and {@link UserJourney} to {@link UserJourneySession} mapping used for
|
||||
* statsd logging for the UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Returns event equivalent of given journey
|
||||
*/
|
||||
@UserLifecycleEvent
|
||||
private static int journeyToEvent(@UserJourney int journey) {
|
||||
switch (journey) {
|
||||
case USER_JOURNEY_USER_SWITCH_UI:
|
||||
case USER_JOURNEY_USER_SWITCH_FG:
|
||||
return USER_LIFECYCLE_EVENT_SWITCH_USER;
|
||||
case USER_JOURNEY_USER_START:
|
||||
return USER_LIFECYCLE_EVENT_START_USER;
|
||||
case USER_JOURNEY_USER_CREATE:
|
||||
return USER_LIFECYCLE_EVENT_CREATE_USER;
|
||||
case USER_JOURNEY_USER_STOP:
|
||||
return USER_LIFECYCLE_EVENT_STOP_USER;
|
||||
case USER_JOURNEY_USER_REMOVE:
|
||||
return USER_LIFECYCLE_EVENT_REMOVE_USER;
|
||||
case USER_JOURNEY_GRANT_ADMIN:
|
||||
return USER_LIFECYCLE_EVENT_GRANT_ADMIN;
|
||||
case USER_JOURNEY_REVOKE_ADMIN:
|
||||
return USER_LIFECYCLE_EVENT_REVOKE_ADMIN;
|
||||
default:
|
||||
return USER_LIFECYCLE_EVENT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to
|
||||
* the user type.
|
||||
* Changes to this method require changes in CTS file
|
||||
* com.android.cts.packagemanager.stats.device.UserInfoUtil
|
||||
* which is duplicate for CTS tests purposes.
|
||||
*/
|
||||
public static int getUserTypeForStatsd(@NonNull String userType) {
|
||||
switch (userType) {
|
||||
case USER_TYPE_FULL_SYSTEM:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SYSTEM;
|
||||
case USER_TYPE_FULL_SECONDARY:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY;
|
||||
case USER_TYPE_FULL_GUEST:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_GUEST;
|
||||
case USER_TYPE_FULL_DEMO:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_DEMO;
|
||||
case USER_TYPE_FULL_RESTRICTED:
|
||||
return FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_RESTRICTED;
|
||||
case USER_TYPE_PROFILE_MANAGED:
|
||||
return FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_MANAGED;
|
||||
case USER_TYPE_SYSTEM_HEADLESS:
|
||||
return FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS;
|
||||
case USER_TYPE_PROFILE_CLONE:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE;
|
||||
default:
|
||||
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map error code to the event finish state.
|
||||
*/
|
||||
@UserLifecycleEventState
|
||||
private static int errorToFinishState(@UserJourneyErrorCode int errorCode) {
|
||||
switch (errorCode) {
|
||||
case ERROR_CODE_ABORTED:
|
||||
return EVENT_STATE_CANCEL;
|
||||
case ERROR_CODE_UNSPECIFIED:
|
||||
return EVENT_STATE_FINISH;
|
||||
default:
|
||||
return EVENT_STATE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply logging USER_LIFECYCLE_JOURNEY_REPORTED if session exists.
|
||||
* If session does not exist then it logs ERROR_CODE_INVALID_SESSION_ID
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void logUserLifecycleJourneyReported(@Nullable UserJourneySession session,
|
||||
@UserJourney int journey, @UserIdInt int originalUserId, @UserIdInt int targetUserId,
|
||||
int userType, int userFlags, @UserJourneyErrorCode int errorCode) {
|
||||
if (session == null) {
|
||||
writeUserLifecycleJourneyReported(-1, journey, originalUserId, targetUserId,
|
||||
userType, userFlags, ERROR_CODE_INVALID_SESSION_ID);
|
||||
} else {
|
||||
writeUserLifecycleJourneyReported(
|
||||
session.mSessionId, journey, originalUserId, targetUserId, userType, userFlags,
|
||||
errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for spy testing
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void writeUserLifecycleJourneyReported(long sessionId, int journey, int originalUserId,
|
||||
int targetUserId, int userType, int userFlags, int errorCode) {
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED,
|
||||
sessionId, journey, originalUserId, targetUserId, userType, userFlags,
|
||||
errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply logging USER_LIFECYCLE_EVENT_OCCURRED if session exists.
|
||||
* If session does not exist then it logs ERROR_CODE_INVALID_SESSION_ID
|
||||
* and EVENT_STATE_ERROR
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void logUserLifecycleEventOccurred(UserJourneySession session,
|
||||
@UserIdInt int targetUserId, @UserLifecycleEvent int event,
|
||||
@UserLifecycleEventState int state, @UserJourneyErrorCode int errorCode) {
|
||||
if (session == null) {
|
||||
writeUserLifecycleEventOccurred(-1, targetUserId, event,
|
||||
EVENT_STATE_ERROR, ERROR_CODE_INVALID_SESSION_ID);
|
||||
} else {
|
||||
writeUserLifecycleEventOccurred(session.mSessionId, targetUserId, event, state,
|
||||
errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for spy testing
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void writeUserLifecycleEventOccurred(long sessionId, int userId, int event, int state,
|
||||
int errorCode) {
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED,
|
||||
sessionId, userId, event, state, errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* statsd helper method for logging the given event for the UserLifecycleEventOccurred statsd
|
||||
* atom. It finds the user journey session for target user id and logs it as that journey.
|
||||
*/
|
||||
public void logUserLifecycleEvent(@UserIdInt int userId, @UserLifecycleEvent int event,
|
||||
@UserLifecycleEventState int eventState) {
|
||||
final UserJourneySession userJourneySession = findUserJourneySession(userId);
|
||||
logUserLifecycleEventOccurred(userJourneySession, userId,
|
||||
event, eventState, UserJourneyLogger.ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first user session from mUserIdToUserJourneyMap for given user id,
|
||||
* or null if user id was not found in mUserIdToUserJourneyMap.
|
||||
*/
|
||||
private @Nullable UserJourneySession findUserJourneySession(@UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
final int keyMapSize = mUserIdToUserJourneyMap.size();
|
||||
for (int i = 0; i < keyMapSize; i++) {
|
||||
int key = mUserIdToUserJourneyMap.keyAt(i);
|
||||
if (key / USER_ID_KEY_MULTIPLICATION == userId) {
|
||||
return mUserIdToUserJourneyMap.get(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique id for user and journey. For example if user id = 11 and journey = 7
|
||||
* then unique key = 11 * 100 + 7 = 1107
|
||||
*/
|
||||
private int getUserJourneyKey(@UserIdInt int targetUserId, @UserJourney int journey) {
|
||||
// We leave 99 for user journeys ids.
|
||||
return (targetUserId * USER_ID_KEY_MULTIPLICATION) + journey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special use case when user journey incomplete or timeout and current user is unclear
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public UserJourneySession finishAndClearIncompleteUserJourney(@UserIdInt int targetUserId,
|
||||
@UserJourney int journey) {
|
||||
synchronized (mLock) {
|
||||
final int key = getUserJourneyKey(targetUserId, journey);
|
||||
final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key);
|
||||
if (userJourneySession != null) {
|
||||
logUserLifecycleEventOccurred(
|
||||
userJourneySession,
|
||||
targetUserId,
|
||||
journeyToEvent(userJourneySession.mJourney),
|
||||
EVENT_STATE_ERROR,
|
||||
UserJourneyLogger.ERROR_CODE_INCOMPLETE_OR_TIMEOUT);
|
||||
|
||||
logUserLifecycleJourneyReported(
|
||||
userJourneySession,
|
||||
journey,
|
||||
/* originalUserId= */ -1,
|
||||
targetUserId,
|
||||
getUserTypeForStatsd(""), -1,
|
||||
ERROR_CODE_INCOMPLETE_OR_TIMEOUT);
|
||||
mUserIdToUserJourneyMap.remove(key);
|
||||
|
||||
return userJourneySession;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log user journey event and report finishing without error
|
||||
*/
|
||||
public UserJourneySession logUserJourneyFinish(@UserIdInt int originalUserId,
|
||||
UserInfo targetUser, @UserJourney int journey) {
|
||||
return logUserJourneyFinishWithError(originalUserId, targetUser, journey,
|
||||
ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case when it is unknown which user switch journey was used and checking both
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public UserJourneySession logUserSwitchJourneyFinish(@UserIdInt int originalUserId,
|
||||
UserInfo targetUser) {
|
||||
synchronized (mLock) {
|
||||
final int key_fg = getUserJourneyKey(targetUser.id, USER_JOURNEY_USER_SWITCH_FG);
|
||||
final int key_ui = getUserJourneyKey(targetUser.id, USER_JOURNEY_USER_SWITCH_UI);
|
||||
|
||||
if (mUserIdToUserJourneyMap.contains(key_fg)) {
|
||||
return logUserJourneyFinish(originalUserId, targetUser,
|
||||
USER_JOURNEY_USER_SWITCH_FG);
|
||||
}
|
||||
|
||||
if (mUserIdToUserJourneyMap.contains(key_ui)) {
|
||||
return logUserJourneyFinish(originalUserId, targetUser,
|
||||
USER_JOURNEY_USER_SWITCH_UI);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log user journey event and report finishing with error
|
||||
*/
|
||||
public UserJourneySession logUserJourneyFinishWithError(@UserIdInt int originalUserId,
|
||||
UserInfo targetUser, @UserJourney int journey, @UserJourneyErrorCode int errorCode) {
|
||||
synchronized (mLock) {
|
||||
final int state = errorToFinishState(errorCode);
|
||||
final int key = getUserJourneyKey(targetUser.id, journey);
|
||||
final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key);
|
||||
if (userJourneySession != null) {
|
||||
logUserLifecycleEventOccurred(
|
||||
userJourneySession, targetUser.id,
|
||||
journeyToEvent(userJourneySession.mJourney),
|
||||
state,
|
||||
errorCode);
|
||||
|
||||
logUserLifecycleJourneyReported(
|
||||
userJourneySession,
|
||||
journey, originalUserId, targetUser.id,
|
||||
getUserTypeForStatsd(targetUser.userType),
|
||||
targetUser.flags,
|
||||
errorCode);
|
||||
mUserIdToUserJourneyMap.remove(key);
|
||||
|
||||
return userJourneySession;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log event and report finish when user is null. This is edge case when UserInfo
|
||||
* can not be passed because it is null, therefore all information are passed as arguments.
|
||||
*/
|
||||
public UserJourneySession logNullUserJourneyError(@UserJourney int journey,
|
||||
@UserIdInt int currentUserId, @UserIdInt int targetUserId, String targetUserType,
|
||||
int targetUserFlags) {
|
||||
synchronized (mLock) {
|
||||
final int key = getUserJourneyKey(targetUserId, journey);
|
||||
final UserJourneySession session = mUserIdToUserJourneyMap.get(key);
|
||||
|
||||
logUserLifecycleEventOccurred(
|
||||
session, targetUserId, journeyToEvent(journey),
|
||||
EVENT_STATE_ERROR,
|
||||
ERROR_CODE_NULL_USER_INFO);
|
||||
|
||||
logUserLifecycleJourneyReported(
|
||||
session, journey, currentUserId, targetUserId,
|
||||
getUserTypeForStatsd(targetUserType), targetUserFlags,
|
||||
ERROR_CODE_NULL_USER_INFO);
|
||||
|
||||
mUserIdToUserJourneyMap.remove(key);
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log for user creation finish event and report. This is edge case when target user id is
|
||||
* different in begin event and finish event as it is unknown what is user id
|
||||
* until it has been created.
|
||||
*/
|
||||
public UserJourneySession logUserCreateJourneyFinish(@UserIdInt int originalUserId,
|
||||
UserInfo targetUser) {
|
||||
synchronized (mLock) {
|
||||
// we do not know user id until we create new user which is why we use -1
|
||||
// as user id to create and find session, but we log correct id.
|
||||
final int key = getUserJourneyKey(-1, USER_JOURNEY_USER_CREATE);
|
||||
final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key);
|
||||
if (userJourneySession != null) {
|
||||
logUserLifecycleEventOccurred(
|
||||
userJourneySession, targetUser.id,
|
||||
USER_LIFECYCLE_EVENT_CREATE_USER,
|
||||
EVENT_STATE_FINISH,
|
||||
ERROR_CODE_UNSPECIFIED);
|
||||
|
||||
logUserLifecycleJourneyReported(
|
||||
userJourneySession,
|
||||
USER_JOURNEY_USER_CREATE, originalUserId, targetUser.id,
|
||||
getUserTypeForStatsd(targetUser.userType),
|
||||
targetUser.flags,
|
||||
ERROR_CODE_UNSPECIFIED);
|
||||
mUserIdToUserJourneyMap.remove(key);
|
||||
|
||||
return userJourneySession;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new UserJourneySession to mUserIdToUserJourneyMap and log UserJourneyEvent Begin state
|
||||
*/
|
||||
public UserJourneySession logUserJourneyBegin(@UserIdInt int targetId,
|
||||
@UserJourney int journey) {
|
||||
final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
|
||||
synchronized (mLock) {
|
||||
final int key = getUserJourneyKey(targetId, journey);
|
||||
final UserJourneySession userJourneySession =
|
||||
new UserJourneySession(newSessionId, journey);
|
||||
mUserIdToUserJourneyMap.append(key, userJourneySession);
|
||||
|
||||
logUserLifecycleEventOccurred(
|
||||
userJourneySession, targetId,
|
||||
journeyToEvent(userJourneySession.mJourney),
|
||||
EVENT_STATE_BEGIN,
|
||||
ERROR_CODE_UNSPECIFIED);
|
||||
|
||||
return userJourneySession;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static class UserJourneySession {
|
||||
public final long mSessionId;
|
||||
@UserJourney
|
||||
public final int mJourney;
|
||||
|
||||
@VisibleForTesting
|
||||
public UserJourneySession(long sessionId, @UserJourney int journey) {
|
||||
mJourney = journey;
|
||||
mSessionId = sessionId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,15 @@ import static android.os.UserManager.DISALLOW_USER_SWITCH;
|
||||
import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
|
||||
import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
|
||||
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_ALREADY_AN_ADMIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_IS_NOT_AN_ADMIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_CREATE;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_REMOVE;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
@@ -162,7 +171,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -519,6 +527,8 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
@GuardedBy("mUserLifecycleListeners")
|
||||
private final ArrayList<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>();
|
||||
|
||||
private final UserJourneyLogger mUserJourneyLogger = new UserJourneyLogger();
|
||||
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
|
||||
private final String ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK =
|
||||
@@ -1580,45 +1590,56 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
@Override
|
||||
public void setUserAdmin(@UserIdInt int userId) {
|
||||
checkManageUserAndAcrossUsersFullPermission("set user admin");
|
||||
final long sessionId = logGrantAdminJourneyBegin(userId);
|
||||
mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_GRANT_ADMIN);
|
||||
UserInfo info;
|
||||
synchronized (mPackagesLock) {
|
||||
synchronized (mUsersLock) {
|
||||
info = getUserInfoLU(userId);
|
||||
}
|
||||
if (info == null || info.isAdmin()) {
|
||||
// Exit if no user found with that id, or the user is already an Admin.
|
||||
logUserJourneyError(sessionId,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
|
||||
userId);
|
||||
if (info == null) {
|
||||
// Exit if no user found with that id,
|
||||
mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN,
|
||||
getCurrentUserId(), userId, /* userType */ "", /* userFlags */ -1);
|
||||
return;
|
||||
} else if (info.isAdmin()) {
|
||||
// Exit if the user is already an Admin.
|
||||
mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), info,
|
||||
USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_USER_ALREADY_AN_ADMIN);
|
||||
return;
|
||||
}
|
||||
info.flags ^= UserInfo.FLAG_ADMIN;
|
||||
writeUserLP(getUserDataLU(info.id));
|
||||
}
|
||||
logGrantAdminJourneyFinish(sessionId, userId, info.userType, info.flags);
|
||||
mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), info,
|
||||
USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeUserAdmin(@UserIdInt int userId) {
|
||||
checkManageUserAndAcrossUsersFullPermission("revoke admin privileges");
|
||||
final long sessionId = logRevokeAdminJourneyBegin(userId);
|
||||
mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_REVOKE_ADMIN);
|
||||
UserData user;
|
||||
synchronized (mPackagesLock) {
|
||||
synchronized (mUsersLock) {
|
||||
user = getUserDataLU(userId);
|
||||
if (user == null || !user.info.isAdmin()) {
|
||||
// Exit if no user found with that id, or the user is not an Admin.
|
||||
logUserJourneyError(sessionId, FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
|
||||
userId);
|
||||
if (user == null) {
|
||||
// Exit if no user found with that id
|
||||
mUserJourneyLogger.logNullUserJourneyError(
|
||||
USER_JOURNEY_REVOKE_ADMIN,
|
||||
getCurrentUserId(), userId, "", -1);
|
||||
return;
|
||||
} else if (!user.info.isAdmin()) {
|
||||
// Exit if no user is not an Admin.
|
||||
mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), user.info,
|
||||
USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_USER_IS_NOT_AN_ADMIN);
|
||||
return;
|
||||
}
|
||||
user.info.flags ^= UserInfo.FLAG_ADMIN;
|
||||
writeUserLP(user);
|
||||
}
|
||||
}
|
||||
logRevokeAdminJourneyFinish(sessionId, userId, user.info.userType, user.info.flags);
|
||||
mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), user.info,
|
||||
USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4700,16 +4721,20 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
final int noneUserId = -1;
|
||||
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
|
||||
t.traceBegin("createUser-" + flags);
|
||||
final long sessionId = logUserCreateJourneyBegin(noneUserId);
|
||||
mUserJourneyLogger.logUserJourneyBegin(noneUserId, USER_JOURNEY_USER_CREATE);
|
||||
UserInfo newUser = null;
|
||||
try {
|
||||
newUser = createUserInternalUncheckedNoTracing(name, userType, flags, parentId,
|
||||
preCreate, disallowedPackages, t, token);
|
||||
return newUser;
|
||||
} finally {
|
||||
logUserCreateJourneyFinish(sessionId,
|
||||
newUser != null ? newUser.id : noneUserId, userType, flags,
|
||||
newUser != null);
|
||||
if (newUser != null) {
|
||||
mUserJourneyLogger.logUserCreateJourneyFinish(getCurrentUserId(), newUser);
|
||||
} else {
|
||||
mUserJourneyLogger.logNullUserJourneyError(
|
||||
USER_JOURNEY_USER_CREATE,
|
||||
getCurrentUserId(), noneUserId, userType, flags);
|
||||
}
|
||||
t.traceEnd();
|
||||
}
|
||||
}
|
||||
@@ -5198,137 +5223,6 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
&& !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED);
|
||||
}
|
||||
|
||||
private long logUserCreateJourneyBegin(@UserIdInt int userId) {
|
||||
return logUserJourneyBegin(
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
|
||||
userId);
|
||||
}
|
||||
|
||||
private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
|
||||
@UserInfoFlag int flags, boolean finish) {
|
||||
logUserJourneyFinish(sessionId,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
|
||||
userId, userType, flags, finish);
|
||||
}
|
||||
|
||||
private long logUserRemoveJourneyBegin(@UserIdInt int userId) {
|
||||
return logUserJourneyBegin(
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE,
|
||||
userId);
|
||||
}
|
||||
|
||||
private void logUserRemoveJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
|
||||
@UserInfoFlag int flags, boolean finish) {
|
||||
logUserJourneyFinish(sessionId,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE,
|
||||
userId, userType, flags, finish);
|
||||
}
|
||||
|
||||
private long logGrantAdminJourneyBegin(@UserIdInt int userId) {
|
||||
return logUserJourneyBegin(
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
|
||||
userId);
|
||||
}
|
||||
|
||||
private void logGrantAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
|
||||
@UserInfoFlag int flags) {
|
||||
logUserJourneyFinish(sessionId,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
|
||||
userId, userType, flags, true);
|
||||
}
|
||||
|
||||
private long logRevokeAdminJourneyBegin(@UserIdInt int userId) {
|
||||
return logUserJourneyBegin(
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
|
||||
userId);
|
||||
}
|
||||
|
||||
private void logRevokeAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
|
||||
@UserInfoFlag int flags) {
|
||||
logUserJourneyFinish(sessionId,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
|
||||
userId, userType, flags, true);
|
||||
}
|
||||
|
||||
private void logUserJourneyFinish(long sessionId, int journey, @UserIdInt int userId,
|
||||
String userType, @UserInfoFlag int flags, boolean finish) {
|
||||
|
||||
// log the journey atom with the user metadata
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
|
||||
journey, /* origin_user= */ getCurrentUserId(), userId,
|
||||
UserManager.getUserTypeForStatsd(userType), flags);
|
||||
|
||||
int event;
|
||||
switch (journey) {
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER;
|
||||
break;
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
|
||||
break;
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
|
||||
break;
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Journey " + journey + " not expected.");
|
||||
}
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
|
||||
event,
|
||||
finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH
|
||||
: FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
|
||||
}
|
||||
|
||||
private long logUserJourneyBegin(int journey, @UserIdInt int userId) {
|
||||
final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
|
||||
|
||||
// log the event atom to indicate the event start
|
||||
int event;
|
||||
switch (journey) {
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER;
|
||||
break;
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
|
||||
break;
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
|
||||
break;
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Journey " + journey + " not expected.");
|
||||
}
|
||||
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
|
||||
event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
private void logUserJourneyError(long sessionId, int journey, @UserIdInt int userId) {
|
||||
|
||||
// log the journey atom with the user metadata
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
|
||||
journey, /* origin_user= */ getCurrentUserId(), userId);
|
||||
|
||||
int event;
|
||||
switch (journey) {
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
|
||||
break;
|
||||
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
|
||||
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Journey " + journey + " not expected.");
|
||||
}
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
|
||||
event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR);
|
||||
}
|
||||
|
||||
/** Register callbacks for statsd pulled atoms. */
|
||||
private void registerStatsCallbacks() {
|
||||
final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
|
||||
@@ -5352,7 +5246,8 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
if (size > 1) {
|
||||
for (int idx = 0; idx < size; idx++) {
|
||||
final UserInfo user = users.get(idx);
|
||||
final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
|
||||
final int userTypeStandard = mUserJourneyLogger
|
||||
.getUserTypeForStatsd(user.userType);
|
||||
final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
|
||||
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
|
||||
?
|
||||
@@ -5635,7 +5530,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
writeUserLP(userData);
|
||||
}
|
||||
|
||||
final long sessionId = logUserRemoveJourneyBegin(userId);
|
||||
mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_USER_REMOVE);
|
||||
|
||||
try {
|
||||
mAppOpsService.removeUser(userId);
|
||||
@@ -5657,13 +5552,17 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
@Override
|
||||
public void userStopped(int userIdParam) {
|
||||
finishRemoveUser(userIdParam);
|
||||
logUserRemoveJourneyFinish(sessionId, userIdParam,
|
||||
userData.info.userType, userData.info.flags, true);
|
||||
int originUserId = UserManagerService.this.getCurrentUserId();
|
||||
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
|
||||
userData.info, USER_JOURNEY_USER_REMOVE,
|
||||
ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
@Override
|
||||
public void userStopAborted(int userIdParam) {
|
||||
logUserRemoveJourneyFinish(sessionId, userIdParam,
|
||||
userData.info.userType, userData.info.flags, false);
|
||||
int originUserId = UserManagerService.this.getCurrentUserId();
|
||||
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
|
||||
userData.info, USER_JOURNEY_USER_REMOVE,
|
||||
ERROR_CODE_ABORTED);
|
||||
}
|
||||
});
|
||||
} catch (RemoteException e) {
|
||||
@@ -7297,9 +7196,9 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
final UserInfo userInfo = getUserInfo(userIds[i]);
|
||||
if (userInfo == null) {
|
||||
// Not possible because the input user ids should all be valid
|
||||
userTypes[i] = UserManager.getUserTypeForStatsd("");
|
||||
userTypes[i] = mUserJourneyLogger.getUserTypeForStatsd("");
|
||||
} else {
|
||||
userTypes[i] = UserManager.getUserTypeForStatsd(userInfo.userType);
|
||||
userTypes[i] = mUserJourneyLogger.getUserTypeForStatsd(userInfo.userType);
|
||||
}
|
||||
}
|
||||
return userTypes;
|
||||
@@ -7536,4 +7435,11 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
.getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of {@link com.android.server.pm.UserJourneyLogger}.
|
||||
*/
|
||||
public UserJourneyLogger getUserJourneyLogger() {
|
||||
return mUserJourneyLogger;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.am.UserState.KeyEvictedCallback;
|
||||
import com.android.server.pm.UserJourneyLogger;
|
||||
import com.android.server.pm.UserManagerInternal;
|
||||
import com.android.server.pm.UserManagerService;
|
||||
import com.android.server.wm.WindowManagerService;
|
||||
@@ -1074,6 +1075,8 @@ public class UserControllerTest {
|
||||
private final KeyguardManager mKeyguardManagerMock;
|
||||
private final LockPatternUtils mLockPatternUtilsMock;
|
||||
|
||||
private final UserJourneyLogger mUserJourneyLoggerMock;
|
||||
|
||||
private final Context mCtx;
|
||||
|
||||
TestInjector(Context ctx) {
|
||||
@@ -1090,6 +1093,7 @@ public class UserControllerTest {
|
||||
mKeyguardManagerMock = mock(KeyguardManager.class);
|
||||
when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
|
||||
mLockPatternUtilsMock = mock(LockPatternUtils.class);
|
||||
mUserJourneyLoggerMock = mock(UserJourneyLogger.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1220,6 +1224,11 @@ public class UserControllerTest {
|
||||
void onSystemUserVisibilityChanged(boolean visible) {
|
||||
Log.i(TAG, "onSystemUserVisibilityChanged(" + visible + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserJourneyLogger getUserJourneyLogger() {
|
||||
return mUserJourneyLoggerMock;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestHandler extends Handler {
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INCOMPLETE_OR_TIMEOUT;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_NULL_USER_INFO;
|
||||
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_BEGIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_CANCEL;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_ERROR;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_FINISH;
|
||||
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_NONE;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_CREATE;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_REMOVE;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_STOP;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_CREATE_USER;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_REMOVE_USER;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_REVOKE_ADMIN;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_START_USER;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_STOP_USER;
|
||||
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.pm.UserInfo;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class UserJourneyLoggerTest {
|
||||
|
||||
public static final int FULL_USER_ADMIN_FLAG = 0x00000402;
|
||||
private UserJourneyLogger mUserJourneyLogger;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mUserJourneyLogger = spy(new UserJourneyLogger());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserStartLifecycleJourneyReported() {
|
||||
final UserLifecycleJourneyReportedCaptor report1 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = new UserJourneyLogger
|
||||
.UserJourneySession(10, USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureLogAndAssert(mUserJourneyLogger, session,
|
||||
USER_JOURNEY_USER_START, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, 1,
|
||||
ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUserLifecycleEventOccurred() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = new UserJourneyLogger
|
||||
.UserJourneySession(10, USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureLogAndAssert(mUserJourneyLogger, session, 0,
|
||||
USER_LIFECYCLE_EVENT_START_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogUserLifecycleEvent() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_START_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
mUserJourneyLogger.logUserLifecycleEvent(10, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
|
||||
EVENT_STATE_NONE);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
|
||||
EVENT_STATE_NONE, ERROR_CODE_UNSPECIFIED, 2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(-1, USER_JOURNEY_USER_CREATE);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, -1,
|
||||
USER_LIFECYCLE_EVENT_CREATE_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
UserInfo targetUser = new UserInfo(10, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserCreateJourneyFinish(0, targetUser);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_CREATE_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_USER_CREATE, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_USER_REMOVE);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_REMOVE_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(10, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(0, targetUser,
|
||||
USER_JOURNEY_USER_REMOVE);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_REMOVE_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_USER_REMOVE, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(10, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(0, targetUser,
|
||||
USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_USER_START, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_USER_STOP);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(10, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(0, targetUser,
|
||||
USER_JOURNEY_USER_STOP);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_USER_STOP, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbortStopUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_USER_STOP);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(10, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
|
||||
mUserJourneyLogger.logUserJourneyFinishWithError(-1, targetUser,
|
||||
USER_JOURNEY_USER_STOP, ERROR_CODE_ABORTED);
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
EVENT_STATE_CANCEL, ERROR_CODE_ABORTED, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_USER_STOP, -1, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_ABORTED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncompleteStopUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_USER_STOP);
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
mUserJourneyLogger.finishAndClearIncompleteUserJourney(10, USER_JOURNEY_USER_STOP);
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
EVENT_STATE_ERROR,
|
||||
ERROR_CODE_INCOMPLETE_OR_TIMEOUT, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_USER_STOP, -1, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN,
|
||||
-1, // information about user are incomplete
|
||||
ERROR_CODE_INCOMPLETE_OR_TIMEOUT, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrantAdminUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_GRANT_ADMIN);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(10, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(0, targetUser,
|
||||
USER_JOURNEY_GRANT_ADMIN);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_GRANT_ADMIN, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullUserErrorGrantAdminUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
|
||||
UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_GRANT_ADMIN);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN,
|
||||
0, 10, "", -1);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN,
|
||||
EVENT_STATE_ERROR, ERROR_CODE_NULL_USER_INFO, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
USER_JOURNEY_GRANT_ADMIN, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN,
|
||||
-1, ERROR_CODE_NULL_USER_INFO, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevokeAdminUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_REVOKE_ADMIN,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(10, "test target user", UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(0, targetUser,
|
||||
UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_REVOKE_ADMIN,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN, 0, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000400, ERROR_CODE_UNSPECIFIED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwitchFGUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger
|
||||
.logUserJourneyBegin(11, USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(11, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(10, targetUser,
|
||||
USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 3);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId,
|
||||
USER_JOURNEY_USER_START, 10, 11,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 4);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG, 10, 11,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSwitchUIUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger
|
||||
.logUserJourneyBegin(11, USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
UserInfo targetUser = new UserInfo(11, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(10, targetUser,
|
||||
USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 3);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId,
|
||||
USER_JOURNEY_USER_START, 10, 11,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
EVENT_STATE_FINISH,
|
||||
ERROR_CODE_UNSPECIFIED, 4);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI, 10, 11,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSwitchWithStopUIUserJourney() {
|
||||
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
|
||||
|
||||
// BEGIN USER SWITCH
|
||||
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
|
||||
.logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
// BEGIN USER STOP
|
||||
final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger
|
||||
.logUserJourneyBegin(10, USER_JOURNEY_USER_STOP);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
// BEGIN USER START
|
||||
UserJourneyLogger.UserJourneySession session3 = mUserJourneyLogger
|
||||
.logUserJourneyBegin(11, USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session3.mSessionId, 11,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 3);
|
||||
|
||||
|
||||
// FINISH USER STOP
|
||||
final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
|
||||
final UserInfo targetUser = new UserInfo(10, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(-1, targetUser,
|
||||
USER_JOURNEY_USER_STOP);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 10,
|
||||
USER_LIFECYCLE_EVENT_STOP_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 4);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId,
|
||||
USER_JOURNEY_USER_STOP, -1, 10,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
FULL_USER_ADMIN_FLAG, ERROR_CODE_UNSPECIFIED, 1);
|
||||
|
||||
// FINISH USER START
|
||||
final UserInfo targetUser2 = new UserInfo(11, "test target user",
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL);
|
||||
mUserJourneyLogger.logUserJourneyFinish(10, targetUser2,
|
||||
USER_JOURNEY_USER_START);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session3.mSessionId, 11,
|
||||
USER_LIFECYCLE_EVENT_START_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 5);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session3.mSessionId,
|
||||
USER_JOURNEY_USER_START, 10, 11,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
FULL_USER_ADMIN_FLAG, ERROR_CODE_UNSPECIFIED, 2);
|
||||
|
||||
|
||||
// FINISH USER SWITCH
|
||||
mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser2);
|
||||
|
||||
report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11,
|
||||
UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER,
|
||||
EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 6);
|
||||
|
||||
report2.captureAndAssert(mUserJourneyLogger, session.mSessionId,
|
||||
UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI, 10, 11,
|
||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY,
|
||||
0x00000402, ERROR_CODE_UNSPECIFIED, 3);
|
||||
}
|
||||
|
||||
static class UserLifecycleJourneyReportedCaptor {
|
||||
ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class);
|
||||
ArgumentCaptor<Integer> mJourney = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mOriginalUserId = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mTargetUserId = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mUserType = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mUserFlags = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class);
|
||||
|
||||
public void captureAndAssert(UserJourneyLogger mUserJourneyLogger,
|
||||
long sessionId, int journey, int originalUserId,
|
||||
int targetUserId, int userType, int userFlags, int errorCode, int times) {
|
||||
verify(mUserJourneyLogger, times(times))
|
||||
.writeUserLifecycleJourneyReported(mSessionId.capture(),
|
||||
mJourney.capture(),
|
||||
mOriginalUserId.capture(),
|
||||
mTargetUserId.capture(),
|
||||
mUserType.capture(),
|
||||
mUserFlags.capture(),
|
||||
mErrorCode.capture());
|
||||
|
||||
assertThat(mSessionId.getValue()).isEqualTo(sessionId);
|
||||
assertThat(mJourney.getValue()).isEqualTo(journey);
|
||||
assertThat(mOriginalUserId.getValue()).isEqualTo(originalUserId);
|
||||
assertThat(mTargetUserId.getValue()).isEqualTo(targetUserId);
|
||||
assertThat(mUserType.getValue()).isEqualTo(userType);
|
||||
assertThat(mUserFlags.getValue()).isEqualTo(userFlags);
|
||||
assertThat(mErrorCode.getValue()).isEqualTo(errorCode);
|
||||
}
|
||||
|
||||
|
||||
public void captureLogAndAssert(UserJourneyLogger mUserJourneyLogger,
|
||||
UserJourneyLogger.UserJourneySession session, int journey, int originalUserId,
|
||||
int targetUserId, int userType, int userFlags, int errorCode) {
|
||||
mUserJourneyLogger.logUserLifecycleJourneyReported(session, journey, originalUserId,
|
||||
targetUserId, userType, userFlags, errorCode);
|
||||
|
||||
captureAndAssert(mUserJourneyLogger, session.mSessionId, journey, originalUserId,
|
||||
targetUserId, userType, userFlags, errorCode, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class UserLifecycleEventOccurredCaptor {
|
||||
ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class);
|
||||
ArgumentCaptor<Integer> mTargetUserId = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mEvent = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mStste = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class);
|
||||
|
||||
|
||||
public void captureAndAssert(UserJourneyLogger mUserJourneyLogger,
|
||||
long sessionId, int targetUserId, int event, int state, int errorCode, int times) {
|
||||
verify(mUserJourneyLogger, times(times))
|
||||
.writeUserLifecycleEventOccurred(mSessionId.capture(),
|
||||
mTargetUserId.capture(),
|
||||
mEvent.capture(),
|
||||
mStste.capture(),
|
||||
mErrorCode.capture());
|
||||
|
||||
assertThat(mSessionId.getValue()).isEqualTo(sessionId);
|
||||
assertThat(mTargetUserId.getValue()).isEqualTo(targetUserId);
|
||||
assertThat(mEvent.getValue()).isEqualTo(event);
|
||||
assertThat(mStste.getValue()).isEqualTo(state);
|
||||
assertThat(mErrorCode.getValue()).isEqualTo(errorCode);
|
||||
}
|
||||
|
||||
|
||||
public void captureLogAndAssert(UserJourneyLogger mUserJourneyLogger,
|
||||
UserJourneyLogger.UserJourneySession session, int targetUserId, int event,
|
||||
int state, int errorCode) {
|
||||
mUserJourneyLogger.logUserLifecycleEventOccurred(session, targetUserId, event,
|
||||
state, errorCode);
|
||||
|
||||
captureAndAssert(mUserJourneyLogger, session.mSessionId, targetUserId, event,
|
||||
state, errorCode, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user