From 6d0ac955ed4952848edde6d93089970b10d26f36 Mon Sep 17 00:00:00 2001 From: Vaibhav Devmurari Date: Mon, 3 Jul 2023 13:18:36 +0000 Subject: [PATCH] Log system keys and shortcuts Test: atest ShortcutLoggingTests Bug: 280423320 Change-Id: I22ca8cca9f3ea42625b1c406eec51ebeebc17cec --- .../input/KeyboardMetricsCollector.java | 310 +++++++++++++++++- .../policy/ModifierShortcutManager.java | 32 +- .../server/policy/PhoneWindowManager.java | 104 ++++-- services/tests/wmtests/Android.bp | 1 + .../server/policy/ShortcutKeyTestBase.java | 2 +- .../server/policy/ShortcutLoggingTests.java | 245 ++++++++++++++ .../server/policy/TestPhoneWindowManager.java | 49 ++- 7 files changed, 708 insertions(+), 35 deletions(-) create mode 100644 services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java index eb2da340004b6..4b30ae530ae67 100644 --- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java +++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java @@ -21,12 +21,16 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Intent; import android.hardware.input.KeyboardLayout; import android.icu.util.ULocale; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.InputDevice; +import android.view.KeyEvent; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.VisibleForTesting; @@ -36,8 +40,12 @@ import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; /** * Collect Keyboard metrics @@ -50,13 +58,14 @@ public final class KeyboardMetricsCollector { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @Retention(SOURCE) - @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = { + @IntDef(prefix = {"LAYOUT_SELECTION_CRITERIA_"}, value = { LAYOUT_SELECTION_CRITERIA_USER, LAYOUT_SELECTION_CRITERIA_DEVICE, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, LAYOUT_SELECTION_CRITERIA_DEFAULT }) - public @interface LayoutSelectionCriteria {} + public @interface LayoutSelectionCriteria { + } /** Manual selection by user */ public static final int LAYOUT_SELECTION_CRITERIA_USER = 0; @@ -76,17 +85,301 @@ public final class KeyboardMetricsCollector { @VisibleForTesting static final String DEFAULT_LANGUAGE_TAG = "None"; + public enum KeyboardLogEvent { + UNSPECIFIED( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED, + "INVALID_KEYBOARD_EVENT"), + HOME(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME, + "HOME"), + RECENT_APPS( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS, + "RECENT_APPS"), + BACK(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK, + "BACK"), + APP_SWITCH( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH, + "APP_SWITCH"), + LAUNCH_ASSISTANT( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT, + "LAUNCH_ASSISTANT"), + LAUNCH_VOICE_ASSISTANT( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT, + "LAUNCH_VOICE_ASSISTANT"), + LAUNCH_SYSTEM_SETTINGS( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS, + "LAUNCH_SYSTEM_SETTINGS"), + TOGGLE_NOTIFICATION_PANEL( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL, + "TOGGLE_NOTIFICATION_PANEL"), + TOGGLE_TASKBAR( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR, + "TOGGLE_TASKBAR"), + TAKE_SCREENSHOT( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT, + "TAKE_SCREENSHOT"), + OPEN_SHORTCUT_HELPER( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER, + "OPEN_SHORTCUT_HELPER"), + BRIGHTNESS_UP( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP, + "BRIGHTNESS_UP"), + BRIGHTNESS_DOWN( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN, + "BRIGHTNESS_DOWN"), + KEYBOARD_BACKLIGHT_UP( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP, + "KEYBOARD_BACKLIGHT_UP"), + KEYBOARD_BACKLIGHT_DOWN( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN, + "KEYBOARD_BACKLIGHT_DOWN"), + KEYBOARD_BACKLIGHT_TOGGLE( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE, + "KEYBOARD_BACKLIGHT_TOGGLE"), + VOLUME_UP( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP, + "VOLUME_UP"), + VOLUME_DOWN( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN, + "VOLUME_DOWN"), + VOLUME_MUTE( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE, + "VOLUME_MUTE"), + ALL_APPS( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS, + "ALL_APPS"), + LAUNCH_SEARCH( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH, + "LAUNCH_SEARCH"), + LANGUAGE_SWITCH( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH, + "LANGUAGE_SWITCH"), + ACCESSIBILITY_ALL_APPS( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS, + "ACCESSIBILITY_ALL_APPS"), + TOGGLE_CAPS_LOCK( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK, + "TOGGLE_CAPS_LOCK"), + SYSTEM_MUTE( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE, + "SYSTEM_MUTE"), + SPLIT_SCREEN_NAVIGATION( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION, + "SPLIT_SCREEN_NAVIGATION"), + TRIGGER_BUG_REPORT( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT, + "TRIGGER_BUG_REPORT"), + LOCK_SCREEN( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN, + "LOCK_SCREEN"), + OPEN_NOTES( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES, + "OPEN_NOTES"), + TOGGLE_POWER( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER, + "TOGGLE_POWER"), + SYSTEM_NAVIGATION( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION, + "SYSTEM_NAVIGATION"), + SLEEP(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP, + "SLEEP"), + WAKEUP(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP, + "WAKEUP"), + MEDIA_KEY( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY, + "MEDIA_KEY"), + LAUNCH_DEFAULT_BROWSER( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER, + "LAUNCH_DEFAULT_BROWSER"), + LAUNCH_DEFAULT_EMAIL( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL, + "LAUNCH_DEFAULT_EMAIL"), + LAUNCH_DEFAULT_CONTACTS( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS, + "LAUNCH_DEFAULT_CONTACTS"), + LAUNCH_DEFAULT_CALENDAR( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR, + "LAUNCH_DEFAULT_CALENDAR"), + LAUNCH_DEFAULT_CALCULATOR( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR, + "LAUNCH_DEFAULT_CALCULATOR"), + LAUNCH_DEFAULT_MUSIC( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC, + "LAUNCH_DEFAULT_MUSIC"), + LAUNCH_DEFAULT_MAPS( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS, + "LAUNCH_DEFAULT_MAPS"), + LAUNCH_DEFAULT_MESSAGING( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING, + "LAUNCH_DEFAULT_MESSAGING"), + LAUNCH_DEFAULT_GALLERY( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY, + "LAUNCH_DEFAULT_GALLERY"), + LAUNCH_DEFAULT_FILES( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES, + "LAUNCH_DEFAULT_FILES"), + LAUNCH_DEFAULT_WEATHER( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER, + "LAUNCH_DEFAULT_WEATHER"), + LAUNCH_DEFAULT_FITNESS( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS, + "LAUNCH_DEFAULT_FITNESS"), + LAUNCH_APPLICATION_BY_PACKAGE_NAME( + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME, + "LAUNCH_APPLICATION_BY_PACKAGE_NAME"); + + private final int mValue; + private final String mName; + + private static final SparseArray VALUE_TO_ENUM_MAP = new SparseArray<>(); + + static { + for (KeyboardLogEvent type : KeyboardLogEvent.values()) { + VALUE_TO_ENUM_MAP.put(type.mValue, type); + } + } + + KeyboardLogEvent(int enumValue, String enumName) { + mValue = enumValue; + mName = enumName; + } + + public int getIntValue() { + return mValue; + } + + /** + * Convert int value to corresponding KeyboardLogEvent enum. If can't find any matching + * value will return {@code null} + */ + @Nullable + public static KeyboardLogEvent from(int value) { + return VALUE_TO_ENUM_MAP.get(value); + } + + /** + * Find KeyboardLogEvent corresponding to volume up/down/mute key events. + */ + @Nullable + public static KeyboardLogEvent getVolumeEvent(int keycode) { + switch (keycode) { + case KeyEvent.KEYCODE_VOLUME_DOWN: + return VOLUME_DOWN; + case KeyEvent.KEYCODE_VOLUME_UP: + return VOLUME_UP; + case KeyEvent.KEYCODE_VOLUME_MUTE: + return VOLUME_MUTE; + default: + return null; + } + } + + /** + * Find KeyboardLogEvent corresponding to brightness up/down key events. + */ + @Nullable + public static KeyboardLogEvent getBrightnessEvent(int keycode) { + switch (keycode) { + case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: + return BRIGHTNESS_DOWN; + case KeyEvent.KEYCODE_BRIGHTNESS_UP: + return BRIGHTNESS_UP; + default: + return null; + } + } + + /** + * Find KeyboardLogEvent corresponding to intent filter category. Returns + * {@code null if no matching event found} + */ + @Nullable + public static KeyboardLogEvent getLogEventFromIntent(Intent intent) { + Intent selectorIntent = intent.getSelector(); + if (selectorIntent != null) { + Set selectorCategories = selectorIntent.getCategories(); + if (selectorCategories != null && !selectorCategories.isEmpty()) { + for (String intentCategory : selectorCategories) { + KeyboardLogEvent logEvent = getEventFromSelectorCategory(intentCategory); + if (logEvent == null) { + continue; + } + return logEvent; + } + } + } + + Set intentCategories = intent.getCategories(); + if (intentCategories == null || intentCategories.isEmpty() + || !intentCategories.contains(Intent.CATEGORY_LAUNCHER)) { + return null; + } + if (intent.getComponent() == null) { + return null; + } + + // TODO(b/280423320): Add new field package name associated in the + // KeyboardShortcutEvent atom and log it accordingly. + return LAUNCH_APPLICATION_BY_PACKAGE_NAME; + } + + @Nullable + private static KeyboardLogEvent getEventFromSelectorCategory(String category) { + switch (category) { + case Intent.CATEGORY_APP_BROWSER: + return LAUNCH_DEFAULT_BROWSER; + case Intent.CATEGORY_APP_EMAIL: + return LAUNCH_DEFAULT_EMAIL; + case Intent.CATEGORY_APP_CONTACTS: + return LAUNCH_DEFAULT_CONTACTS; + case Intent.CATEGORY_APP_CALENDAR: + return LAUNCH_DEFAULT_CALENDAR; + case Intent.CATEGORY_APP_CALCULATOR: + return LAUNCH_DEFAULT_CALCULATOR; + case Intent.CATEGORY_APP_MUSIC: + return LAUNCH_DEFAULT_MUSIC; + case Intent.CATEGORY_APP_MAPS: + return LAUNCH_DEFAULT_MAPS; + case Intent.CATEGORY_APP_MESSAGING: + return LAUNCH_DEFAULT_MESSAGING; + case Intent.CATEGORY_APP_GALLERY: + return LAUNCH_DEFAULT_GALLERY; + case Intent.CATEGORY_APP_FILES: + return LAUNCH_DEFAULT_FILES; + case Intent.CATEGORY_APP_WEATHER: + return LAUNCH_DEFAULT_WEATHER; + case Intent.CATEGORY_APP_FITNESS: + return LAUNCH_DEFAULT_FITNESS; + default: + return null; + } + } + } + /** * Log keyboard system shortcuts for the proto * {@link com.android.os.input.KeyboardSystemsEventReported} * defined in "stats/atoms/input/input_extension_atoms.proto" */ - public static void logKeyboardSystemsEventReportedAtom(InputDevice inputDevice, - int keyboardSystemEvent, int[] keyCode, int modifierState) { + public static void logKeyboardSystemsEventReportedAtom(@Nullable InputDevice inputDevice, + @Nullable KeyboardLogEvent keyboardSystemEvent, int modifierState, int... keyCodes) { + // Logging Keyboard system event only for an external HW keyboard. We should not log events + // for virtual keyboards or internal Key events. + if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { + return; + } int vendorId = inputDevice.getVendorId(); int productId = inputDevice.getProductId(); + if (keyboardSystemEvent == null) { + Slog.w(TAG, "Invalid keyboard event logging, keycode = " + Arrays.toString(keyCodes) + + ", modifier state = " + modifierState); + return; + } FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED, - vendorId, productId, keyboardSystemEvent, keyCode, modifierState); + vendorId, productId, keyboardSystemEvent.getIntValue(), keyCodes, modifierState); + + if (DEBUG) { + Slog.d(TAG, "Logging Keyboard system event: " + keyboardSystemEvent.mName); + } } /** @@ -94,8 +387,8 @@ public final class KeyboardMetricsCollector { * {@link com.android.os.input.KeyboardConfigured} atom * * @param event {@link KeyboardConfigurationEvent} contains information about keyboard - * configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the - * configuration event to log. + * configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the + * configuration event to log. */ public static void logKeyboardConfiguredAtom(KeyboardConfigurationEvent event) { // Creating proto to log nested field KeyboardLayoutConfig in atom @@ -241,7 +534,7 @@ public final class KeyboardMetricsCollector { KeyboardLayout selectedLayout = mSelectedLayoutList.get(i); @LayoutSelectionCriteria int layoutSelectionCriteria = mLayoutSelectionCriteriaList.get(i); - InputMethodSubtype imeSubtype = mImeSubtypeList.get(i); + InputMethodSubtype imeSubtype = mImeSubtypeList.get(i); String keyboardLanguageTag = mInputDevice.getKeyboardLanguageTag(); keyboardLanguageTag = keyboardLanguageTag == null ? DEFAULT_LANGUAGE_TAG : keyboardLanguageTag; @@ -328,4 +621,3 @@ public final class KeyboardMetricsCollector { || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT; } } - diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java index 784e177d6362c..69cc1252f6966 100644 --- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java +++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java @@ -16,6 +16,7 @@ package com.android.server.policy; +import android.annotation.SuppressLint; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; @@ -23,6 +24,8 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; +import android.hardware.input.InputManager; +import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; @@ -30,11 +33,14 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; +import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import com.android.internal.policy.IShortcutService; import com.android.internal.util.XmlUtils; +import com.android.server.input.KeyboardMetricsCollector; +import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -88,11 +94,13 @@ class ModifierShortcutManager { } private final Context mContext; + private final Handler mHandler; private boolean mSearchKeyShortcutPending = false; private boolean mConsumeSearchKeyUp = true; - ModifierShortcutManager(Context context) { + ModifierShortcutManager(Context context, Handler handler) { mContext = context; + mHandler = handler; loadShortcuts(); } @@ -273,11 +281,13 @@ class ModifierShortcutManager { * Handle the shortcut to {@link Intent} * * @param kcm the {@link KeyCharacterMap} associated with the keyboard device. - * @param keyCode The key code of the event. + * @param keyEvent The key event. * @param metaState The meta key modifier state. * @return True if invoked the shortcut, otherwise false. */ - private boolean handleIntentShortcut(KeyCharacterMap kcm, int keyCode, int metaState) { + @SuppressLint("MissingPermission") + private boolean handleIntentShortcut(KeyCharacterMap kcm, KeyEvent keyEvent, int metaState) { + final int keyCode = keyEvent.getKeyCode(); // Shortcuts are invoked through Search+key, so intercept those here // Any printing key that is chorded with Search should be consumed // even if no shortcut was invoked. This prevents text from being @@ -307,6 +317,7 @@ class ModifierShortcutManager { + "keyCode=" + KeyEvent.keyCodeToString(keyCode) + "," + " category=" + category); } + logKeyboardShortcut(keyEvent, KeyboardLogEvent.getLogEventFromIntent(intent)); return true; } else { return false; @@ -323,11 +334,24 @@ class ModifierShortcutManager { + "the activity to which it is registered was not found: " + "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode)); } + logKeyboardShortcut(keyEvent, KeyboardLogEvent.getLogEventFromIntent(shortcutIntent)); return true; } return false; } + private void logKeyboardShortcut(KeyEvent event, KeyboardLogEvent logEvent) { + mHandler.post(() -> handleKeyboardLogging(event, logEvent)); + } + + private void handleKeyboardLogging(KeyEvent event, KeyboardLogEvent logEvent) { + final InputManager inputManager = mContext.getSystemService(InputManager.class); + final InputDevice inputDevice = inputManager != null + ? inputManager.getInputDevice(event.getDeviceId()) : null; + KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(inputDevice, + logEvent, event.getMetaState(), event.getKeyCode()); + } + /** * Handle the shortcut from {@link KeyEvent} * @@ -360,7 +384,7 @@ class ModifierShortcutManager { } final KeyCharacterMap kcm = event.getKeyCharacterMap(); - if (handleIntentShortcut(kcm, keyCode, metaState)) { + if (handleIntentShortcut(kcm, event, metaState)) { return true; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 5cfbcaafe82ef..ca647929c271b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -219,6 +219,7 @@ import com.android.server.UiThread; import com.android.server.display.BrightnessUtils; import com.android.server.input.InputManagerInternal; import com.android.server.input.KeyboardMetricsCollector; +import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule; @@ -409,6 +410,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { ActivityManagerInternal mActivityManagerInternal; ActivityTaskManagerInternal mActivityTaskManagerInternal; AutofillManagerInternal mAutofillManagerInternal; + InputManager mInputManager; InputManagerInternal mInputManagerInternal; DreamManagerInternal mDreamManagerInternal; PowerManagerInternal mPowerManagerInternal; @@ -762,7 +764,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { handleSwitchKeyboardLayout(msg.arg1, msg.arg2); break; case MSG_LOG_KEYBOARD_SYSTEM_EVENT: - handleKeyboardSystemEvent(msg.arg2, (KeyEvent) msg.obj); + handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj); break; } } @@ -1762,8 +1764,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void launchAllAppsViaA11y() { - getAccessibilityManagerInternal().performSystemAction( - AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal(); + if (accessibilityManager != null) { + accessibilityManager.performSystemAction( + AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + } } private void toggleNotificationPanel() { @@ -1834,6 +1839,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If we have released the home key, and didn't do anything else // while it was pressed, then it is time to go home! if (!down) { + logKeyboardSystemsEvent(event, KeyboardLogEvent.HOME); if (mDisplayId == DEFAULT_DISPLAY) { cancelPreloadRecentApps(); } @@ -2035,6 +2041,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); + mInputManager = mContext.getSystemService(InputManager.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); @@ -2104,7 +2111,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler); mSettingsObserver = new SettingsObserver(mHandler); mSettingsObserver.observe(); - mModifierShortcutManager = new ModifierShortcutManager(mContext); + mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler); mUiMode = mContext.getResources().getInteger( com.android.internal.R.integer.config_defaultUiModeType); mHomeIntent = new Intent(Intent.ACTION_MAIN, null); @@ -2930,20 +2937,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { * We won't log keyboard events when the input device is null * or when it is virtual. */ - private void handleKeyboardSystemEvent(int keyboardSystemEvent, KeyEvent event) { - final InputManager inputManager = mContext.getSystemService(InputManager.class); - final InputDevice inputDevice = inputManager != null - ? inputManager.getInputDevice(event.getDeviceId()) : null; - if (inputDevice != null && !inputDevice.isVirtual()) { - KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom( - inputDevice, keyboardSystemEvent, - new int[]{event.getKeyCode()}, event.getMetaState()); - } + private void handleKeyboardSystemEvent(KeyboardLogEvent keyboardLogEvent, KeyEvent event) { + final InputDevice inputDevice = mInputManager.getInputDevice(event.getDeviceId()); + KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(inputDevice, + keyboardLogEvent, event.getMetaState(), event.getKeyCode()); + event.recycle(); } - private void logKeyboardSystemsEvent(KeyEvent event, int keyboardSystemEvent) { - mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, 0, keyboardSystemEvent, event) - .sendToTarget(); + private void logKeyboardSystemsEventOnActionUp(KeyEvent event, + KeyboardLogEvent keyboardSystemEvent) { + if (event.getAction() != KeyEvent.ACTION_UP) { + return; + } + logKeyboardSystemsEvent(event, keyboardSystemEvent); + } + + private void logKeyboardSystemsEventOnActionDown(KeyEvent event, + KeyboardLogEvent keyboardSystemEvent) { + if (event.getAction() != KeyEvent.ACTION_DOWN) { + return; + } + logKeyboardSystemsEvent(event, keyboardSystemEvent); + } + + private void logKeyboardSystemsEvent(KeyEvent event, KeyboardLogEvent keyboardSystemEvent) { + KeyEvent eventToLog = KeyEvent.obtain(event); + mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, keyboardSystemEvent.getIntValue(), 0, + eventToLog).sendToTarget(); } // TODO(b/117479243): handle it in InputPolicy @@ -3044,8 +3064,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { switch (keyCode) { case KeyEvent.KEYCODE_HOME: - logKeyboardSystemsEvent(event, - FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME); return handleHomeShortcuts(displayId, focusedToken, event); case KeyEvent.KEYCODE_MENU: // Hijack modified menu keys for debugging features @@ -3056,14 +3074,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { Intent intent = new Intent(Intent.ACTION_BUG_REPORT); mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, null, null, 0, null, null); + logKeyboardSystemsEvent(event, KeyboardLogEvent.TRIGGER_BUG_REPORT); return true; } break; case KeyEvent.KEYCODE_RECENT_APPS: if (firstDown) { showRecentApps(false /* triggeredFromAltTab */); - logKeyboardSystemsEvent(event, - FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS); + logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS); } return true; case KeyEvent.KEYCODE_APP_SWITCH: @@ -3072,6 +3090,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { preloadRecentApps(); } else if (!down) { toggleRecentApps(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.APP_SWITCH); } } return true; @@ -3080,6 +3099,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, deviceId, event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT); return true; } break; @@ -3092,12 +3112,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_I: if (firstDown && event.isMetaPressed()) { showSystemSettings(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS); return true; } break; case KeyEvent.KEYCODE_L: if (firstDown && event.isMetaPressed()) { lockNow(null /* options */); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LOCK_SCREEN); return true; } break; @@ -3105,8 +3127,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (firstDown && event.isMetaPressed()) { if (event.isCtrlPressed()) { sendSystemKeyToStatusBarAsync(event); + logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_NOTES); } else { toggleNotificationPanel(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL); } return true; } @@ -3114,12 +3138,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_S: if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/); + logKeyboardSystemsEvent(event, KeyboardLogEvent.TAKE_SCREENSHOT); return true; } break; case KeyEvent.KEYCODE_T: if (firstDown && event.isMetaPressed()) { toggleTaskbar(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_TASKBAR); return true; } break; @@ -3128,6 +3154,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); if (statusbar != null) { statusbar.goToFullscreenFromSplit(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION); return true; } } @@ -3135,18 +3162,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_DPAD_LEFT: if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { enterStageSplitFromRunningApp(true /* leftOrTop */); + logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION); return true; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { enterStageSplitFromRunningApp(false /* leftOrTop */); + logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION); return true; } break; case KeyEvent.KEYCODE_SLASH: if (firstDown && event.isMetaPressed() && !keyguardOn) { toggleKeyboardShortcutsMenu(event.getDeviceId()); + logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_SHORTCUT_HELPER); return true; } break; @@ -3215,20 +3245,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true); startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); + logKeyboardSystemsEvent(event, KeyboardLogEvent.getBrightnessEvent(keyCode)); } return true; case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN: if (down) { mInputManagerInternal.decrementKeyboardBacklight(event.getDeviceId()); + logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_DOWN); } return true; case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP: if (down) { mInputManagerInternal.incrementKeyboardBacklight(event.getDeviceId()); + logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_UP); } return true; case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE: // TODO: Add logic + if (!down) { + logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_TOGGLE); + } return true; case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: @@ -3254,6 +3290,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (firstDown && !keyguardOn && isUserSetupComplete()) { if (event.isMetaPressed()) { showRecentApps(false); + logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS); return true; } else if (mRecentAppsHeldModifiers == 0) { final int shiftlessModifiers = @@ -3262,6 +3299,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { shiftlessModifiers, KeyEvent.META_ALT_ON)) { mRecentAppsHeldModifiers = shiftlessModifiers; showRecentApps(true); + logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS); return true; } } @@ -3273,11 +3311,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS); msg.setAsynchronous(true); msg.sendToTarget(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS); } return true; case KeyEvent.KEYCODE_NOTIFICATION: if (!down) { toggleNotificationPanel(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL); } return true; case KeyEvent.KEYCODE_SEARCH: @@ -3285,6 +3325,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { switch (mSearchKeyBehavior) { case SEARCH_BEHAVIOR_TARGET_ACTIVITY: { launchTargetSearchActivity(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SEARCH); return true; } case SEARCH_BEHAVIOR_DEFAULT_SEARCH: @@ -3297,6 +3338,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (firstDown) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; sendSwitchKeyboardLayout(event, direction); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH); return true; } break; @@ -3305,6 +3347,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (firstDown && event.isMetaPressed()) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; sendSwitchKeyboardLayout(event, direction); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH); return true; } break; @@ -3323,9 +3366,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mPendingCapsLockToggle) { mInputManagerInternal.toggleCapsLock(event.getDeviceId()); mPendingCapsLockToggle = false; + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK); } else if (mPendingMetaAction) { if (!canceled) { launchAllAppsViaA11y(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS); } mPendingMetaAction = false; } @@ -3353,10 +3398,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mPendingCapsLockToggle) { mInputManagerInternal.toggleCapsLock(event.getDeviceId()); mPendingCapsLockToggle = false; + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK); return true; } } break; + case KeyEvent.KEYCODE_CAPS_LOCK: + if (!down) { + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK); + } + break; case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY: case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY: case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY: @@ -4200,6 +4251,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_BACK: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.BACK); if (down) { mBackKeyHandled = false; } else { @@ -4217,6 +4269,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE: { + logKeyboardSystemsEventOnActionDown(event, + KeyboardLogEvent.getVolumeEvent(keyCode)); if (down) { sendSystemKeyToStatusBarAsync(event); @@ -4317,6 +4371,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } case KeyEvent.KEYCODE_TV_POWER: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER); result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately if (down && hdmiControlManager != null) { @@ -4326,6 +4381,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } case KeyEvent.KEYCODE_POWER: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER); EventLogTags.writeInterceptPower( KeyEvent.actionToString(event.getAction()), mPowerKeyHandled ? 1 : 0, @@ -4348,12 +4404,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT: // fall through case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SYSTEM_NAVIGATION); result &= ~ACTION_PASS_TO_USER; interceptSystemNavigationKey(event); break; } case KeyEvent.KEYCODE_SLEEP: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP); result &= ~ACTION_PASS_TO_USER; isWakeKey = false; if (!mPowerManager.isInteractive()) { @@ -4368,6 +4426,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } case KeyEvent.KEYCODE_SOFT_SLEEP: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP); result &= ~ACTION_PASS_TO_USER; isWakeKey = false; if (!down) { @@ -4377,6 +4436,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } case KeyEvent.KEYCODE_WAKEUP: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.WAKEUP); result &= ~ACTION_PASS_TO_USER; isWakeKey = true; break; @@ -4385,6 +4445,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_MUTE: result &= ~ACTION_PASS_TO_USER; if (down && event.getRepeatCount() == 0) { + logKeyboardSystemsEvent(event, KeyboardLogEvent.SYSTEM_MUTE); toggleMicrophoneMuteFromKey(); } break; @@ -4399,6 +4460,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { + logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.MEDIA_KEY); if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) { // If the global session is active pass all media keys to it // instead of the active window. @@ -4443,6 +4505,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 0 /* unused */, event.getEventTime() /* eventTime */); msg.setAsynchronous(true); msg.sendToTarget(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT); } result &= ~ACTION_PASS_TO_USER; break; @@ -4453,6 +4516,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK); msg.setAsynchronous(true); msg.sendToTarget(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_VOICE_ASSISTANT); } result &= ~ACTION_PASS_TO_USER; break; diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index 5f48f3ce1d682..6aa59895fb82d 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -51,6 +51,7 @@ android_test { "services.core", "androidx.test.runner", "androidx.test.rules", + "junit-params", "mockito-target-extended-minus-junit4", "platform-test-annotations", "servicestests-utils", diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java index 2015ae9b8081a..bf88ce460b998 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -63,7 +63,7 @@ class ShortcutKeyTestBase { final Context mContext = spy(getInstrumentation().getTargetContext()); /** Modifier key to meta state */ - private static final Map MODIFIER; + protected static final Map MODIFIER; static { final Map map = new ArrayMap<>(); map.put(KEYCODE_CTRL_LEFT, META_CTRL_LEFT_ON | META_CTRL_ON); diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java new file mode 100644 index 0000000000000..feca3268a95c7 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java @@ -0,0 +1,245 @@ +/* + * Copyright 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.policy; + +import android.platform.test.annotations.Presubmit; +import android.view.KeyEvent; + +import androidx.test.filters.SmallTest; + +import com.android.internal.annotations.Keep; +import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +@Presubmit +@SmallTest +@RunWith(JUnitParamsRunner.class) +public class ShortcutLoggingTests extends ShortcutKeyTestBase { + + private static final int VENDOR_ID = 0x123; + private static final int PRODUCT_ID = 0x456; + private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT; + private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT); + private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT; + private static final int ALT_ON = MODIFIER.get(KeyEvent.KEYCODE_ALT_LEFT); + private static final int CTRL_KEY = KeyEvent.KEYCODE_CTRL_LEFT; + private static final int CTRL_ON = MODIFIER.get(KeyEvent.KEYCODE_CTRL_LEFT); + private static final int SHIFT_KEY = KeyEvent.KEYCODE_SHIFT_LEFT; + private static final int SHIFT_ON = MODIFIER.get(KeyEvent.KEYCODE_SHIFT_LEFT); + + @Keep + private static Object[][] shortcutTestArguments() { + // testName, testKeys, expectedLogEvent, expectedKey, expectedModifierState + return new Object[][]{ + {"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H}, + KeyboardLogEvent.HOME, KeyEvent.KEYCODE_H, META_ON}, + {"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, + KeyboardLogEvent.HOME, KeyEvent.KEYCODE_ENTER, META_ON}, + {"HOME key -> Open Home", new int[]{KeyEvent.KEYCODE_HOME}, KeyboardLogEvent.HOME, + KeyEvent.KEYCODE_HOME, 0}, + {"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS}, + KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_RECENT_APPS, 0}, + {"Meta + Tab -> Open OVerview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB}, + KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, META_ON}, + {"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB}, + KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, ALT_ON}, + {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK}, KeyboardLogEvent.BACK, + KeyEvent.KEYCODE_BACK, 0}, + {"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH}, + KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_APP_SWITCH, 0}, + {"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST}, + KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_ASSIST, 0}, + {"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A}, + KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A, META_ON}, + {"VOICE_ASSIST key -> Launch Voice Assistant", + new int[]{KeyEvent.KEYCODE_VOICE_ASSIST}, + KeyboardLogEvent.LAUNCH_VOICE_ASSISTANT, KeyEvent.KEYCODE_VOICE_ASSIST, 0}, + {"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I}, + KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS, KeyEvent.KEYCODE_I, META_ON}, + {"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N}, + KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_N, META_ON}, + {"NOTIFICATION key -> Toggle Notification Panel", + new int[]{KeyEvent.KEYCODE_NOTIFICATION}, + KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_NOTIFICATION, + 0}, + {"Meta + T -> Toggle Taskbar", new int[]{META_KEY, KeyEvent.KEYCODE_T}, + KeyboardLogEvent.TOGGLE_TASKBAR, KeyEvent.KEYCODE_T, META_ON}, + {"Meta + Ctrl + S -> Take Screenshot", + new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_S}, + KeyboardLogEvent.TAKE_SCREENSHOT, KeyEvent.KEYCODE_S, META_ON | CTRL_ON}, + {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH}, + KeyboardLogEvent.OPEN_SHORTCUT_HELPER, KeyEvent.KEYCODE_SLASH, META_ON}, + {"BRIGHTNESS_UP key -> Increase Brightness", + new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP}, KeyboardLogEvent.BRIGHTNESS_UP, + KeyEvent.KEYCODE_BRIGHTNESS_UP, 0}, + {"BRIGHTNESS_DOWN key -> Decrease Brightness", + new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN}, + KeyboardLogEvent.BRIGHTNESS_DOWN, KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0}, + {"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight", + new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP}, + KeyboardLogEvent.KEYBOARD_BACKLIGHT_UP, + KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0}, + {"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight", + new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN}, + KeyboardLogEvent.KEYBOARD_BACKLIGHT_DOWN, + KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0}, + {"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight", + new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE}, + KeyboardLogEvent.KEYBOARD_BACKLIGHT_TOGGLE, + KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0}, + {"VOLUME_UP key -> Increase Volume", new int[]{KeyEvent.KEYCODE_VOLUME_UP}, + KeyboardLogEvent.VOLUME_UP, KeyEvent.KEYCODE_VOLUME_UP, 0}, + {"VOLUME_DOWN key -> Decrease Volume", new int[]{KeyEvent.KEYCODE_VOLUME_DOWN}, + KeyboardLogEvent.VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN, 0}, + {"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE}, + KeyboardLogEvent.VOLUME_MUTE, KeyEvent.KEYCODE_VOLUME_MUTE, 0}, + {"ALL_APPS key -> Open App Drawer", new int[]{KeyEvent.KEYCODE_ALL_APPS}, + KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_ALL_APPS, 0}, + {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH}, + KeyboardLogEvent.LAUNCH_SEARCH, KeyEvent.KEYCODE_SEARCH, 0}, + {"LANGUAGE_SWITCH key -> Switch Keyboard Language", + new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH}, + KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0}, + {"Meta + Space -> Switch Keyboard Language", + new int[]{META_KEY, KeyEvent.KEYCODE_SPACE}, + KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE, META_ON}, + {"Meta + Shift + Space -> Switch Keyboard Language", + new int[]{META_KEY, SHIFT_KEY, KeyEvent.KEYCODE_SPACE}, + KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE, + META_ON | SHIFT_ON}, + {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY}, + KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, META_KEY, META_ON}, + {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY}, + KeyboardLogEvent.TOGGLE_CAPS_LOCK, ALT_KEY, META_ON | ALT_ON}, + {"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY}, + KeyboardLogEvent.TOGGLE_CAPS_LOCK, META_KEY, META_ON | ALT_ON}, + {"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK}, + KeyboardLogEvent.TOGGLE_CAPS_LOCK, KeyEvent.KEYCODE_CAPS_LOCK, 0}, + {"MUTE key -> Mute System Microphone", new int[]{KeyEvent.KEYCODE_MUTE}, + KeyboardLogEvent.SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE, 0}, + {"Meta + Ctrl + DPAD_UP -> Split screen navigation", + new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP}, + KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_UP, + META_ON | CTRL_ON}, + {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation", + new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT}, + KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_LEFT, + META_ON | CTRL_ON}, + {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation", + new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT}, + KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_RIGHT, + META_ON | CTRL_ON}, + {"Shift + Menu -> Trigger Bug Report", new int[]{SHIFT_KEY, KeyEvent.KEYCODE_MENU}, + KeyboardLogEvent.TRIGGER_BUG_REPORT, KeyEvent.KEYCODE_MENU, SHIFT_ON}, + {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L}, + KeyboardLogEvent.LOCK_SCREEN, KeyEvent.KEYCODE_L, META_ON}, + {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N}, + KeyboardLogEvent.OPEN_NOTES, KeyEvent.KEYCODE_N, META_ON | CTRL_ON}, + {"POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_POWER}, + KeyboardLogEvent.TOGGLE_POWER, KeyEvent.KEYCODE_POWER, 0}, + {"TV_POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_TV_POWER}, + KeyboardLogEvent.TOGGLE_POWER, KeyEvent.KEYCODE_TV_POWER, 0}, + {"SYSTEM_NAVIGATION_DOWN key -> System Navigation", + new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN}, + KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN, + 0}, + {"SYSTEM_NAVIGATION_UP key -> System Navigation", + new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP}, + KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP, + 0}, + {"SYSTEM_NAVIGATION_LEFT key -> System Navigation", + new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT}, + KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT, + 0}, + {"SYSTEM_NAVIGATION_RIGHT key -> System Navigation", + new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT}, + KeyboardLogEvent.SYSTEM_NAVIGATION, + KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT, 0}, + {"SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SLEEP}, + KeyboardLogEvent.SLEEP, KeyEvent.KEYCODE_SLEEP, 0}, + {"SOFT_SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SOFT_SLEEP}, + KeyboardLogEvent.SLEEP, KeyEvent.KEYCODE_SOFT_SLEEP, 0}, + {"WAKEUP key -> System Wakeup", new int[]{KeyEvent.KEYCODE_WAKEUP}, + KeyboardLogEvent.WAKEUP, KeyEvent.KEYCODE_WAKEUP, 0}, + {"MEDIA_PLAY key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PLAY}, + KeyboardLogEvent.MEDIA_KEY, KeyEvent.KEYCODE_MEDIA_PLAY, 0}, + {"MEDIA_PAUSE key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PAUSE}, + KeyboardLogEvent.MEDIA_KEY, KeyEvent.KEYCODE_MEDIA_PAUSE, 0}, + {"MEDIA_PLAY_PAUSE key -> Media Control", + new int[]{KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE}, KeyboardLogEvent.MEDIA_KEY, + KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0}, + {"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B}, + KeyboardLogEvent.LAUNCH_DEFAULT_BROWSER, KeyEvent.KEYCODE_B, META_ON}, + {"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER}, + KeyboardLogEvent.LAUNCH_DEFAULT_BROWSER, KeyEvent.KEYCODE_EXPLORER, 0}, + {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C}, + KeyboardLogEvent.LAUNCH_DEFAULT_CONTACTS, KeyEvent.KEYCODE_C, META_ON}, + {"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS}, + KeyboardLogEvent.LAUNCH_DEFAULT_CONTACTS, KeyEvent.KEYCODE_CONTACTS, 0}, + {"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E}, + KeyboardLogEvent.LAUNCH_DEFAULT_EMAIL, KeyEvent.KEYCODE_E, META_ON}, + {"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE}, + KeyboardLogEvent.LAUNCH_DEFAULT_EMAIL, KeyEvent.KEYCODE_ENVELOPE, 0}, + {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K}, + KeyboardLogEvent.LAUNCH_DEFAULT_CALENDAR, KeyEvent.KEYCODE_K, META_ON}, + {"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR}, + KeyboardLogEvent.LAUNCH_DEFAULT_CALENDAR, KeyEvent.KEYCODE_CALENDAR, 0}, + {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P}, + KeyboardLogEvent.LAUNCH_DEFAULT_MUSIC, KeyEvent.KEYCODE_P, META_ON}, + {"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC}, + KeyboardLogEvent.LAUNCH_DEFAULT_MUSIC, KeyEvent.KEYCODE_MUSIC, 0}, + {"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U}, + KeyboardLogEvent.LAUNCH_DEFAULT_CALCULATOR, KeyEvent.KEYCODE_U, META_ON}, + {"CALCULATOR key -> Launch Default Calculator", + new int[]{KeyEvent.KEYCODE_CALCULATOR}, + KeyboardLogEvent.LAUNCH_DEFAULT_CALCULATOR, KeyEvent.KEYCODE_CALCULATOR, 0}, + {"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M}, + KeyboardLogEvent.LAUNCH_DEFAULT_MAPS, KeyEvent.KEYCODE_M, META_ON}, + {"Meta + S -> Launch Default Messaging App", + new int[]{META_KEY, KeyEvent.KEYCODE_S}, + KeyboardLogEvent.LAUNCH_DEFAULT_MESSAGING, KeyEvent.KEYCODE_S, META_ON}}; + } + + @Before + @Override + public void setUp() { + super.setUp(); + mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID); + mPhoneWindowManager.overrideLaunchHome(); + mPhoneWindowManager.overrideSearchKeyBehavior( + PhoneWindowManager.SEARCH_BEHAVIOR_TARGET_ACTIVITY); + mPhoneWindowManager.overrideEnableBugReportTrigger(true); + mPhoneWindowManager.overrideStatusBarManagerInternal(); + mPhoneWindowManager.overrideStartActivity(); + mPhoneWindowManager.overrideUserSetupComplete(); + } + + @Test + @Parameters(method = "shortcutTestArguments") + public void testShortcuts(String testName, int[] testKeys, KeyboardLogEvent expectedLogEvent, + int expectedKey, int expectedModifierState) { + sendKeyCombination(testKeys, 0); + mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent, + expectedKey, expectedModifierState, "Failed while executing " + testName); + } +} diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 766a88f6476cc..186676772a6af 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -26,6 +26,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.description; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; @@ -35,6 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GO_TO_VOICE_ASSIST; @@ -50,7 +52,6 @@ import static org.mockito.Mockito.after; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mockingDetails; import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.withSettings; import android.app.ActivityManagerInternal; @@ -60,8 +61,10 @@ import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.hardware.SensorPrivacyManager; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; +import android.hardware.input.InputManager; import android.media.AudioManagerInternal; import android.os.Handler; import android.os.HandlerThread; @@ -75,14 +78,17 @@ import android.service.dreams.DreamManagerInternal; import android.telecom.TelecomManager; import android.util.FeatureFlagUtils; import android.view.Display; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.autofill.AutofillManagerInternal; import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.GestureLauncherService; import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; +import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.policy.keyguard.KeyguardServiceDelegate; @@ -102,6 +108,7 @@ import org.mockito.Mock; import org.mockito.MockSettings; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.quality.Strictness; import java.util.function.Supplier; @@ -117,6 +124,8 @@ class TestPhoneWindowManager { @Mock private ActivityManagerInternal mActivityManagerInternal; @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal; @Mock private InputManagerInternal mInputManagerInternal; + @Mock private InputManager mInputManager; + @Mock private SensorPrivacyManager mSensorPrivacyManager; @Mock private DreamManagerInternal mDreamManagerInternal; @Mock private PowerManagerInternal mPowerManagerInternal; @Mock private DisplayManagerInternal mDisplayManagerInternal; @@ -186,6 +195,8 @@ class TestPhoneWindowManager { // Return mocked services: LocalServices.getService mMockitoSession = mockitoSession() .mockStatic(LocalServices.class, spyStubOnly) + .mockStatic(FrameworkStatsLog.class) + .strictness(Strictness.LENIENT) .startMocking(); doReturn(mWindowManagerInternal).when( @@ -213,7 +224,10 @@ class TestPhoneWindowManager { doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class)); doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class)); + doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class)); doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(mSensorPrivacyManager).when(mContext).getSystemService( + eq(SensorPrivacyManager.class)); doReturn(false).when(mPackageManager).hasSystemFeature(any()); try { doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager) @@ -409,6 +423,31 @@ class TestPhoneWindowManager { doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing(); } + void overrideKeyEventSource(int vendorId, int productId) { + InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId( + productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType( + InputDevice.KEYBOARD_TYPE_ALPHABETIC).build(); + doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class)); + doReturn(device).when(mInputManager).getInputDevice(anyInt()); + } + + void overrideSearchKeyBehavior(int behavior) { + mPhoneWindowManager.mSearchKeyBehavior = behavior; + } + + void overrideEnableBugReportTrigger(boolean enable) { + mPhoneWindowManager.mEnableShiftMenuBugReports = enable; + } + + void overrideStartActivity() { + doNothing().when(mContext).startActivityAsUser(any(), any()); + doNothing().when(mContext).startActivityAsUser(any(), any(), any()); + } + + void overrideUserSetupComplete() { + doReturn(true).when(mPhoneWindowManager).isUserSetupComplete(); + } + /** * Below functions will check the policy behavior could be invoked. */ @@ -563,4 +602,12 @@ class TestPhoneWindowManager { verify(mContext, after(TEST_SINGLE_KEY_DELAY_MILLIS).never()) .startActivityAsUser(any(Intent.class), any(), any(UserHandle.class)); } + + void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent, + int expectedKey, int expectedModifierState, String errorMsg) { + waitForIdle(); + verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED, + vendorId, productId, logEvent.getIntValue(), new int[]{expectedKey}, + expectedModifierState), description(errorMsg)); + } }