Loading services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +8 −1 Original line number Diff line number Diff line Loading @@ -763,10 +763,17 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream } if ((mEnabledFeatures & FLAG_FEATURE_MOUSE_KEYS) != 0) { TimeSource systemClockTimeSource = new TimeSource() { @Override public long uptimeMillis() { return SystemClock.uptimeMillis(); } }; mMouseKeysInterceptor = new MouseKeysInterceptor(mAms, Objects.requireNonNull(mContext.getSystemService(InputManager.class)), Looper.myLooper(), Display.DEFAULT_DISPLAY); Display.DEFAULT_DISPLAY, systemClockTimeSource); addFirstEventHandler(Display.DEFAULT_DISPLAY, mMouseKeysInterceptor); } Loading services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java +105 −25 Original line number Diff line number Diff line Loading @@ -75,9 +75,28 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation private static final int MESSAGE_SCROLL_MOUSE_POINTER = 2; private static final int KEY_NOT_SET = -1; /** Time interval after which mouse action will be repeated */ /** * The base time interval, in milliseconds, after which a mouse action (like movement or scroll) * will be repeated. This is used as the default interval when FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT * is not enabled, or for scroll actions even when FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT is enabled. */ private static final int INTERVAL_MILLIS = 10; /** * The specific time interval, in milliseconds, after which mouse pointer movement actions * are repeated when FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT is enabled. This value is longer than * {@link #INTERVAL_MILLIS} to allow for a perceptible acceleration curve. */ private static final int INTERVAL_MILLIS_MOUSE_POINTER = 25; /** * The initial movement step, in pixels per interval, for the mouse pointer. * This value is used as the starting point for acceleration, and also as the * reset value for the current movement step when a key is released and * FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT is enabled. */ private static final float INITIAL_MOUSE_POINTER_MOVEMENT_STEP = 1.0f; @VisibleForTesting public static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f; @VisibleForTesting Loading Loading @@ -122,6 +141,18 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation /** The ID of the input device that is currently active */ private int mActiveInputDeviceId = 0; /** The maximum movement step the mouse pointer can reach when accelerating. */ private float mMaxMovementStep = 10.0f; /** The acceleration factor applied to the mouse pointer's speed per interval. */ private float mAcceleration = 0.1f; /** The current movement step of the mouse pointer, which increases with acceleration. */ private float mCurrentMovementStep = INITIAL_MOUSE_POINTER_MOVEMENT_STEP; /** Provides a source for obtaining uptime, used for precise timing calculations. */ private final TimeSource mTimeSource; /** * Enum representing different types of mouse key events, each associated with a specific * key code. Loading Loading @@ -215,10 +246,11 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation */ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public MouseKeysInterceptor(AccessibilityManagerService service, InputManager inputManager, Looper looper, int displayId) { InputManager inputManager, Looper looper, int displayId, TimeSource timeSource) { mAms = service; mInputManager = inputManager; mHandler = new Handler(looper, this); mTimeSource = timeSource; // Create the virtual mouse on a separate thread since virtual device creation // should happen on an auxiliary thread, and not from the handler's thread. // This is because the handler thread is the same as the main thread, Loading Loading @@ -386,41 +418,53 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation private void performMousePointerAction(int keyCode) { float x = 0f; float y = 0f; if (Flags.enableMouseKeyEnhancement()) { // If there is no acceleration, start at the max movement step if (mAcceleration == 0.0f) { mCurrentMovementStep = mMaxMovementStep; } else { mCurrentMovementStep = Math.min( mCurrentMovementStep * (1 + mAcceleration), mMaxMovementStep); } } else { mCurrentMovementStep = MOUSE_POINTER_MOVEMENT_STEP; } MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from( keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap); switch (mouseKeyEvent) { case DIAGONAL_DOWN_LEFT_MOVE -> { x = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = -mCurrentMovementStep / sqrt(2); y = mCurrentMovementStep / sqrt(2); } case DOWN_MOVE_OR_SCROLL -> { if (!mScrollToggleOn) { y = MOUSE_POINTER_MOVEMENT_STEP; y = mCurrentMovementStep; } } case DIAGONAL_DOWN_RIGHT_MOVE -> { x = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = mCurrentMovementStep / sqrt(2); y = mCurrentMovementStep / sqrt(2); } case LEFT_MOVE_OR_SCROLL -> { x = -MOUSE_POINTER_MOVEMENT_STEP; x = -mCurrentMovementStep; } case RIGHT_MOVE_OR_SCROLL -> { x = MOUSE_POINTER_MOVEMENT_STEP; x = mCurrentMovementStep; } case DIAGONAL_UP_LEFT_MOVE -> { x = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = -mCurrentMovementStep / sqrt(2); y = -mCurrentMovementStep / sqrt(2); } case UP_MOVE_OR_SCROLL -> { if (!mScrollToggleOn) { y = -MOUSE_POINTER_MOVEMENT_STEP; y = -mCurrentMovementStep; } } case DIAGONAL_UP_RIGHT_MOVE -> { x = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = mCurrentMovementStep / sqrt(2); y = -mCurrentMovementStep / sqrt(2); } default -> { x = 0.0f; Loading Loading @@ -544,6 +588,8 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation if (mActiveMoveKey == keyCode) { // If the key is released, and it is the active key, stop moving the pointer mActiveMoveKey = KEY_NOT_SET; mCurrentMovementStep = Flags.enableMouseKeyEnhancement() ? INITIAL_MOUSE_POINTER_MOVEMENT_STEP : MOUSE_POINTER_MOVEMENT_STEP; mHandler.removeMessages(MESSAGE_MOVE_MOUSE_POINTER); } else if (mActiveScrollKey == keyCode) { // If the key is released, and it is the active key, stop scrolling the pointer Loading @@ -566,9 +612,14 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @Override public boolean handleMessage(Message msg) { long currentProcessingTime = msg.getWhen(); if (Flags.enableMouseKeyEnhancement()) { currentProcessingTime = this.mTimeSource.uptimeMillis(); } switch (msg.what) { case MESSAGE_MOVE_MOUSE_POINTER -> handleMouseMessage(msg.getWhen(), mActiveMoveKey, MESSAGE_MOVE_MOUSE_POINTER); handleMouseMessage(currentProcessingTime, mActiveMoveKey, MESSAGE_MOVE_MOUSE_POINTER); case MESSAGE_SCROLL_MOUSE_POINTER -> handleMouseMessage(msg.getWhen(), mActiveScrollKey, MESSAGE_SCROLL_MOUSE_POINTER); Loading @@ -582,14 +633,30 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation /** * Handles mouse-related messages for moving or scrolling the mouse pointer. * This method checks if the specified time interval {@code INTERVAL_MILLIS} has passed since * the last movement or scroll action and performs the corresponding action if necessary. * If there is an active key, the message is rescheduled to be handled again * after the specified {@code INTERVAL_MILLIS}. * * @param currentTime The current time when the message is being handled. * @param activeKey The key code representing the active key. This determines * the direction or type of action to be performed. * This method checks if the specified time interval (either {@code INTERVAL_MILLIS} or * {@code INTERVAL_MILLIS_MOUSE_POINTER} if mouse keys enhancement is enabled for move messages) * has passed since the last action was performed. If it has, the corresponding mouse * action (move or scroll) is executed based on the {@code activeKey} and {@code messageType}. * The time of this action is then recorded. * * If there is an {@code activeKey} (i.e., a key is still considered held down): * <ul> * <li>If {@code Flags.enableMouseKeyEnhancement()} is true, the message is precisely * rescheduled to be handled at a target uptime derived from the controlled * {@code mTimeSource} plus the relevant delay ({@code INTERVAL_MILLIS} or * {@code INTERVAL_MILLIS_MOUSE_POINTER}). This ensures consistent timing * irrespective of message handling latencies.</li> * <li>If {@code Flags.enableMouseKeyEnhancement()} is false, the message is rescheduled * to be handled again after a fixed delay of {@code INTERVAL_MILLIS} using * {@code sendEmptyMessageDelayed}.</li> * </ul> * * @param currentTime The current time (typically from the event or looper) when the message * is being initially processed. * @param activeKey The key code representing the active key. This determines the * direction or type of action to be performed. Should be * {@code KEY_NOT_SET} if no key is active. * @param messageType The type of message to be handled. It can be one of the * following: * <ul> Loading @@ -599,7 +666,13 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation */ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void handleMouseMessage(long currentTime, int activeKey, int messageType) { if (currentTime - mLastTimeKeyActionPerformed >= INTERVAL_MILLIS) { int delayMillis = INTERVAL_MILLIS; if (Flags.enableMouseKeyEnhancement() && messageType == MESSAGE_MOVE_MOUSE_POINTER) { delayMillis = INTERVAL_MILLIS_MOUSE_POINTER; } if (currentTime - mLastTimeKeyActionPerformed >= delayMillis) { if (messageType == MESSAGE_MOVE_MOUSE_POINTER) { performMousePointerAction(activeKey); } else if (messageType == MESSAGE_SCROLL_MOUSE_POINTER) { Loading @@ -608,10 +681,17 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation mLastTimeKeyActionPerformed = currentTime; } if (activeKey != KEY_NOT_SET) { if (Flags.enableMouseKeyEnhancement() && messageType == MESSAGE_MOVE_MOUSE_POINTER) { // Schedule next message using a target time based on the controlled clock long targetTime = this.mTimeSource.uptimeMillis() + delayMillis; Message nextMessage = Message.obtain(mHandler, messageType); mHandler.sendMessageAtTime(nextMessage, targetTime); } else { // Reschedule the message if the key is still active mHandler.sendEmptyMessageDelayed(messageType, INTERVAL_MILLIS); } } } @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @Override Loading services/accessibility/java/com/android/server/accessibility/TimeSource.java 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.accessibility; import android.os.SystemClock; /** * Provides a source for obtaining uptime in milliseconds. * This interface is used for dependency injection, allowing different implementations * for production code (using {@link SystemClock#uptimeMillis()}) and test code * (using a controllable, mockable clock). This ensures that time-dependent logic * can be tested deterministically. */ public interface TimeSource { /** * Returns the number of milliseconds since the device was last booted. * This time does not include time spent in deep sleep. It is typically * used for measuring durations or scheduling events that should be robust * to changes in wall-clock time (e.g., user changing the date/time). * In production, this should typically return the value of * {@link SystemClock#uptimeMillis()}. In tests, it might return a value * from a controlled, mockable clock. * * @return The number of milliseconds since device boot, not including deep sleep. */ long uptimeMillis(); } services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt +269 −51 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +8 −1 Original line number Diff line number Diff line Loading @@ -763,10 +763,17 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream } if ((mEnabledFeatures & FLAG_FEATURE_MOUSE_KEYS) != 0) { TimeSource systemClockTimeSource = new TimeSource() { @Override public long uptimeMillis() { return SystemClock.uptimeMillis(); } }; mMouseKeysInterceptor = new MouseKeysInterceptor(mAms, Objects.requireNonNull(mContext.getSystemService(InputManager.class)), Looper.myLooper(), Display.DEFAULT_DISPLAY); Display.DEFAULT_DISPLAY, systemClockTimeSource); addFirstEventHandler(Display.DEFAULT_DISPLAY, mMouseKeysInterceptor); } Loading
services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java +105 −25 Original line number Diff line number Diff line Loading @@ -75,9 +75,28 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation private static final int MESSAGE_SCROLL_MOUSE_POINTER = 2; private static final int KEY_NOT_SET = -1; /** Time interval after which mouse action will be repeated */ /** * The base time interval, in milliseconds, after which a mouse action (like movement or scroll) * will be repeated. This is used as the default interval when FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT * is not enabled, or for scroll actions even when FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT is enabled. */ private static final int INTERVAL_MILLIS = 10; /** * The specific time interval, in milliseconds, after which mouse pointer movement actions * are repeated when FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT is enabled. This value is longer than * {@link #INTERVAL_MILLIS} to allow for a perceptible acceleration curve. */ private static final int INTERVAL_MILLIS_MOUSE_POINTER = 25; /** * The initial movement step, in pixels per interval, for the mouse pointer. * This value is used as the starting point for acceleration, and also as the * reset value for the current movement step when a key is released and * FLAG_ENABLE_MOUSE_KEY_ENHANCEMENT is enabled. */ private static final float INITIAL_MOUSE_POINTER_MOVEMENT_STEP = 1.0f; @VisibleForTesting public static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f; @VisibleForTesting Loading Loading @@ -122,6 +141,18 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation /** The ID of the input device that is currently active */ private int mActiveInputDeviceId = 0; /** The maximum movement step the mouse pointer can reach when accelerating. */ private float mMaxMovementStep = 10.0f; /** The acceleration factor applied to the mouse pointer's speed per interval. */ private float mAcceleration = 0.1f; /** The current movement step of the mouse pointer, which increases with acceleration. */ private float mCurrentMovementStep = INITIAL_MOUSE_POINTER_MOVEMENT_STEP; /** Provides a source for obtaining uptime, used for precise timing calculations. */ private final TimeSource mTimeSource; /** * Enum representing different types of mouse key events, each associated with a specific * key code. Loading Loading @@ -215,10 +246,11 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation */ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public MouseKeysInterceptor(AccessibilityManagerService service, InputManager inputManager, Looper looper, int displayId) { InputManager inputManager, Looper looper, int displayId, TimeSource timeSource) { mAms = service; mInputManager = inputManager; mHandler = new Handler(looper, this); mTimeSource = timeSource; // Create the virtual mouse on a separate thread since virtual device creation // should happen on an auxiliary thread, and not from the handler's thread. // This is because the handler thread is the same as the main thread, Loading Loading @@ -386,41 +418,53 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation private void performMousePointerAction(int keyCode) { float x = 0f; float y = 0f; if (Flags.enableMouseKeyEnhancement()) { // If there is no acceleration, start at the max movement step if (mAcceleration == 0.0f) { mCurrentMovementStep = mMaxMovementStep; } else { mCurrentMovementStep = Math.min( mCurrentMovementStep * (1 + mAcceleration), mMaxMovementStep); } } else { mCurrentMovementStep = MOUSE_POINTER_MOVEMENT_STEP; } MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from( keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap); switch (mouseKeyEvent) { case DIAGONAL_DOWN_LEFT_MOVE -> { x = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = -mCurrentMovementStep / sqrt(2); y = mCurrentMovementStep / sqrt(2); } case DOWN_MOVE_OR_SCROLL -> { if (!mScrollToggleOn) { y = MOUSE_POINTER_MOVEMENT_STEP; y = mCurrentMovementStep; } } case DIAGONAL_DOWN_RIGHT_MOVE -> { x = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = mCurrentMovementStep / sqrt(2); y = mCurrentMovementStep / sqrt(2); } case LEFT_MOVE_OR_SCROLL -> { x = -MOUSE_POINTER_MOVEMENT_STEP; x = -mCurrentMovementStep; } case RIGHT_MOVE_OR_SCROLL -> { x = MOUSE_POINTER_MOVEMENT_STEP; x = mCurrentMovementStep; } case DIAGONAL_UP_LEFT_MOVE -> { x = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = -mCurrentMovementStep / sqrt(2); y = -mCurrentMovementStep / sqrt(2); } case UP_MOVE_OR_SCROLL -> { if (!mScrollToggleOn) { y = -MOUSE_POINTER_MOVEMENT_STEP; y = -mCurrentMovementStep; } } case DIAGONAL_UP_RIGHT_MOVE -> { x = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); y = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2); x = mCurrentMovementStep / sqrt(2); y = -mCurrentMovementStep / sqrt(2); } default -> { x = 0.0f; Loading Loading @@ -544,6 +588,8 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation if (mActiveMoveKey == keyCode) { // If the key is released, and it is the active key, stop moving the pointer mActiveMoveKey = KEY_NOT_SET; mCurrentMovementStep = Flags.enableMouseKeyEnhancement() ? INITIAL_MOUSE_POINTER_MOVEMENT_STEP : MOUSE_POINTER_MOVEMENT_STEP; mHandler.removeMessages(MESSAGE_MOVE_MOUSE_POINTER); } else if (mActiveScrollKey == keyCode) { // If the key is released, and it is the active key, stop scrolling the pointer Loading @@ -566,9 +612,14 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @Override public boolean handleMessage(Message msg) { long currentProcessingTime = msg.getWhen(); if (Flags.enableMouseKeyEnhancement()) { currentProcessingTime = this.mTimeSource.uptimeMillis(); } switch (msg.what) { case MESSAGE_MOVE_MOUSE_POINTER -> handleMouseMessage(msg.getWhen(), mActiveMoveKey, MESSAGE_MOVE_MOUSE_POINTER); handleMouseMessage(currentProcessingTime, mActiveMoveKey, MESSAGE_MOVE_MOUSE_POINTER); case MESSAGE_SCROLL_MOUSE_POINTER -> handleMouseMessage(msg.getWhen(), mActiveScrollKey, MESSAGE_SCROLL_MOUSE_POINTER); Loading @@ -582,14 +633,30 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation /** * Handles mouse-related messages for moving or scrolling the mouse pointer. * This method checks if the specified time interval {@code INTERVAL_MILLIS} has passed since * the last movement or scroll action and performs the corresponding action if necessary. * If there is an active key, the message is rescheduled to be handled again * after the specified {@code INTERVAL_MILLIS}. * * @param currentTime The current time when the message is being handled. * @param activeKey The key code representing the active key. This determines * the direction or type of action to be performed. * This method checks if the specified time interval (either {@code INTERVAL_MILLIS} or * {@code INTERVAL_MILLIS_MOUSE_POINTER} if mouse keys enhancement is enabled for move messages) * has passed since the last action was performed. If it has, the corresponding mouse * action (move or scroll) is executed based on the {@code activeKey} and {@code messageType}. * The time of this action is then recorded. * * If there is an {@code activeKey} (i.e., a key is still considered held down): * <ul> * <li>If {@code Flags.enableMouseKeyEnhancement()} is true, the message is precisely * rescheduled to be handled at a target uptime derived from the controlled * {@code mTimeSource} plus the relevant delay ({@code INTERVAL_MILLIS} or * {@code INTERVAL_MILLIS_MOUSE_POINTER}). This ensures consistent timing * irrespective of message handling latencies.</li> * <li>If {@code Flags.enableMouseKeyEnhancement()} is false, the message is rescheduled * to be handled again after a fixed delay of {@code INTERVAL_MILLIS} using * {@code sendEmptyMessageDelayed}.</li> * </ul> * * @param currentTime The current time (typically from the event or looper) when the message * is being initially processed. * @param activeKey The key code representing the active key. This determines the * direction or type of action to be performed. Should be * {@code KEY_NOT_SET} if no key is active. * @param messageType The type of message to be handled. It can be one of the * following: * <ul> Loading @@ -599,7 +666,13 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation */ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void handleMouseMessage(long currentTime, int activeKey, int messageType) { if (currentTime - mLastTimeKeyActionPerformed >= INTERVAL_MILLIS) { int delayMillis = INTERVAL_MILLIS; if (Flags.enableMouseKeyEnhancement() && messageType == MESSAGE_MOVE_MOUSE_POINTER) { delayMillis = INTERVAL_MILLIS_MOUSE_POINTER; } if (currentTime - mLastTimeKeyActionPerformed >= delayMillis) { if (messageType == MESSAGE_MOVE_MOUSE_POINTER) { performMousePointerAction(activeKey); } else if (messageType == MESSAGE_SCROLL_MOUSE_POINTER) { Loading @@ -608,10 +681,17 @@ public class MouseKeysInterceptor extends BaseEventStreamTransformation mLastTimeKeyActionPerformed = currentTime; } if (activeKey != KEY_NOT_SET) { if (Flags.enableMouseKeyEnhancement() && messageType == MESSAGE_MOVE_MOUSE_POINTER) { // Schedule next message using a target time based on the controlled clock long targetTime = this.mTimeSource.uptimeMillis() + delayMillis; Message nextMessage = Message.obtain(mHandler, messageType); mHandler.sendMessageAtTime(nextMessage, targetTime); } else { // Reschedule the message if the key is still active mHandler.sendEmptyMessageDelayed(messageType, INTERVAL_MILLIS); } } } @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @Override Loading
services/accessibility/java/com/android/server/accessibility/TimeSource.java 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.accessibility; import android.os.SystemClock; /** * Provides a source for obtaining uptime in milliseconds. * This interface is used for dependency injection, allowing different implementations * for production code (using {@link SystemClock#uptimeMillis()}) and test code * (using a controllable, mockable clock). This ensures that time-dependent logic * can be tested deterministically. */ public interface TimeSource { /** * Returns the number of milliseconds since the device was last booted. * This time does not include time spent in deep sleep. It is typically * used for measuring durations or scheduling events that should be robust * to changes in wall-clock time (e.g., user changing the date/time). * In production, this should typically return the value of * {@link SystemClock#uptimeMillis()}. In tests, it might return a value * from a controlled, mockable clock. * * @return The number of milliseconds since device boot, not including deep sleep. */ long uptimeMillis(); }
services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt +269 −51 File changed.Preview size limit exceeded, changes collapsed. Show changes