diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java new file mode 100644 index 0000000000000..a9ae3121ce9f3 --- /dev/null +++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java @@ -0,0 +1,41 @@ +/* + * 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.input; + +import android.view.InputDevice; + +import com.android.internal.util.FrameworkStatsLog; + +/** + * Collect Keyboard metrics + */ +public final class KeyboardMetricsCollector { + private static final String TAG = "KeyboardMetricCollector"; + + /** + * 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) { + int vendor_id = inputDevice.getVendorId(); + int product_id = inputDevice.getProductId(); + FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED, + vendor_id, product_id, keyboardSystemEvent, keyCode, modifierState); + } +} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 56dcac8d122a9..9526784083438 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -124,6 +124,7 @@ import android.hardware.hdmi.HdmiAudioSystemClient; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback; +import android.hardware.input.InputManager; import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioSystem; @@ -205,6 +206,7 @@ import com.android.internal.policy.PhoneWindow; import com.android.internal.policy.TransitionAnimation; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.server.AccessibilityManagerInternal; import com.android.server.ExtconStateObserver; @@ -215,6 +217,7 @@ import com.android.server.SystemServiceManager; 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.inputmethod.InputMethodManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule; @@ -678,6 +681,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_LAUNCH_ASSIST = 23; private static final int MSG_RINGER_TOGGLE_CHORD = 24; private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25; + private static final int MSG_LOG_KEYBOARD_SYSTEM_EVENT = 26; private class PolicyHandler extends Handler { @Override @@ -751,6 +755,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_SWITCH_KEYBOARD_LAYOUT: handleSwitchKeyboardLayout(msg.arg1, msg.arg2); break; + case MSG_LOG_KEYBOARD_SYSTEM_EVENT: + handleKeyboardSystemEvent(msg.arg2, (KeyEvent) msg.obj); + break; } } } @@ -2288,6 +2295,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { cancelPendingScreenshotChordAction(); } }); + if (mHasFeatureWatch) { mKeyCombinationManager.addRule( new TwoKeysCombinationRule(KEYCODE_POWER, KEYCODE_STEM_PRIMARY) { @@ -2907,7 +2915,30 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, }; + /** + * Log the keyboard shortcuts without blocking the current thread. + * + * 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 logKeyboardSystemsEvent(KeyEvent event, int keyboardSystemEvent) { + mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, 0, keyboardSystemEvent, event) + .sendToTarget(); + } + // TODO(b/117479243): handle it in InputPolicy + // TODO (b/283241997): Add the remaining keyboard shortcut logging after refactoring /** {@inheritDoc} */ @Override public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, @@ -2961,6 +2992,8 @@ 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 @@ -2978,6 +3011,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_RECENT_APPS: if (down && repeatCount == 0) { showRecentApps(false /* triggeredFromAltTab */); + logKeyboardSystemsEvent(event, FrameworkStatsLog + .KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS); } return key_consumed; case KeyEvent.KEYCODE_APP_SWITCH: