Added option to dump end-to-end duration of first unlocked user.
This will be useful to measure the impact of using the User HAL to get
the initial user.
Test: atest CarServicesTest:com.android.internal.car.CarHelperServiceTest \
CarServiceUnitTest:com.android.car.user.CarUserServiceTest
Test: m android.car.testapi
Test: adb shell dumpsys car_service --first-user-metrics
Bug: 141388849
Bug: 146207078
Bug: 150222501
Change-Id: Iaad06d3cd2c72061650c27a14cba7f16c1d809bc
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index a68e171..0ff4bc0 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -366,6 +366,11 @@
}
@Override
+ public void onFirstUserUnlocked(int userId, long timestampMs, long duration) {
+ mUserMetrics.logFirstUnlockedUser(userId, timestampMs, duration);
+ }
+
+ @Override
public boolean isFeatureEnabled(String featureName) {
return mFeatureController.isFeatureEnabled(featureName);
}
@@ -670,6 +675,8 @@
return;
} else if ("--user-metrics".equals(args[0])) {
mUserMetrics.dump(writer);
+ } else if ("--first-user-metrics".equals(args[0])) {
+ mUserMetrics.dumpFirstUserUnlockDuration(writer);
} else if ("--help".equals(args[0])) {
showDumpHelp(writer);
} else if (Build.IS_USERDEBUG || Build.IS_ENG) {
@@ -711,6 +718,9 @@
writer.println("\t where HAL is just the class name (like UserHalService)");
writer.println("--user-metrics");
writer.println("\t dumps user switching and stopping metrics ");
+ writer.println("--first-user-metrics");
+ writer.println("\t dumps how long it took to unlock first user since Android started\n");
+ writer.println("\t (or -1 if not unlocked)");
writer.println("-h");
writer.println("\t shows commands usage (NOTE: commands are not available on USER builds");
writer.println("[ANYTHING ELSE]");
diff --git a/service/src/com/android/car/user/UserMetrics.java b/service/src/com/android/car/user/UserMetrics.java
index dad71fd..6c94f28 100644
--- a/service/src/com/android/car/user/UserMetrics.java
+++ b/service/src/com/android/car/user/UserMetrics.java
@@ -31,6 +31,7 @@
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -78,6 +79,9 @@
@GuardedBy("mLock")
private final LocalLog mUserStoppedLogs = new LocalLog(LOG_SIZE);
+ @GuardedBy("mLock")
+ private final SparseLongArray mFirstUserUnlockDuration = new SparseLongArray(1);
+
/**
* Logs a user lifecycle event.
*/
@@ -109,6 +113,16 @@
}
}
+ /**
+ * Logs when the first user was unlocked.
+ */
+ public void logFirstUnlockedUser(int userId, long timestampMs, long duration) {
+ synchronized (mLock) {
+ mFirstUserUnlockDuration.put(userId, duration);
+ onUserUnlockedEventLocked(timestampMs, userId);
+ }
+ }
+
private void onUserStartingEventLocked(long timestampMs, @UserIdInt int userId) {
if (mUserStartingMetrics == null) {
mUserStartingMetrics = new SparseArray<>(INITIAL_CAPACITY);
@@ -209,6 +223,14 @@
pw.println("* User Metrics *");
synchronized (mLock) {
+ if (mFirstUserUnlockDuration.size() == 0) {
+ pw.println("First user not unlocked yet");
+ } else {
+ pw.printf("First user (%d) unlocked in ", mFirstUserUnlockDuration.keyAt(0));
+ TimeUtils.formatDuration(mFirstUserUnlockDuration.valueAt(0), pw);
+ pw.println();
+ }
+
dump(pw, "starting", mUserStartingMetrics);
dump(pw, "stopping", mUserStoppingMetrics);
@@ -220,6 +242,19 @@
}
}
+ /**
+ * Dumps only how long it took to unlock the first user (or {@code -1} if not available).
+ */
+ public void dumpFirstUserUnlockDuration(@NonNull PrintWriter pw) {
+ synchronized (mLock) {
+ if (mFirstUserUnlockDuration.size() == 0) {
+ pw.println(-1);
+ return;
+ }
+ pw.println(mFirstUserUnlockDuration.valueAt(0));
+ }
+ }
+
private void dump(@NonNull PrintWriter pw, @NonNull String message,
@NonNull SparseArray<? extends BaseUserMetric> metrics) {
String indent = " ";