diff options
3 files changed, 96 insertions, 9 deletions
diff --git a/services/core/java/com/android/server/pm/UserJourneyLogger.java b/services/core/java/com/android/server/pm/UserJourneyLogger.java index f48a1669c259..895edce093b5 100644 --- a/services/core/java/com/android/server/pm/UserJourneyLogger.java +++ b/services/core/java/com/android/server/pm/UserJourneyLogger.java @@ -99,6 +99,8 @@ public class UserJourneyLogger { FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN; public static final int USER_JOURNEY_REVOKE_ADMIN = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN; + public static final int USER_JOURNEY_USER_LIFECYCLE = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_LIFECYCLE; @IntDef(prefix = {"USER_JOURNEY"}, value = { USER_JOURNEY_UNKNOWN, @@ -109,7 +111,8 @@ public class UserJourneyLogger { USER_JOURNEY_USER_CREATE, USER_JOURNEY_USER_REMOVE, USER_JOURNEY_GRANT_ADMIN, - USER_JOURNEY_REVOKE_ADMIN + USER_JOURNEY_REVOKE_ADMIN, + USER_JOURNEY_USER_LIFECYCLE }) public @interface UserJourney { } @@ -272,11 +275,12 @@ public class UserJourneyLogger { int userType, int userFlags, @UserJourneyErrorCode int errorCode) { if (session == null) { writeUserLifecycleJourneyReported(-1, journey, originalUserId, targetUserId, - userType, userFlags, ERROR_CODE_INVALID_SESSION_ID); + userType, userFlags, ERROR_CODE_INVALID_SESSION_ID, -1); } else { + final long elapsedTime = System.currentTimeMillis() - session.mStartTimeInMills; writeUserLifecycleJourneyReported( session.mSessionId, journey, originalUserId, targetUserId, userType, userFlags, - errorCode); + errorCode, elapsedTime); } } @@ -285,10 +289,10 @@ public class UserJourneyLogger { */ @VisibleForTesting public void writeUserLifecycleJourneyReported(long sessionId, int journey, int originalUserId, - int targetUserId, int userType, int userFlags, int errorCode) { + int targetUserId, int userType, int userFlags, int errorCode, long elapsedTime) { FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, journey, originalUserId, targetUserId, userType, userFlags, - errorCode); + errorCode, elapsedTime); } /** @@ -452,6 +456,29 @@ public class UserJourneyLogger { } /** + * Log user journey event and report finishing with error + */ + public UserJourneySession logDelayedUserJourneyFinishWithError(@UserIdInt int originalUserId, + UserInfo targetUser, @UserJourney int journey, @UserJourneyErrorCode int errorCode) { + synchronized (mLock) { + final int key = getUserJourneyKey(targetUser.id, journey); + final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key); + if (userJourneySession != null) { + 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. */ @@ -533,6 +560,23 @@ public class UserJourneyLogger { } /** + * This keeps the start time when finishing extensively long journey was began. + * For instance full user lifecycle ( from creation to deletion )when user is about to delete + * we need to get user creation time before it was deleted. + */ + public UserJourneySession startSessionForDelayedJourney(@UserIdInt int targetId, + @UserJourney int journey, long startTime) { + 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, startTime); + mUserIdToUserJourneyMap.append(key, userJourneySession); + 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 @@ -542,11 +586,19 @@ public class UserJourneyLogger { public final long mSessionId; @UserJourney public final int mJourney; + public long mStartTimeInMills; @VisibleForTesting public UserJourneySession(long sessionId, @UserJourney int journey) { mJourney = journey; mSessionId = sessionId; + mStartTimeInMills = System.currentTimeMillis(); + } + @VisibleForTesting + public UserJourneySession(long sessionId, @UserJourney int journey, long startTimeInMills) { + mJourney = journey; + mSessionId = sessionId; + mStartTimeInMills = startTimeInMills; } } -} +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 4ef68d8f5aaf..4e043aae4f09 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -30,6 +30,7 @@ import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_IS_NOT_AN_ 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_LIFECYCLE; import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_REMOVE; import android.Manifest; @@ -3332,7 +3333,7 @@ public class UserManagerService extends IUserManager.Stub { } /** - * Enforces that only the system UID or root's UID or apps that have the + * Enforces that only the system UID or root's UID or apps that have the * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS} * can make certain calls to the UserManager. @@ -5535,6 +5536,8 @@ public class UserManagerService extends IUserManager.Stub { } mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_USER_REMOVE); + mUserJourneyLogger.startSessionForDelayedJourney(userId, + USER_JOURNEY_USER_LIFECYCLE, userData.info.creationTime); try { mAppOpsService.removeUser(userId); @@ -5560,6 +5563,10 @@ public class UserManagerService extends IUserManager.Stub { mUserJourneyLogger.logUserJourneyFinishWithError(originUserId, userData.info, USER_JOURNEY_USER_REMOVE, ERROR_CODE_UNSPECIFIED); + mUserJourneyLogger + .logDelayedUserJourneyFinishWithError(originUserId, + userData.info, USER_JOURNEY_USER_LIFECYCLE, + ERROR_CODE_UNSPECIFIED); } @Override public void userStopAborted(int userIdParam) { @@ -5567,6 +5574,10 @@ public class UserManagerService extends IUserManager.Stub { mUserJourneyLogger.logUserJourneyFinishWithError(originUserId, userData.info, USER_JOURNEY_USER_REMOVE, ERROR_CODE_ABORTED); + mUserJourneyLogger + .logDelayedUserJourneyFinishWithError(originUserId, + userData.info, USER_JOURNEY_USER_LIFECYCLE, + ERROR_CODE_ABORTED); } }); } catch (RemoteException e) { diff --git a/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java index 20e2692cb747..bfd407216c3b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java @@ -27,6 +27,7 @@ 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_LIFECYCLE; 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; @@ -499,6 +500,27 @@ public class UserJourneyLoggerTest { 0x00000402, ERROR_CODE_UNSPECIFIED, 3); } + @Test + public void testUserLifecycleJourney() { + final long startTime = System.currentTimeMillis(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .startSessionForDelayedJourney(10, USER_JOURNEY_USER_LIFECYCLE, startTime); + + + final UserLifecycleJourneyReportedCaptor report = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logDelayedUserJourneyFinishWithError(0, targetUser, + USER_JOURNEY_USER_LIFECYCLE, ERROR_CODE_UNSPECIFIED); + + + report.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_LIFECYCLE, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + assertThat(report.mElapsedTime.getValue() > 0L).isTrue(); + } + static class UserLifecycleJourneyReportedCaptor { ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class); ArgumentCaptor<Integer> mJourney = ArgumentCaptor.forClass(Integer.class); @@ -507,6 +529,7 @@ public class UserJourneyLoggerTest { ArgumentCaptor<Integer> mUserType = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Integer> mUserFlags = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Long> mElapsedTime = ArgumentCaptor.forClass(Long.class); public void captureAndAssert(UserJourneyLogger mUserJourneyLogger, long sessionId, int journey, int originalUserId, @@ -518,7 +541,8 @@ public class UserJourneyLoggerTest { mTargetUserId.capture(), mUserType.capture(), mUserFlags.capture(), - mErrorCode.capture()); + mErrorCode.capture(), + mElapsedTime.capture()); assertThat(mSessionId.getValue()).isEqualTo(sessionId); assertThat(mJourney.getValue()).isEqualTo(journey); @@ -577,4 +601,4 @@ public class UserJourneyLoggerTest { state, errorCode, 1); } } -} +}
\ No newline at end of file |