Merge "Add elapsed time to user journey report and user lifecycle journey (from create to delete)" into udc-dev am: cdd7f04ff5 am: 76a1b10a82
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23168143 Change-Id: I3465e659f6faee41b0a4469416d1e73292623808 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -99,6 +99,8 @@ public class UserJourneyLogger {
|
|||||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN;
|
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN;
|
||||||
public static final int USER_JOURNEY_REVOKE_ADMIN =
|
public static final int USER_JOURNEY_REVOKE_ADMIN =
|
||||||
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__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 = {
|
@IntDef(prefix = {"USER_JOURNEY"}, value = {
|
||||||
USER_JOURNEY_UNKNOWN,
|
USER_JOURNEY_UNKNOWN,
|
||||||
@@ -109,7 +111,8 @@ public class UserJourneyLogger {
|
|||||||
USER_JOURNEY_USER_CREATE,
|
USER_JOURNEY_USER_CREATE,
|
||||||
USER_JOURNEY_USER_REMOVE,
|
USER_JOURNEY_USER_REMOVE,
|
||||||
USER_JOURNEY_GRANT_ADMIN,
|
USER_JOURNEY_GRANT_ADMIN,
|
||||||
USER_JOURNEY_REVOKE_ADMIN
|
USER_JOURNEY_REVOKE_ADMIN,
|
||||||
|
USER_JOURNEY_USER_LIFECYCLE
|
||||||
})
|
})
|
||||||
public @interface UserJourney {
|
public @interface UserJourney {
|
||||||
}
|
}
|
||||||
@@ -272,11 +275,12 @@ public class UserJourneyLogger {
|
|||||||
int userType, int userFlags, @UserJourneyErrorCode int errorCode) {
|
int userType, int userFlags, @UserJourneyErrorCode int errorCode) {
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
writeUserLifecycleJourneyReported(-1, journey, originalUserId, targetUserId,
|
writeUserLifecycleJourneyReported(-1, journey, originalUserId, targetUserId,
|
||||||
userType, userFlags, ERROR_CODE_INVALID_SESSION_ID);
|
userType, userFlags, ERROR_CODE_INVALID_SESSION_ID, -1);
|
||||||
} else {
|
} else {
|
||||||
|
final long elapsedTime = System.currentTimeMillis() - session.mStartTimeInMills;
|
||||||
writeUserLifecycleJourneyReported(
|
writeUserLifecycleJourneyReported(
|
||||||
session.mSessionId, journey, originalUserId, targetUserId, userType, userFlags,
|
session.mSessionId, journey, originalUserId, targetUserId, userType, userFlags,
|
||||||
errorCode);
|
errorCode, elapsedTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,10 +289,10 @@ public class UserJourneyLogger {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void writeUserLifecycleJourneyReported(long sessionId, int journey, int originalUserId,
|
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,
|
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED,
|
||||||
sessionId, journey, originalUserId, targetUserId, userType, userFlags,
|
sessionId, journey, originalUserId, targetUserId, userType, userFlags,
|
||||||
errorCode);
|
errorCode, elapsedTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -451,6 +455,29 @@ public class UserJourneyLogger {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* 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.
|
* can not be passed because it is null, therefore all information are passed as arguments.
|
||||||
@@ -532,6 +559,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.
|
* Helper class to store user journey and session id.
|
||||||
*
|
*
|
||||||
@@ -542,11 +586,19 @@ public class UserJourneyLogger {
|
|||||||
public final long mSessionId;
|
public final long mSessionId;
|
||||||
@UserJourney
|
@UserJourney
|
||||||
public final int mJourney;
|
public final int mJourney;
|
||||||
|
public long mStartTimeInMills;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public UserJourneySession(long sessionId, @UserJourney int journey) {
|
public UserJourneySession(long sessionId, @UserJourney int journey) {
|
||||||
mJourney = journey;
|
mJourney = journey;
|
||||||
mSessionId = sessionId;
|
mSessionId = sessionId;
|
||||||
|
mStartTimeInMills = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
@VisibleForTesting
|
||||||
|
public UserJourneySession(long sessionId, @UserJourney int journey, long startTimeInMills) {
|
||||||
|
mJourney = journey;
|
||||||
|
mSessionId = sessionId;
|
||||||
|
mStartTimeInMills = startTimeInMills;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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_GRANT_ADMIN;
|
||||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_REVOKE_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_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_REMOVE;
|
||||||
|
|
||||||
import android.Manifest;
|
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#MANAGE_USERS MANAGE_USERS} or
|
||||||
* {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
|
* {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
|
||||||
* can make certain calls to the UserManager.
|
* 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.logUserJourneyBegin(userId, USER_JOURNEY_USER_REMOVE);
|
||||||
|
mUserJourneyLogger.startSessionForDelayedJourney(userId,
|
||||||
|
USER_JOURNEY_USER_LIFECYCLE, userData.info.creationTime);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mAppOpsService.removeUser(userId);
|
mAppOpsService.removeUser(userId);
|
||||||
@@ -5560,6 +5563,10 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
|
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
|
||||||
userData.info, USER_JOURNEY_USER_REMOVE,
|
userData.info, USER_JOURNEY_USER_REMOVE,
|
||||||
ERROR_CODE_UNSPECIFIED);
|
ERROR_CODE_UNSPECIFIED);
|
||||||
|
mUserJourneyLogger
|
||||||
|
.logDelayedUserJourneyFinishWithError(originUserId,
|
||||||
|
userData.info, USER_JOURNEY_USER_LIFECYCLE,
|
||||||
|
ERROR_CODE_UNSPECIFIED);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void userStopAborted(int userIdParam) {
|
public void userStopAborted(int userIdParam) {
|
||||||
@@ -5567,6 +5574,10 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
|
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
|
||||||
userData.info, USER_JOURNEY_USER_REMOVE,
|
userData.info, USER_JOURNEY_USER_REMOVE,
|
||||||
ERROR_CODE_ABORTED);
|
ERROR_CODE_ABORTED);
|
||||||
|
mUserJourneyLogger
|
||||||
|
.logDelayedUserJourneyFinishWithError(originUserId,
|
||||||
|
userData.info, USER_JOURNEY_USER_LIFECYCLE,
|
||||||
|
ERROR_CODE_ABORTED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
|
|||||||
@@ -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.EVENT_STATE_NONE;
|
||||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN;
|
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_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_REMOVE;
|
||||||
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START;
|
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_STOP;
|
||||||
@@ -499,6 +500,27 @@ public class UserJourneyLoggerTest {
|
|||||||
0x00000402, ERROR_CODE_UNSPECIFIED, 3);
|
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 {
|
static class UserLifecycleJourneyReportedCaptor {
|
||||||
ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class);
|
ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class);
|
||||||
ArgumentCaptor<Integer> mJourney = ArgumentCaptor.forClass(Integer.class);
|
ArgumentCaptor<Integer> mJourney = ArgumentCaptor.forClass(Integer.class);
|
||||||
@@ -507,6 +529,7 @@ public class UserJourneyLoggerTest {
|
|||||||
ArgumentCaptor<Integer> mUserType = ArgumentCaptor.forClass(Integer.class);
|
ArgumentCaptor<Integer> mUserType = ArgumentCaptor.forClass(Integer.class);
|
||||||
ArgumentCaptor<Integer> mUserFlags = ArgumentCaptor.forClass(Integer.class);
|
ArgumentCaptor<Integer> mUserFlags = ArgumentCaptor.forClass(Integer.class);
|
||||||
ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class);
|
ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
ArgumentCaptor<Long> mElapsedTime = ArgumentCaptor.forClass(Long.class);
|
||||||
|
|
||||||
public void captureAndAssert(UserJourneyLogger mUserJourneyLogger,
|
public void captureAndAssert(UserJourneyLogger mUserJourneyLogger,
|
||||||
long sessionId, int journey, int originalUserId,
|
long sessionId, int journey, int originalUserId,
|
||||||
@@ -518,7 +541,8 @@ public class UserJourneyLoggerTest {
|
|||||||
mTargetUserId.capture(),
|
mTargetUserId.capture(),
|
||||||
mUserType.capture(),
|
mUserType.capture(),
|
||||||
mUserFlags.capture(),
|
mUserFlags.capture(),
|
||||||
mErrorCode.capture());
|
mErrorCode.capture(),
|
||||||
|
mElapsedTime.capture());
|
||||||
|
|
||||||
assertThat(mSessionId.getValue()).isEqualTo(sessionId);
|
assertThat(mSessionId.getValue()).isEqualTo(sessionId);
|
||||||
assertThat(mJourney.getValue()).isEqualTo(journey);
|
assertThat(mJourney.getValue()).isEqualTo(journey);
|
||||||
|
|||||||
Reference in New Issue
Block a user