Add multi-user jank monitoring

Created CUJ for monitoring jank when switching users, including guest
users, but excluding the "Add User" user journey.

Bug: 200160966
Test: switch users manually, look at logs
Test: atest KeyguardUpdateMonitorTest
Test: atest UserSwitcherControllerTest
Change-Id: I5570133a1f96a58d39a8f053f9f480cdaada8df1
This commit is contained in:
Lucas Dupin
2021-09-27 17:36:30 -07:00
parent 9fbadb5a3b
commit 71d3f5bb9e
7 changed files with 66 additions and 5 deletions

View File

@@ -59,6 +59,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.annotation.IntDef;
@@ -171,6 +172,7 @@ public class InteractionJankMonitor {
public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
public static final int CUJ_PIP_TRANSITION = 35;
public static final int CUJ_WALLPAPER_TRANSITION = 36;
public static final int CUJ_USER_SWITCH = 37;
private static final int NO_STATSD_LOGGING = -1;
@@ -216,6 +218,7 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH,
};
private static volatile InteractionJankMonitor sInstance;
@@ -272,6 +275,7 @@ public class InteractionJankMonitor {
CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
CUJ_PIP_TRANSITION,
CUJ_WALLPAPER_TRANSITION,
CUJ_USER_SWITCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -642,6 +646,8 @@ public class InteractionJankMonitor {
return "PIP_TRANSITION";
case CUJ_WALLPAPER_TRANSITION:
return "WALLPAPER_TRANSITION";
case CUJ_USER_SWITCH:
return "USER_SWITCH";
}
return "UNKNOWN";
}

View File

@@ -90,6 +90,7 @@ import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
@@ -321,6 +322,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean mIsDreaming;
private final DevicePolicyManager mDevicePolicyManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final InteractionJankMonitor mInteractionJankMonitor;
private boolean mLogoutEnabled;
// cached value to avoid IPCs
private boolean mIsUdfpsEnrolled;
@@ -1770,6 +1772,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
AuthController authController,
TelephonyListenerManager telephonyListenerManager,
FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
@Nullable Vibrator vibrator) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
@@ -1778,6 +1781,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mInteractionJankMonitor = interactionJankMonitor;
mRingerModeTracker = ringerModeTracker;
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
@@ -2637,7 +2641,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/**
* Handle {@link #MSG_USER_SWITCH_COMPLETE}
*/
private void handleUserSwitchComplete(int userId) {
@VisibleForTesting
void handleUserSwitchComplete(int userId) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2645,6 +2650,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onUserSwitchComplete(userId);
}
}
mInteractionJankMonitor.end(InteractionJankMonitor.CUJ_USER_SWITCH);
}
/**

View File

@@ -74,6 +74,7 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -218,6 +219,12 @@ public class FrameworkServicesModule {
return context.getSystemService(FingerprintManager.class);
}
@Provides
@Singleton
static InteractionJankMonitor provideInteractionJankMonitor() {
return InteractionJankMonitor.getInstance();
}
@Provides
@Singleton
static InputMethodManager provideInputMethodManager(Context context) {

View File

@@ -1139,6 +1139,8 @@ public class StatusBar extends SystemUI implements
mNotificationPanelViewController.addExpansionListener(
this::dispatchPanelExpansionForKeyguardDismiss);
mUserSwitcherController.init(mNotificationShadeWindowView);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);

View File

@@ -25,6 +25,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -55,6 +56,7 @@ import android.view.WindowManagerGlobal;
import android.widget.BaseAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -75,6 +77,7 @@ import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
@@ -107,6 +110,7 @@ public class UserSwitcherController implements Dumpable {
private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l;
protected final Context mContext;
protected final UserTracker mUserTracker;
@@ -123,6 +127,7 @@ public class UserSwitcherController implements Dumpable {
private final BroadcastDispatcher mBroadcastDispatcher;
private final TelephonyListenerManager mTelephonyListenerManager;
private final IActivityTaskManager mActivityTaskManager;
private final InteractionJankMonitor mInteractionJankMonitor;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
@VisibleForTesting
@@ -141,15 +146,18 @@ public class UserSwitcherController implements Dumpable {
private Intent mSecondaryUserServiceIntent;
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
private final UiEventLogger mUiEventLogger;
private final IActivityManager mActivityManager;
public final DetailAdapter mUserDetailAdapter;
private final Executor mBgExecutor;
private final boolean mGuestUserAutoCreated;
private final AtomicBoolean mGuestIsResetting;
private final AtomicBoolean mGuestCreationScheduled;
private FalsingManager mFalsingManager;
private NotificationShadeWindowView mRootView;
@Inject
public UserSwitcherController(Context context,
IActivityManager activityManager,
UserManager userManager,
UserTracker userTracker,
KeyguardStateController keyguardStateController,
@@ -165,14 +173,17 @@ public class UserSwitcherController implements Dumpable {
UserDetailAdapter userDetailAdapter,
SecureSettings secureSettings,
@Background Executor bgExecutor,
InteractionJankMonitor interactionJankMonitor,
DumpManager dumpManager) {
mContext = context;
mActivityManager = activityManager;
mUserTracker = userTracker;
mBroadcastDispatcher = broadcastDispatcher;
mTelephonyListenerManager = telephonyListenerManager;
mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
mFalsingManager = falsingManager;
mInteractionJankMonitor = interactionJankMonitor;
mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
this, mUserTracker, mUiEventLogger, secureSettings);
mUserDetailAdapter = userDetailAdapter;
@@ -485,8 +496,11 @@ public class UserSwitcherController implements Dumpable {
protected void switchToUserId(int id) {
try {
mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
.withView(InteractionJankMonitor.CUJ_USER_SWITCH, mRootView)
.setTimeout(MULTI_USER_JOURNEY_TIMEOUT));
pauseRefreshUsers();
ActivityManager.getService().switchUser(id);
mActivityManager.switchUser(id);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't switch user.", e);
}
@@ -793,6 +807,10 @@ public class UserSwitcherController implements Dumpable {
return guest.id;
}
public void init(NotificationShadeWindowView notificationShadeWindowView) {
mRootView = notificationShadeWindowView;
}
public static abstract class BaseUserAdapter extends BaseAdapter {
final UserSwitcherController mController;

View File

@@ -78,6 +78,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
@@ -171,6 +172,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Mock
private FeatureFlags mFeatureFlags;
@Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Mock
private Vibrator mVibrator;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
@@ -736,6 +739,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
}
@Test
public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
final IRemoteCallback reply = new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle data) {} // do nothing
};
mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
verify(mInteractionJankMonitor).end(eq(InteractionJankMonitor.CUJ_USER_SWITCH));
}
@Test
public void testGetUserCanSkipBouncer_whenTrust() {
int user = KeyguardUpdateMonitor.getCurrentUser();
@@ -1051,7 +1064,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mRingerModeTracker, mBackgroundExecutor,
mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager, mFeatureFlags,
mVibrator);
mInteractionJankMonitor, mVibrator);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy
import android.app.IActivityManager
import android.app.IActivityTaskManager
import android.app.admin.DevicePolicyManager
import android.content.Context
@@ -31,6 +32,7 @@ import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.UserIcons
import com.android.systemui.GuestResumeSessionReceiver
@@ -54,10 +56,11 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -65,6 +68,7 @@ import org.mockito.MockitoAnnotations
@SmallTest
class UserSwitcherControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var handler: Handler
@@ -78,6 +82,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
@Mock private lateinit var secureSettings: SecureSettings
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var interactionJankMonitor: InteractionJankMonitor
private lateinit var testableLooper: TestableLooper
private lateinit var uiBgExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -110,6 +115,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
userSwitcherController = UserSwitcherController(
context,
activityManager,
userManager,
userTracker,
keyguardStateController,
@@ -125,6 +131,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
userDetailAdapter,
secureSettings,
uiBgExecutor,
interactionJankMonitor,
dumpManager)
userSwitcherController.mPauseRefreshUsers = true
@@ -132,7 +139,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
}
@Test
fun testAddGuest_okButtonPressed_isLogged() {
fun testAddGuest_okButtonPressed() {
val emptyGuestUserRecord = UserSwitcherController.UserRecord(
null,
null,
@@ -148,6 +155,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
testableLooper.processAllMessages()
verify(interactionJankMonitor).begin(any())
verify(activityManager).switchUser(guestInfo.id)
assertEquals(1, uiEventLogger.numLogs())
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
}