/* * Copyright (C) 2012 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 android.hardware.input; import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API; import static com.android.input.flags.Flags.FLAG_DEVICE_ASSOCIATIONS; import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag; import android.Manifest; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityThread; import android.compat.annotation.ChangeId; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.drawable.Drawable; import android.hardware.BatteryState; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.InputEventInjectionSync; import android.os.RemoteException; import android.os.SystemClock; import android.os.Vibrator; import android.util.Log; import android.view.Display; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.VerifiedInputEvent; import android.view.WindowManager.LayoutParams; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; /** * Provides information about input devices and available key layouts. */ @SystemService(Context.INPUT_SERVICE) public final class InputManager { private static final String TAG = "InputManager"; // To enable these logs, run: 'adb shell setprop log.tag.InputManager DEBUG' (requires restart) private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final IInputManager mIm; /** * Whether a PointerIcon is shown for stylus pointers. * Obtain using {@link #isStylusPointerIconEnabled()}. */ @Nullable private Boolean mIsStylusPointerIconEnabled = null; /** * Broadcast Action: Query available keyboard layouts. *

* The input manager service locates available keyboard layouts * by querying broadcast receivers that are registered for this action. * An application can offer additional keyboard layouts to the user * by declaring a suitable broadcast receiver in its manifest. *

* Here is an example broadcast receiver declaration that an application * might include in its AndroidManifest.xml to advertise keyboard layouts. * The meta-data specifies a resource that contains a description of each keyboard * layout that is provided by the application. *


     * <receiver android:name=".InputDeviceReceiver"
     *         android:label="@string/keyboard_layouts_label">
     *     <intent-filter>
     *         <action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
     *     </intent-filter>
     *     <meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
     *             android:resource="@xml/keyboard_layouts" />
     * </receiver>
     * 
*

* In the above example, the @xml/keyboard_layouts resource refers to * an XML resource whose root element is <keyboard-layouts> that * contains zero or more <keyboard-layout> elements. * Each <keyboard-layout> element specifies the name, label, and location * of a key character map for a particular keyboard layout. The label on the receiver * is used to name the collection of keyboard layouts provided by this receiver in the * keyboard layout settings. *


     * <?xml version="1.0" encoding="utf-8"?>
     * <keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
     *     <keyboard-layout android:name="keyboard_layout_english_us"
     *             android:label="@string/keyboard_layout_english_us_label"
     *             android:keyboardLayout="@raw/keyboard_layout_english_us" />
     * </keyboard-layouts>
     * 
*

* The android:name attribute specifies an identifier by which * the keyboard layout will be known in the package. * The android:label attribute specifies a human-readable descriptive * label to describe the keyboard layout in the user interface, such as "English (US)". * The android:keyboardLayout attribute refers to a * * key character map resource that defines the keyboard layout. * The android:keyboardLocale attribute specifies a comma separated list of BCP 47 * language tags depicting the locales supported by the keyboard layout. This attribute is * optional and will be used for auto layout selection for external physical keyboards. * The android:keyboardLayoutType attribute specifies the layoutType for the * keyboard layout. This can be either empty or one of the following supported layout types: * qwerty, qwertz, azerty, dvorak, colemak, workman, extended, turkish_q, turkish_f. This * attribute is optional and will be used for auto layout selection for external physical * keyboards. *

*/ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_QUERY_KEYBOARD_LAYOUTS = "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS"; /** * Metadata Key: Keyboard layout metadata associated with * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}. *

* Specifies the resource id of a XML resource that describes the keyboard * layouts that are provided by the application. *

*/ public static final String META_DATA_KEYBOARD_LAYOUTS = "android.hardware.input.metadata.KEYBOARD_LAYOUTS"; /** * Prevent touches from being consumed by apps if these touches passed through a non-trusted * window from a different UID and are considered unsafe. * * @hide */ @TestApi @ChangeId public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; /** * Input Event Injection Synchronization Mode: None. * Never blocks. Injection is asynchronous and is assumed always to be successful. * @hide */ public static final int INJECT_INPUT_EVENT_MODE_ASYNC = InputEventInjectionSync.NONE; /** * Input Event Injection Synchronization Mode: Wait for result. * Waits for previous events to be dispatched so that the input dispatcher can * determine whether input event injection will be permitted based on the current * input focus. Does not wait for the input event to finish being handled * by the application. * @hide */ public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = InputEventInjectionSync.WAIT_FOR_RESULT; /** * Input Event Injection Synchronization Mode: Wait for finish. * Waits for the event to be delivered to the application and handled. * @hide */ @UnsupportedAppUsage(trackingBug = 171972397) public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = InputEventInjectionSync.WAIT_FOR_FINISHED; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "SWITCH_STATE_" }, value = { SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON }) public @interface SwitchState {} /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "REMAPPABLE_MODIFIER_KEY_" }, value = { RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_CTRL_LEFT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_CTRL_RIGHT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_META_LEFT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_META_RIGHT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_ALT_LEFT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_ALT_RIGHT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_SHIFT_LEFT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_SHIFT_RIGHT, RemappableModifierKey.REMAPPABLE_MODIFIER_KEY_CAPS_LOCK, }) public @interface RemappableModifierKey { int REMAPPABLE_MODIFIER_KEY_CTRL_LEFT = KeyEvent.KEYCODE_CTRL_LEFT; int REMAPPABLE_MODIFIER_KEY_CTRL_RIGHT = KeyEvent.KEYCODE_CTRL_RIGHT; int REMAPPABLE_MODIFIER_KEY_META_LEFT = KeyEvent.KEYCODE_META_LEFT; int REMAPPABLE_MODIFIER_KEY_META_RIGHT = KeyEvent.KEYCODE_META_RIGHT; int REMAPPABLE_MODIFIER_KEY_ALT_LEFT = KeyEvent.KEYCODE_ALT_LEFT; int REMAPPABLE_MODIFIER_KEY_ALT_RIGHT = KeyEvent.KEYCODE_ALT_RIGHT; int REMAPPABLE_MODIFIER_KEY_SHIFT_LEFT = KeyEvent.KEYCODE_SHIFT_LEFT; int REMAPPABLE_MODIFIER_KEY_SHIFT_RIGHT = KeyEvent.KEYCODE_SHIFT_RIGHT; int REMAPPABLE_MODIFIER_KEY_CAPS_LOCK = KeyEvent.KEYCODE_CAPS_LOCK; } /** * Switch State: Unknown. * * The system has yet to report a valid value for the switch. * @hide */ public static final int SWITCH_STATE_UNKNOWN = -1; /** * Switch State: Off. * @hide */ public static final int SWITCH_STATE_OFF = 0; /** * Switch State: On. * @hide */ public static final int SWITCH_STATE_ON = 1; private final InputManagerGlobal mGlobal; private final Context mContext; /** @hide */ public InputManager(Context context) { mGlobal = InputManagerGlobal.getInstance(); mIm = mGlobal.getInputManagerService(); mContext = context; } /** * Gets an instance of the input manager. * * Warning: The usage of this method is not supported! * * @return The input manager instance. * Use {@link Context#getSystemService(Class)} * to obtain the InputManager instance. * * TODO (b/277717573): Soft remove this API in version V. * TODO (b/277039664): Migrate app usage off this API. * * @hide */ @Deprecated @UnsupportedAppUsage public static InputManager getInstance() { return Objects.requireNonNull(ActivityThread.currentApplication()) .getSystemService(InputManager.class); } /** * Get the current VelocityTracker strategy. * @hide */ public String getVelocityTrackerStrategy() { return mGlobal.getVelocityTrackerStrategy(); } /** * Gets information about the input device with the specified id. * @param id The device id. * @return The input device or null if not found. */ @Nullable public InputDevice getInputDevice(int id) { return mGlobal.getInputDevice(id); } /** * Gets the {@link InputDevice.ViewBehavior} of the input device with a given {@code id}. * *

Use this API to query a fresh view behavior instance whenever the input device * changes. * * @param deviceId the id of the input device whose view behavior is being requested. * @return the view behavior of the input device with the provided id, or {@code null} if there * is not input device with the provided id. */ @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API) @Nullable public InputDevice.ViewBehavior getInputDeviceViewBehavior(int deviceId) { InputDevice device = getInputDevice(deviceId); return device == null ? null : device.getViewBehavior(); } /** * Gets information about the input device with the specified descriptor. * @param descriptor The input device descriptor. * @return The input device or null if not found. * @hide */ public InputDevice getInputDeviceByDescriptor(String descriptor) { return mGlobal.getInputDeviceByDescriptor(descriptor); } /** * Gets the ids of all input devices in the system. * @return The input device ids. */ public int[] getInputDeviceIds() { return mGlobal.getInputDeviceIds(); } /** * Returns true if an input device is enabled. Should return true for most * situations. Some system apps may disable an input device, for * example to prevent unwanted touch events. * * @param id The input device Id. * * @hide */ public boolean isInputDeviceEnabled(int id) { return mGlobal.isInputDeviceEnabled(id); } /** * Enables an InputDevice. *

* Requires {@link android.Manifest.permission#DISABLE_INPUT_DEVICE}. *

* * @param id The input device Id. * * @hide */ public void enableInputDevice(int id) { mGlobal.enableInputDevice(id); } /** * Disables an InputDevice. *

* Requires {@link android.Manifest.permission#DISABLE_INPUT_DEVICE}. *

* * @param id The input device Id. * * @hide */ public void disableInputDevice(int id) { mGlobal.disableInputDevice(id); } /** * Registers an input device listener to receive notifications about when * input devices are added, removed or changed. * * @param listener The listener to register. * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. * * @see #unregisterInputDeviceListener */ public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { mGlobal.registerInputDeviceListener(listener, handler); } /** * Unregisters an input device listener. * * @param listener The listener to unregister. * * @see #registerInputDeviceListener */ public void unregisterInputDeviceListener(InputDeviceListener listener) { mGlobal.unregisterInputDeviceListener(listener); } /** * Queries whether the device is in tablet mode. * * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN}, * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}. * @hide */ @SwitchState public int isInTabletMode() { try { return mIm.isInTabletMode(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Register a tablet mode changed listener. * * @param listener The listener to register. * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. * @hide */ public void registerOnTabletModeChangedListener( OnTabletModeChangedListener listener, Handler handler) { mGlobal.registerOnTabletModeChangedListener(listener, handler); } /** * Unregister a tablet mode changed listener. * * @param listener The listener to unregister. * @hide */ public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) { mGlobal.unregisterOnTabletModeChangedListener(listener); } /** * Queries whether the device's microphone is muted * * @return The mic mute switch state which is one of {@link #SWITCH_STATE_UNKNOWN}, * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}. * @hide */ @SwitchState public int isMicMuted() { try { return mIm.isMicMuted(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Gets information about all supported keyboard layouts. *

* The input manager consults the built-in keyboard layouts as well * as all keyboard layouts advertised by applications using a * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. *

* * @return A list of all supported keyboard layouts. * * @hide */ public KeyboardLayout[] getKeyboardLayouts() { try { return mIm.getKeyboardLayouts(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Returns the descriptors of all supported keyboard layouts. *

* The input manager consults the built-in keyboard layouts as well as all keyboard layouts * advertised by applications using a {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. *

* * @return The ids of all keyboard layouts which are supported by the specified input device. * * @hide */ @TestApi @NonNull @SuppressLint("UnflaggedApi") public List getKeyboardLayoutDescriptors() { KeyboardLayout[] layouts = getKeyboardLayouts(); List res = new ArrayList<>(); for (KeyboardLayout kl : layouts) { res.add(kl.getDescriptor()); } return res; } /** * Returns the layout type of the queried layout *

* The input manager consults the built-in keyboard layouts as well as all keyboard layouts * advertised by applications using a {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. *

* * @param layoutDescriptor The layout descriptor of the queried layout * @return layout type of the queried layout * * @hide */ @TestApi @NonNull public String getKeyboardLayoutTypeForLayoutDescriptor(@NonNull String layoutDescriptor) { KeyboardLayout layout = getKeyboardLayout(layoutDescriptor); return layout == null ? "" : layout.getLayoutType(); } /** * TODO(b/330517633): Cleanup the unsupported API * @hide */ @NonNull public KeyboardLayout[] getKeyboardLayoutsForInputDevice( @NonNull InputDeviceIdentifier identifier) { return new KeyboardLayout[0]; } /** * Gets the keyboard layout with the specified descriptor. * * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by * {@link KeyboardLayout#getDescriptor()}. * @return The keyboard layout, or null if it could not be loaded. * * @hide */ @Nullable public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { if (keyboardLayoutDescriptor == null) { throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); } try { return mIm.getKeyboardLayout(keyboardLayoutDescriptor); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * TODO(b/330517633): Cleanup the unsupported API * @hide */ @Nullable public String getCurrentKeyboardLayoutForInputDevice( @NonNull InputDeviceIdentifier identifier) { return null; } /** * TODO(b/330517633): Cleanup the unsupported API * @hide */ public void setCurrentKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier, @NonNull String keyboardLayoutDescriptor) {} /** * TODO(b/330517633): Cleanup the unsupported API * @hide */ public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { return new String[0]; } /** * TODO(b/330517633): Cleanup the unsupported API * @hide */ public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor) { } /** * TODO(b/330517633): Cleanup the unsupported API * @hide */ @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier, @NonNull String keyboardLayoutDescriptor) { } /** * Remaps modifier keys. Remapping a modifier key to itself will clear any previous remappings * for that key. * * @param fromKey The modifier key getting remapped. * @param toKey The modifier key that it is remapped to. * * @hide */ @TestApi @RequiresPermission(Manifest.permission.REMAP_MODIFIER_KEYS) public void remapModifierKey(@RemappableModifierKey int fromKey, @RemappableModifierKey int toKey) { try { mIm.remapModifierKey(fromKey, toKey); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Clears all existing modifier key remappings * * @hide */ @TestApi @RequiresPermission(Manifest.permission.REMAP_MODIFIER_KEYS) public void clearAllModifierKeyRemappings() { try { mIm.clearAllModifierKeyRemappings(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Provides the current modifier key remapping * * @return a {fromKey, toKey} map that contains the existing modifier key remappings.. * {@link RemappableModifierKey} * * @hide */ @TestApi @NonNull @SuppressWarnings("unchecked") @RequiresPermission(Manifest.permission.REMAP_MODIFIER_KEYS) public Map getModifierKeyRemapping() { try { return mIm.getModifierKeyRemapping(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Gets the TouchCalibration applied to the specified input device's coordinates. * * @param inputDeviceDescriptor The input device descriptor. * @return The TouchCalibration currently assigned for use with the given * input device. If none is set, an identity TouchCalibration is returned. * * @hide */ public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) { try { return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Sets the TouchCalibration to apply to the specified input device's coordinates. *

* This method may have the side-effect of causing the input device in question * to be reconfigured. Requires {@link android.Manifest.permission#SET_INPUT_CALIBRATION}. *

* * @param inputDeviceDescriptor The input device descriptor. * @param calibration The calibration to be applied * * @hide */ public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) { try { mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Gets the keyboard layout descriptor for the specified input device, userId, imeInfo and * imeSubtype. * * @param identifier Identifier for the input device * @param userId user profile ID * @param imeInfo contains IME information like imeId, etc. * @param imeSubtype contains IME subtype information like input languageTag, layoutType, etc. * @return The keyboard layout descriptor, or null if no keyboard layout has been set. * * @hide */ @NonNull public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice( @NonNull InputDeviceIdentifier identifier, @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) { try { return mIm.getKeyboardLayoutForInputDevice(identifier, userId, imeInfo, imeSubtype); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Sets the keyboard layout descriptor for the specified input device, userId, imeInfo and * imeSubtype. * *

* This method may have the side-effect of causing the input device in question to be * reconfigured. *

* * @param identifier The identifier for the input device. * @param userId user profile ID * @param imeInfo contains IME information like imeId, etc. * @param imeSubtype contains IME subtype information like input languageTag, layoutType, etc. * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null. * * @hide */ @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT) public void setKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier, @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype, @NonNull String keyboardLayoutDescriptor) { if (identifier == null) { throw new IllegalArgumentException("identifier must not be null"); } if (keyboardLayoutDescriptor == null) { throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); } try { mIm.setKeyboardLayoutForInputDevice(identifier, userId, imeInfo, imeSubtype, keyboardLayoutDescriptor); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Gets all keyboard layouts that are enabled for the specified input device, userId, imeInfo * and imeSubtype. * * @param identifier The identifier for the input device. * @param userId user profile ID * @param imeInfo contains IME information like imeId, etc. * @param imeSubtype contains IME subtype information like input languageTag, layoutType, etc. * @return The keyboard layout descriptors. * * @hide */ public KeyboardLayout[] getKeyboardLayoutListForInputDevice(InputDeviceIdentifier identifier, @UserIdInt int userId, @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) { if (identifier == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } try { return mIm.getKeyboardLayoutListForInputDevice(identifier, userId, imeInfo, imeSubtype); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Returns the mouse pointer speed. * *

The pointer speed is a value between {@link InputSettings#MIN_POINTER_SPEED} and * {@link InputSettings#MAX_POINTER_SPEED}, the default value being * {@link InputSettings#DEFAULT_POINTER_SPEED}. * *

Note that while setting the mouse pointer speed, it's possible that the input reader has * only received this value and has not yet completed reconfiguring itself with this value. * * @hide */ @SuppressLint("UnflaggedApi") // TestApi without associated feature. @TestApi public int getMousePointerSpeed() { try { return mIm.getMousePointerSpeed(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Changes the mouse pointer speed temporarily, but does not save the setting. *

* Requires {@link android.Manifest.permission#SET_POINTER_SPEED}. *

* * @param speed The pointer speed as a value between {@link InputSettings#MIN_POINTER_SPEED} and * {@link InputSettings#MAX_POINTER_SPEED}, or the default value {@link InputSettings#DEFAULT_POINTER_SPEED}. * * @hide */ public void tryPointerSpeed(int speed) { if (speed < InputSettings.MIN_POINTER_SPEED || speed > InputSettings.MAX_POINTER_SPEED) { throw new IllegalArgumentException("speed out of range"); } try { mIm.tryPointerSpeed(speed); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Returns the maximum allowed obscuring opacity per UID to propagate touches. * *

For certain window types (eg. {@link LayoutParams#TYPE_APPLICATION_OVERLAY}), the decision * of honoring {@link LayoutParams#FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring * opacity of the windows above the touch-consuming window, per UID. Check documentation of * {@link LayoutParams#FLAG_NOT_TOUCHABLE} for more details. * *

The value returned is between 0 (inclusive) and 1 (inclusive). * * @see LayoutParams#FLAG_NOT_TOUCHABLE */ @FloatRange(from = 0, to = 1) public float getMaximumObscuringOpacityForTouch() { return InputSettings.getMaximumObscuringOpacityForTouch(mContext); } /** * Queries the framework about whether any physical keys exist on any currently attached input * devices that are capable of producing the given array of key codes. * * @param keyCodes The array of key codes to query. * @return A new array of the same size as the key codes array whose elements * are set to true if at least one attached keyboard supports the corresponding key code * at the same index in the key codes array. * * @hide */ public boolean[] deviceHasKeys(int[] keyCodes) { return deviceHasKeys(-1, keyCodes); } /** * Queries the framework about whether any physical keys exist on the specified input device * that are capable of producing the given array of key codes. * * @param id The id of the input device to query or -1 to consult all devices. * @param keyCodes The array of key codes to query. * @return A new array of the same size as the key codes array whose elements are set to true * if the given device could produce the corresponding key code at the same index in the key * codes array. * * @hide */ public boolean[] deviceHasKeys(int id, int[] keyCodes) { return mGlobal.deviceHasKeys(id, keyCodes); } /** * Gets the {@link android.view.KeyEvent key code} produced by the given location on a reference * QWERTY keyboard layout. *

* This API is useful for querying the physical location of keys that change the character * produced based on the current locale and keyboard layout. *

* @see InputDevice#getKeyCodeForKeyLocation(int) for examples. * * @param locationKeyCode The location of a key specified as a key code on the QWERTY layout. * This provides a consistent way of referring to the physical location of a key independently * of the current keyboard layout. Also see the * * hypothetical keyboard provided by the W3C, which may be helpful for identifying the * physical location of a key. * @return The key code produced by the key at the specified location, given the current * keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the device does not specify * {@link InputDevice#SOURCE_KEYBOARD} or the requested mapping cannot be determined. * * @hide */ public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) { return mGlobal.getKeyCodeForKeyLocation(deviceId, locationKeyCode); } /** * Provides a Keyboard layout preview of a particular dimension. * * @param keyboardLayout Layout whose preview is requested. If null, will return preview of * the default Keyboard layout defined by {@code Generic.kl}. * @param width Expected width of the drawable * @param height Expected height of the drawable * * NOTE: Width and height will auto-adjust to the width and height of the ImageView that * shows the drawable but this allows the caller to provide an intrinsic width and height of * the drawable allowing the ImageView to properly wrap the drawable content. * * @hide */ @Nullable public Drawable getKeyboardLayoutPreview(@Nullable KeyboardLayout keyboardLayout, int width, int height) { if (!keyboardLayoutPreviewFlag()) { return null; } PhysicalKeyLayout keyLayout = new PhysicalKeyLayout( mGlobal.getKeyCharacterMap(keyboardLayout), keyboardLayout); return new KeyboardLayoutPreviewDrawable(mContext, keyLayout, width, height); } /** * Injects an input event into the event system, targeting windows owned by the provided uid. * * If a valid targetUid is provided, the system will only consider injecting the input event * into windows owned by the provided uid. If the input event is targeted at a window that is * not owned by the provided uid, input injection will fail and a RemoteException will be * thrown. * * The synchronization mode determines whether the method blocks while waiting for * input injection to proceed. *

* Requires the {@link android.Manifest.permission#INJECT_EVENTS} permission. *

* Make sure you correctly set the event time and input source of the event * before calling this method. *

* * @param event The event to inject. * @param mode The synchronization mode. One of: * {@link android.os.InputEventInjectionSync#NONE}, * {@link android.os.InputEventInjectionSync#WAIT_FOR_RESULT}, or * {@link android.os.InputEventInjectionSync#WAIT_FOR_FINISHED}. * @param targetUid The uid to target, or {@link android.os.Process#INVALID_UID} to target all * windows. * @return True if input event injection succeeded. * * @hide */ @RequiresPermission(Manifest.permission.INJECT_EVENTS) public boolean injectInputEvent(InputEvent event, int mode, int targetUid) { return mGlobal.injectInputEvent(event, mode, targetUid); } /** * Injects an input event into the event system on behalf of an application. * The synchronization mode determines whether the method blocks while waiting for * input injection to proceed. *

* Requires the {@link android.Manifest.permission#INJECT_EVENTS} permission. *

* Make sure you correctly set the event time and input source of the event * before calling this method. *

* * @param event The event to inject. * @param mode The synchronization mode. One of: * {@link android.os.InputEventInjectionSync#NONE}, * {@link android.os.InputEventInjectionSync#WAIT_FOR_RESULT}, or * {@link android.os.InputEventInjectionSync#WAIT_FOR_FINISHED}. * @return True if input event injection succeeded. * * @hide */ @RequiresPermission(Manifest.permission.INJECT_EVENTS) @UnsupportedAppUsage public boolean injectInputEvent(InputEvent event, int mode) { return mGlobal.injectInputEvent(event, mode); } /** * Verify the details of an {@link android.view.InputEvent} that came from the system. * If the event did not come from the system, or its details could not be verified, then this * will return {@code null}. Receiving {@code null} does not mean that the event did not * originate from the system, just that we were unable to verify it. This can * happen for a number of reasons during normal operation. * * @param event The {@link android.view.InputEvent} to check * * @return {@link android.view.VerifiedInputEvent}, which is a subset of the provided * {@link android.view.InputEvent} * {@code null} if the event could not be verified. */ @Nullable public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) { try { return mIm.verifyInputEvent(event); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * This method exists for backwards-compatibility, and is a no-op. * * @deprecated * @hide */ @UnsupportedAppUsage public void setPointerIconType(int iconId) { Log.e(TAG, "setPointerIcon: Unsupported app usage!"); } /** @hide */ public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId, IBinder inputToken) { return mGlobal.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken); } /** * Check if showing a {@link android.view.PointerIcon} for styluses is enabled. * * @return true if a pointer icon will be shown over the location of a * stylus pointer, false if there is no pointer icon shown for styluses. */ public boolean isStylusPointerIconEnabled() { if (mIsStylusPointerIconEnabled == null) { mIsStylusPointerIconEnabled = InputSettings.isStylusPointerIconEnabled(mContext); } return mIsStylusPointerIconEnabled; } /** * Request or release pointer capture. *

* When in capturing mode, the pointer icon disappears and all mouse events are dispatched to * the window which has requested the capture. Relative position changes are available through * {@link MotionEvent#getX} and {@link MotionEvent#getY}. * * @param enable true when requesting pointer capture, false when releasing. * * @hide */ public void requestPointerCapture(IBinder windowToken, boolean enable) { mGlobal.requestPointerCapture(windowToken, enable); } /** * Monitor input on the specified display for gestures. * * @hide */ public InputMonitor monitorGestureInput(String name, int displayId) { return mGlobal.monitorGestureInput(name, displayId); } /** * Add a runtime association between the input port and the display port. This overrides any * static associations. * @param inputPort the port of the input device * @param displayPort the physical port of the associated display *

* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. *

* @hide */ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY) public void addPortAssociation(@NonNull String inputPort, int displayPort) { try { mIm.addPortAssociation(inputPort, displayPort); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Remove the runtime association between the input port and the display port. Any existing * static association for the cleared input port will be restored. * @param inputPort the port of the input device to be cleared *

* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. *

* @hide */ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY) public void removePortAssociation(@NonNull String inputPort) { try { mIm.removePortAssociation(inputPort); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Add a runtime association between the input port and display, by unique id. Input ports are * expected to be unique. * @param inputPort the port of the input device * @param displayUniqueId the unique id of the associated display *

* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. *

* @hide */ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY) @TestApi public void addUniqueIdAssociationByPort(@NonNull String inputPort, @NonNull String displayUniqueId) { mGlobal.addUniqueIdAssociationByPort(inputPort, displayUniqueId); } /** * Removes a runtime association between the input device and display. * @param inputPort the port of the input device *

* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. *

* @hide */ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY) @TestApi public void removeUniqueIdAssociationByPort(@NonNull String inputPort) { mGlobal.removeUniqueIdAssociationByPort(inputPort); } /** * Add a runtime association between the input device name and display, by descriptor. Input * device descriptors are expected to be unique per physical device, though one physical * device can have multiple virtual input devices that possess the same descriptor. * E.g. a keyboard with built in trackpad will be 2 different input devices with the same * descriptor. * @param inputDeviceDescriptor the descriptor of the input device * @param displayUniqueId the unique id of the associated display *

* Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. *

* @hide */ @FlaggedApi(FLAG_DEVICE_ASSOCIATIONS) @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY) @TestApi public void addUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor, @NonNull String displayUniqueId) { mGlobal.addUniqueIdAssociationByDescriptor(inputDeviceDescriptor, displayUniqueId); } /** * Removes a runtime association between the input device and display. } /** * Removes a runtime association between the input device and display. * @param inputDeviceDescriptor the descriptor of the input device *

* Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. *

* @hide */ @FlaggedApi(FLAG_DEVICE_ASSOCIATIONS) @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY) @TestApi public void removeUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor) { mGlobal.removeUniqueIdAssociationByDescriptor(inputDeviceDescriptor); } /** * Reports the version of the Universal Stylus Initiative (USI) protocol supported by the given * display, if any. * * @return the USI version supported by the display, or null if the device does not support USI * @see Universal Stylus Initiative */ @Nullable public HostUsiVersion getHostUsiVersion(@NonNull Display display) { return mGlobal.getHostUsiVersion(display); } /** * Returns the Bluetooth address of this input device, if known. * * The returned string is always null if this input device is not connected * via Bluetooth, or if the Bluetooth address of the device cannot be * determined. The returned address will look like: "11:22:33:44:55:66". * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) @Nullable public String getInputDeviceBluetoothAddress(int deviceId) { return mGlobal.getInputDeviceBluetoothAddress(deviceId); } /** * Gets a vibrator service associated with an input device, always creates a new instance. * @return The vibrator, never null. * @hide */ public Vibrator getInputDeviceVibrator(int deviceId, int vibratorId) { return new InputDeviceVibrator(deviceId, vibratorId); } /** * Cancel all ongoing pointer gestures on all displays. * @hide */ public void cancelCurrentTouch() { mGlobal.cancelCurrentTouch(); } /** * Pilfer pointers from an input channel. * * Takes all the current pointer event streams that are currently being sent to the given * input channel and generates appropriate cancellations for all other windows that are * receiving these pointers. * * This API is intended to be used in conjunction with spy windows. When a spy window pilfers * pointers, the foreground windows and all other spy windows that are receiving any of the * pointers that are currently being dispatched to the pilfering window will have those pointers * canceled. Only the pilfering window will continue to receive events for the affected pointers * until the pointer is lifted. * * This method should be used with caution as unexpected pilfering can break fundamental user * interactions. * * @see android.os.InputConfig#SPY * @hide */ @RequiresPermission(Manifest.permission.MONITOR_INPUT) public void pilferPointers(IBinder inputChannelToken) { mGlobal.pilferPointers(inputChannelToken); } /** * Adds a battery listener to be notified about {@link BatteryState} changes for an input * device. The same listener can be registered for multiple input devices. * The listener will be notified of the initial battery state of the device after it is * successfully registered. * @param deviceId the input device that should be monitored * @param executor an executor on which the callback will be called * @param listener the {@link InputDeviceBatteryListener} * @see #removeInputDeviceBatteryListener(int, InputDeviceBatteryListener) * @hide */ public void addInputDeviceBatteryListener(int deviceId, @NonNull Executor executor, @NonNull InputDeviceBatteryListener listener) { mGlobal.addInputDeviceBatteryListener(deviceId, executor, listener); } /** * Removes a previously registered battery listener for an input device. * @see #addInputDeviceBatteryListener(int, Executor, InputDeviceBatteryListener) * @hide */ public void removeInputDeviceBatteryListener(int deviceId, @NonNull InputDeviceBatteryListener listener) { mGlobal.removeInputDeviceBatteryListener(deviceId, listener); } /** * Whether there is a gesture-compatible touchpad connected to the device. * @hide */ public boolean areTouchpadGesturesAvailable(@NonNull Context context) { // TODO: implement the right logic return true; } /** * Registers a Keyboard backlight change listener to be notified about {@link * KeyboardBacklightState} changes for connected keyboard devices. * * @param executor an executor on which the callback will be called * @param listener the {@link KeyboardBacklightListener} * @hide * @see #unregisterKeyboardBacklightListener(KeyboardBacklightListener) * @throws IllegalArgumentException if {@code listener} has already been registered previously. * @throws NullPointerException if {@code listener} or {@code executor} is null. */ @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT) public void registerKeyboardBacklightListener(@NonNull Executor executor, @NonNull KeyboardBacklightListener listener) throws IllegalArgumentException { mGlobal.registerKeyboardBacklightListener(executor, listener); } /** * Unregisters a previously added Keyboard backlight change listener. * * @param listener the {@link KeyboardBacklightListener} * @see #registerKeyboardBacklightListener(Executor, KeyboardBacklightListener) * @hide */ @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT) public void unregisterKeyboardBacklightListener( @NonNull KeyboardBacklightListener listener) { mGlobal.unregisterKeyboardBacklightListener(listener); } /** * Registers a Sticky modifier state change listener to be notified about {@link * StickyModifierState} changes. * * @param executor an executor on which the callback will be called * @param listener the {@link StickyModifierStateListener} * @throws IllegalArgumentException if {@code listener} has already been registered previously. * @throws NullPointerException if {@code listener} or {@code executor} is null. * @hide * @see #unregisterStickyModifierStateListener(StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) public void registerStickyModifierStateListener(@NonNull Executor executor, @NonNull StickyModifierStateListener listener) throws IllegalArgumentException { if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) { return; } mGlobal.registerStickyModifierStateListener(executor, listener); } /** * Unregisters a previously added Sticky modifier state change listener. * * @param listener the {@link StickyModifierStateListener} * @hide * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) public void unregisterStickyModifierStateListener( @NonNull StickyModifierStateListener listener) { if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) { return; } mGlobal.unregisterStickyModifierStateListener(listener); } /** * A callback used to be notified about battery state changes for an input device. The * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the * listener is successfully registered to provide the initial battery state of the device. * @see InputDevice#getBatteryState() * @see #addInputDeviceBatteryListener(int, Executor, InputDeviceBatteryListener) * @see #removeInputDeviceBatteryListener(int, InputDeviceBatteryListener) * @hide */ public interface InputDeviceBatteryListener { /** * Called when the battery state of an input device changes. * @param deviceId the input device for which the battery changed. * @param eventTimeMillis the time (in ms) when the battery change took place. * This timestamp is in the {@link SystemClock#uptimeMillis()} time base. * @param batteryState the new battery state, never null. */ void onBatteryStateChanged( int deviceId, long eventTimeMillis, @NonNull BatteryState batteryState); } /** * Listens for changes in input devices. */ public interface InputDeviceListener { /** * Called whenever an input device has been added to the system. * Use {@link #getInputDevice(int)} to get more information about the device. * * @param deviceId The id of the input device that was added. */ void onInputDeviceAdded(int deviceId); /** * Called whenever an input device has been removed from the system. * * @param deviceId The id of the input device that was removed. */ void onInputDeviceRemoved(int deviceId); /** * Called whenever the properties of an input device have changed since they * were last queried. Use {@link InputManager#getInputDevice} to get * a fresh {@link InputDevice} object with the new properties. * * @param deviceId The id of the input device that changed. */ void onInputDeviceChanged(int deviceId); } /** @hide */ public interface OnTabletModeChangedListener { /** * Called whenever the device goes into or comes out of tablet mode. * * @param whenNanos The time at which the device transitioned into or * out of tablet mode. This is given in nanoseconds in the * {@link SystemClock#uptimeMillis} time base. */ void onTabletModeChanged(long whenNanos, boolean inTabletMode); } /** * A callback used to be notified about keyboard backlight state changes for keyboard device. * The {@link #onKeyboardBacklightChanged(int, KeyboardBacklightState, boolean)} method * will be called once after the listener is successfully registered to provide the initial * keyboard backlight state of the device. * @see #registerKeyboardBacklightListener(Executor, KeyboardBacklightListener) * @see #unregisterKeyboardBacklightListener(KeyboardBacklightListener) * @hide */ public interface KeyboardBacklightListener { /** * Called when the keyboard backlight brightness level changes. * @param deviceId the keyboard for which the backlight brightness changed. * @param state the new keyboard backlight state, never null. * @param isTriggeredByKeyPress whether brightness change was triggered by the user * pressing up/down key on the keyboard. */ void onKeyboardBacklightChanged( int deviceId, @NonNull KeyboardBacklightState state, boolean isTriggeredByKeyPress); } /** * A callback used to be notified about sticky modifier state changes when A11y Sticky keys * feature is enabled. * * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener) * @see #unregisterStickyModifierStateListener(StickyModifierStateListener) * @hide */ public interface StickyModifierStateListener { /** * Called when the sticky modifier state changes. * This method will be called once after the listener is successfully registered to provide * the initial modifier state. * * @param state the new sticky modifier state, never null. */ void onStickyModifierStateChanged(@NonNull StickyModifierState state); } }