| /* |
| * 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 android.Manifest; |
| 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.SystemService; |
| import android.annotation.TestApi; |
| import android.app.ActivityThread; |
| import android.compat.annotation.ChangeId; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.Context; |
| import android.hardware.SensorManager; |
| import android.hardware.lights.Light; |
| import android.hardware.lights.LightState; |
| import android.hardware.lights.LightsManager; |
| import android.hardware.lights.LightsRequest; |
| import android.os.BlockUntrustedTouchesMode; |
| import android.os.Build; |
| import android.os.CombinedVibration; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IVibratorStateListener; |
| import android.os.InputEventInjectionSync; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.ServiceManager.ServiceNotFoundException; |
| import android.os.SystemClock; |
| import android.os.VibrationEffect; |
| import android.os.Vibrator; |
| import android.os.VibratorManager; |
| import android.provider.Settings; |
| import android.provider.Settings.SettingNotFoundException; |
| import android.util.Log; |
| import android.util.SparseArray; |
| import android.view.InputDevice; |
| import android.view.InputEvent; |
| import android.view.InputMonitor; |
| import android.view.MotionEvent; |
| import android.view.PointerIcon; |
| import android.view.VerifiedInputEvent; |
| import android.view.WindowManager.LayoutParams; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.os.SomeArgs; |
| import com.android.internal.util.ArrayUtils; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Provides information about input devices and available key layouts. |
| */ |
| @SystemService(Context.INPUT_SERVICE) |
| public final class InputManager { |
| private static final String TAG = "InputManager"; |
| private static final boolean DEBUG = false; |
| |
| private static final int MSG_DEVICE_ADDED = 1; |
| private static final int MSG_DEVICE_REMOVED = 2; |
| private static final int MSG_DEVICE_CHANGED = 3; |
| |
| /** @hide */ |
| public static final int[] BLOCK_UNTRUSTED_TOUCHES_MODES = { |
| BlockUntrustedTouchesMode.DISABLED, |
| BlockUntrustedTouchesMode.PERMISSIVE, |
| BlockUntrustedTouchesMode.BLOCK |
| }; |
| |
| private static InputManager sInstance; |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| private final IInputManager mIm; |
| |
| // Guarded by mInputDevicesLock |
| private final Object mInputDevicesLock = new Object(); |
| private SparseArray<InputDevice> mInputDevices; |
| private InputDevicesChangedListener mInputDevicesChangedListener; |
| private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = |
| new ArrayList<InputDeviceListenerDelegate>(); |
| |
| // Guarded by mTabletModeLock |
| private final Object mTabletModeLock = new Object(); |
| private TabletModeChangedListener mTabletModeChangedListener; |
| private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners; |
| |
| private InputDeviceSensorManager mInputDeviceSensorManager; |
| /** |
| * Broadcast Action: Query available keyboard layouts. |
| * <p> |
| * 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. |
| * </p><p> |
| * 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. |
| * <pre><code> |
| * <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> |
| * </code></pre> |
| * </p><p> |
| * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to |
| * an XML resource whose root element is <code><keyboard-layouts></code> that |
| * contains zero or more <code><keyboard-layout></code> elements. |
| * Each <code><keyboard-layout></code> 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. |
| * <pre><code> |
| * <?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> |
| * </pre></code> |
| * </p><p> |
| * The <code>android:name</code> attribute specifies an identifier by which |
| * the keyboard layout will be known in the package. |
| * The <code>android:label</code> attribute specifies a human-readable descriptive |
| * label to describe the keyboard layout in the user interface, such as "English (US)". |
| * The <code>android:keyboardLayout</code> attribute refers to a |
| * <a href="http://source.android.com/tech/input/key-character-map-files.html"> |
| * key character map</a> resource that defines the keyboard layout. |
| * </p> |
| */ |
| @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}. |
| * <p> |
| * Specifies the resource id of a XML resource that describes the keyboard |
| * layouts that are provided by the application. |
| * </p> |
| */ |
| public static final String META_DATA_KEYBOARD_LAYOUTS = |
| "android.hardware.input.metadata.KEYBOARD_LAYOUTS"; |
| |
| /** |
| * Pointer Speed: The minimum (slowest) pointer speed (-7). |
| * @hide |
| */ |
| public static final int MIN_POINTER_SPEED = -7; |
| |
| /** |
| * Pointer Speed: The maximum (fastest) pointer speed (7). |
| * @hide |
| */ |
| public static final int MAX_POINTER_SPEED = 7; |
| |
| /** |
| * Pointer Speed: The default pointer speed (0). |
| * @hide |
| */ |
| public static final int DEFAULT_POINTER_SPEED = 0; |
| |
| /** |
| * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1). |
| * @hide |
| */ |
| public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f; |
| |
| /** |
| * Default mode of the block untrusted touches mode feature. |
| * @hide |
| */ |
| @BlockUntrustedTouchesMode |
| public static final int DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE = |
| BlockUntrustedTouchesMode.BLOCK; |
| |
| /** |
| * 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; |
| |
| /** |
| * Check whether apps are using FLAG_SLIPPERY for their windows. We expect that this flag is |
| * only used by the system components. If so, we can lock it down. |
| * @hide |
| */ |
| @ChangeId |
| public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY; |
| |
| /** |
| * 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 {} |
| |
| /** |
| * 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 InputManager(IInputManager im) { |
| mIm = im; |
| } |
| |
| /** |
| * Gets an instance of the input manager. |
| * |
| * @return The input manager instance. |
| * |
| * @hide |
| */ |
| @VisibleForTesting |
| public static InputManager resetInstance(IInputManager inputManagerService) { |
| synchronized (InputManager.class) { |
| sInstance = new InputManager(inputManagerService); |
| return sInstance; |
| } |
| } |
| |
| /** |
| * Clear the instance of the input manager. |
| * |
| * @hide |
| */ |
| @VisibleForTesting |
| public static void clearInstance() { |
| synchronized (InputManager.class) { |
| sInstance = null; |
| } |
| } |
| |
| /** |
| * Gets an instance of the input manager. |
| * |
| * @return The input manager instance. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public static InputManager getInstance() { |
| synchronized (InputManager.class) { |
| if (sInstance == null) { |
| try { |
| sInstance = new InputManager(IInputManager.Stub |
| .asInterface(ServiceManager.getServiceOrThrow(Context.INPUT_SERVICE))); |
| } catch (ServiceNotFoundException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| return sInstance; |
| } |
| } |
| |
| /** |
| * Gets information about the input device with the specified id. |
| * @param id The device id. |
| * @return The input device or null if not found. |
| */ |
| public InputDevice getInputDevice(int id) { |
| synchronized (mInputDevicesLock) { |
| populateInputDevicesLocked(); |
| |
| int index = mInputDevices.indexOfKey(id); |
| if (index < 0) { |
| return null; |
| } |
| |
| InputDevice inputDevice = mInputDevices.valueAt(index); |
| if (inputDevice == null) { |
| try { |
| inputDevice = mIm.getInputDevice(id); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| if (inputDevice != null) { |
| mInputDevices.setValueAt(index, inputDevice); |
| } |
| } |
| return inputDevice; |
| } |
| } |
| |
| /** |
| * 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) { |
| if (descriptor == null) { |
| throw new IllegalArgumentException("descriptor must not be null."); |
| } |
| |
| synchronized (mInputDevicesLock) { |
| populateInputDevicesLocked(); |
| |
| int numDevices = mInputDevices.size(); |
| for (int i = 0; i < numDevices; i++) { |
| InputDevice inputDevice = mInputDevices.valueAt(i); |
| if (inputDevice == null) { |
| int id = mInputDevices.keyAt(i); |
| try { |
| inputDevice = mIm.getInputDevice(id); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| if (inputDevice == null) { |
| continue; |
| } |
| mInputDevices.setValueAt(i, inputDevice); |
| } |
| if (descriptor.equals(inputDevice.getDescriptor())) { |
| return inputDevice; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * Gets the ids of all input devices in the system. |
| * @return The input device ids. |
| */ |
| public int[] getInputDeviceIds() { |
| synchronized (mInputDevicesLock) { |
| populateInputDevicesLocked(); |
| |
| final int count = mInputDevices.size(); |
| final int[] ids = new int[count]; |
| for (int i = 0; i < count; i++) { |
| ids[i] = mInputDevices.keyAt(i); |
| } |
| return ids; |
| } |
| } |
| |
| /** |
| * 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) { |
| try { |
| return mIm.isInputDeviceEnabled(id); |
| } catch (RemoteException ex) { |
| Log.w(TAG, "Could not check enabled status of input device with id = " + id); |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Enables an InputDevice. |
| * <p> |
| * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}. |
| * </p> |
| * |
| * @param id The input device Id. |
| * |
| * @hide |
| */ |
| public void enableInputDevice(int id) { |
| try { |
| mIm.enableInputDevice(id); |
| } catch (RemoteException ex) { |
| Log.w(TAG, "Could not enable input device with id = " + id); |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Disables an InputDevice. |
| * <p> |
| * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}. |
| * </p> |
| * |
| * @param id The input device Id. |
| * |
| * @hide |
| */ |
| public void disableInputDevice(int id) { |
| try { |
| mIm.disableInputDevice(id); |
| } catch (RemoteException ex) { |
| Log.w(TAG, "Could not disable input device with id = " + id); |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * 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) { |
| if (listener == null) { |
| throw new IllegalArgumentException("listener must not be null"); |
| } |
| |
| synchronized (mInputDevicesLock) { |
| populateInputDevicesLocked(); |
| int index = findInputDeviceListenerLocked(listener); |
| if (index < 0) { |
| mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler)); |
| } |
| } |
| } |
| |
| /** |
| * Unregisters an input device listener. |
| * |
| * @param listener The listener to unregister. |
| * |
| * @see #registerInputDeviceListener |
| */ |
| public void unregisterInputDeviceListener(InputDeviceListener listener) { |
| if (listener == null) { |
| throw new IllegalArgumentException("listener must not be null"); |
| } |
| |
| synchronized (mInputDevicesLock) { |
| int index = findInputDeviceListenerLocked(listener); |
| if (index >= 0) { |
| InputDeviceListenerDelegate d = mInputDeviceListeners.get(index); |
| d.removeCallbacksAndMessages(null); |
| mInputDeviceListeners.remove(index); |
| } |
| } |
| } |
| |
| private int findInputDeviceListenerLocked(InputDeviceListener listener) { |
| final int numListeners = mInputDeviceListeners.size(); |
| for (int i = 0; i < numListeners; i++) { |
| if (mInputDeviceListeners.get(i).mListener == listener) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * 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) { |
| if (listener == null) { |
| throw new IllegalArgumentException("listener must not be null"); |
| } |
| synchronized (mTabletModeLock) { |
| if (mOnTabletModeChangedListeners == null) { |
| initializeTabletModeListenerLocked(); |
| } |
| int idx = findOnTabletModeChangedListenerLocked(listener); |
| if (idx < 0) { |
| OnTabletModeChangedListenerDelegate d = |
| new OnTabletModeChangedListenerDelegate(listener, handler); |
| mOnTabletModeChangedListeners.add(d); |
| } |
| } |
| } |
| |
| /** |
| * Unregister a tablet mode changed listener. |
| * |
| * @param listener The listener to unregister. |
| * @hide |
| */ |
| public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) { |
| if (listener == null) { |
| throw new IllegalArgumentException("listener must not be null"); |
| } |
| synchronized (mTabletModeLock) { |
| int idx = findOnTabletModeChangedListenerLocked(listener); |
| if (idx >= 0) { |
| OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx); |
| d.removeCallbacksAndMessages(null); |
| } |
| } |
| } |
| |
| private void initializeTabletModeListenerLocked() { |
| final TabletModeChangedListener listener = new TabletModeChangedListener(); |
| try { |
| mIm.registerTabletModeChangedListener(listener); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| mTabletModeChangedListener = listener; |
| mOnTabletModeChangedListeners = new ArrayList<>(); |
| } |
| |
| private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) { |
| final int N = mOnTabletModeChangedListeners.size(); |
| for (int i = 0; i < N; i++) { |
| if (mOnTabletModeChangedListeners.get(i).mListener == listener) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * 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. |
| * <p> |
| * 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. |
| * </p> |
| * |
| * @return A list of all supported keyboard layouts. |
| * |
| * @hide |
| */ |
| public KeyboardLayout[] getKeyboardLayouts() { |
| try { |
| return mIm.getKeyboardLayouts(); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Gets information about all supported keyboard layouts appropriate |
| * for a specific input device. |
| * <p> |
| * 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. |
| * </p> |
| * |
| * @return A list of all supported keyboard layouts for a specific |
| * input device. |
| * |
| * @hide |
| */ |
| public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { |
| try { |
| return mIm.getKeyboardLayoutsForInputDevice(identifier); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * 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 |
| */ |
| 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(); |
| } |
| } |
| |
| /** |
| * Gets the current keyboard layout descriptor for the specified input |
| * device. |
| * |
| * @param identifier Identifier for the input device |
| * @return The keyboard layout descriptor, or null if no keyboard layout has |
| * been set. |
| * @hide |
| */ |
| public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { |
| try { |
| return mIm.getCurrentKeyboardLayoutForInputDevice(identifier); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Sets the current keyboard layout descriptor for the specified input |
| * device. |
| * <p> |
| * This method may have the side-effect of causing the input device in |
| * question to be reconfigured. |
| * </p> |
| * |
| * @param identifier The identifier for the input device. |
| * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, |
| * must not be null. |
| * @hide |
| */ |
| public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, |
| 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.setCurrentKeyboardLayoutForInputDevice(identifier, |
| keyboardLayoutDescriptor); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Gets all keyboard layout descriptors that are enabled for the specified |
| * input device. |
| * |
| * @param identifier The identifier for the input device. |
| * @return The keyboard layout descriptors. |
| * @hide |
| */ |
| public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { |
| if (identifier == null) { |
| throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); |
| } |
| |
| try { |
| return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Adds the keyboard layout descriptor for the specified input device. |
| * <p> |
| * This method may have the side-effect of causing the input device in |
| * question to be reconfigured. |
| * </p> |
| * |
| * @param identifier The identifier for the input device. |
| * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to |
| * add. |
| * @hide |
| */ |
| public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, |
| String keyboardLayoutDescriptor) { |
| if (identifier == null) { |
| throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); |
| } |
| if (keyboardLayoutDescriptor == null) { |
| throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); |
| } |
| |
| try { |
| mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Removes the keyboard layout descriptor for the specified input device. |
| * <p> |
| * This method may have the side-effect of causing the input device in |
| * question to be reconfigured. |
| * </p> |
| * |
| * @param identifier The identifier for the input device. |
| * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to |
| * remove. |
| * @hide |
| */ |
| public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, |
| String keyboardLayoutDescriptor) { |
| if (identifier == null) { |
| throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); |
| } |
| if (keyboardLayoutDescriptor == null) { |
| throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); |
| } |
| |
| try { |
| mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); |
| } 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. |
| * <p> |
| * This method may have the side-effect of causing the input device in question |
| * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}. |
| * </p> |
| * |
| * @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 mouse pointer speed. |
| * <p> |
| * Only returns the permanent mouse pointer speed. Ignores any temporary pointer |
| * speed set by {@link #tryPointerSpeed}. |
| * </p> |
| * |
| * @param context The application context. |
| * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and |
| * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. |
| * |
| * @hide |
| */ |
| public int getPointerSpeed(Context context) { |
| int speed = DEFAULT_POINTER_SPEED; |
| try { |
| speed = Settings.System.getInt(context.getContentResolver(), |
| Settings.System.POINTER_SPEED); |
| } catch (SettingNotFoundException snfe) { |
| } |
| return speed; |
| } |
| |
| /** |
| * Sets the mouse pointer speed. |
| * <p> |
| * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}. |
| * </p> |
| * |
| * @param context The application context. |
| * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and |
| * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. |
| * |
| * @hide |
| */ |
| public void setPointerSpeed(Context context, int speed) { |
| if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { |
| throw new IllegalArgumentException("speed out of range"); |
| } |
| |
| Settings.System.putInt(context.getContentResolver(), |
| Settings.System.POINTER_SPEED, speed); |
| } |
| |
| /** |
| * Changes the mouse pointer speed temporarily, but does not save the setting. |
| * <p> |
| * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}. |
| * </p> |
| * |
| * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and |
| * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. |
| * |
| * @hide |
| */ |
| public void tryPointerSpeed(int speed) { |
| if (speed < MIN_POINTER_SPEED || speed > 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. |
| * |
| * <p>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. |
| * |
| * <p>The value returned is between 0 (inclusive) and 1 (inclusive). |
| * |
| * @see LayoutParams#FLAG_NOT_TOUCHABLE |
| */ |
| @FloatRange(from = 0, to = 1) |
| public float getMaximumObscuringOpacityForTouch() { |
| Context context = ActivityThread.currentApplication(); |
| return Settings.Global.getFloat(context.getContentResolver(), |
| Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, |
| DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH); |
| } |
| |
| /** |
| * Sets the maximum allowed obscuring opacity by UID to propagate touches. |
| * |
| * <p>For certain window types (eg. SAWs), 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. |
| * |
| * <p>For a certain UID: |
| * <ul> |
| * <li>If it's the same as the UID of the touch-consuming window, allow it to propagate |
| * the touch. |
| * <li>Otherwise take all its windows of eligible window types above the touch-consuming |
| * window, compute their combined obscuring opacity considering that {@code |
| * opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is |
| * lesser than or equal to this setting and there are no other windows preventing the |
| * touch, allow the UID to propagate the touch. |
| * </ul> |
| * |
| * <p>This value should be between 0 (inclusive) and 1 (inclusive). |
| * |
| * @see #getMaximumObscuringOpacityForTouch() |
| * |
| * @hide |
| */ |
| @TestApi |
| @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) |
| public void setMaximumObscuringOpacityForTouch(@FloatRange(from = 0, to = 1) float opacity) { |
| if (opacity < 0 || opacity > 1) { |
| throw new IllegalArgumentException( |
| "Maximum obscuring opacity for touch should be >= 0 and <= 1"); |
| } |
| Context context = ActivityThread.currentApplication(); |
| Settings.Global.putFloat(context.getContentResolver(), |
| Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity); |
| } |
| |
| /** |
| * Returns the current mode of the block untrusted touches feature, one of: |
| * <ul> |
| * <li>{@link BlockUntrustedTouchesMode#DISABLED} |
| * <li>{@link BlockUntrustedTouchesMode#PERMISSIVE} |
| * <li>{@link BlockUntrustedTouchesMode#BLOCK} |
| * </ul> |
| * |
| * @hide |
| */ |
| @TestApi |
| @BlockUntrustedTouchesMode |
| public int getBlockUntrustedTouchesMode(@NonNull Context context) { |
| int mode = Settings.Global.getInt(context.getContentResolver(), |
| Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE); |
| if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) { |
| Log.w(TAG, "Unknown block untrusted touches feature mode " + mode + ", using " |
| + "default " + DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE); |
| return DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE; |
| } |
| return mode; |
| } |
| |
| /** |
| * Sets the mode of the block untrusted touches feature to one of: |
| * <ul> |
| * <li>{@link BlockUntrustedTouchesMode#DISABLED} |
| * <li>{@link BlockUntrustedTouchesMode#PERMISSIVE} |
| * <li>{@link BlockUntrustedTouchesMode#BLOCK} |
| * </ul> |
| * |
| * @hide |
| */ |
| @TestApi |
| @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) |
| public void setBlockUntrustedTouchesMode(@NonNull Context context, |
| @BlockUntrustedTouchesMode int mode) { |
| if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) { |
| throw new IllegalArgumentException("Invalid feature mode " + mode); |
| } |
| Settings.Global.putInt(context.getContentResolver(), |
| Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, mode); |
| } |
| |
| /** |
| * Queries the framework about whether any physical keys exist on the |
| * any keyboard attached to the device 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 |
| * any keyboard attached to the device that are capable of producing the given |
| * array of key codes. |
| * |
| * @param id The id of the device to query. |
| * @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) { |
| boolean[] ret = new boolean[keyCodes.length]; |
| try { |
| mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| return ret; |
| } |
| |
| |
| /** |
| * 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. |
| * <p> |
| * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into |
| * windows that are owned by other applications. |
| * </p><p> |
| * Make sure you correctly set the event time and input source of the event |
| * before calling this method. |
| * </p> |
| * |
| * @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 |
| */ |
| @UnsupportedAppUsage |
| public boolean injectInputEvent(InputEvent event, int mode) { |
| if (event == null) { |
| throw new IllegalArgumentException("event must not be null"); |
| } |
| if (mode != InputEventInjectionSync.NONE |
| && mode != InputEventInjectionSync.WAIT_FOR_FINISHED |
| && mode != InputEventInjectionSync.WAIT_FOR_RESULT) { |
| throw new IllegalArgumentException("mode is invalid"); |
| } |
| |
| try { |
| return mIm.injectInputEvent(event, mode); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| public @Nullable VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) { |
| try { |
| return mIm.verifyInputEvent(event); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Changes the mouse pointer's icon shape into the specified id. |
| * |
| * @param iconId The id of the pointer graphic, as a value between |
| * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_GRABBING}. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public void setPointerIconType(int iconId) { |
| try { |
| mIm.setPointerIconType(iconId); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void setCustomPointerIcon(PointerIcon icon) { |
| try { |
| mIm.setCustomPointerIcon(icon); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Request or release pointer capture. |
| * <p> |
| * 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) { |
| try { |
| mIm.requestPointerCapture(windowToken, enable); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Monitor input on the specified display for gestures. |
| * |
| * @hide |
| */ |
| public InputMonitor monitorGestureInput(String name, int displayId) { |
| try { |
| return mIm.monitorGestureInput(name, displayId); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Get sensors information as list. |
| * |
| * @hide |
| */ |
| public InputSensorInfo[] getSensorList(int deviceId) { |
| try { |
| return mIm.getSensorList(deviceId); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Enable input device sensor |
| * |
| * @hide |
| */ |
| public boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs, |
| int maxBatchReportLatencyUs) { |
| try { |
| return mIm.enableSensor(deviceId, sensorType, samplingPeriodUs, |
| maxBatchReportLatencyUs); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Enable input device sensor |
| * |
| * @hide |
| */ |
| public void disableSensor(int deviceId, int sensorType) { |
| try { |
| mIm.disableSensor(deviceId, sensorType); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Flush input device sensor |
| * |
| * @hide |
| */ |
| public boolean flushSensor(int deviceId, int sensorType) { |
| try { |
| return mIm.flushSensor(deviceId, sensorType); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Register input device sensor listener |
| * |
| * @hide |
| */ |
| public boolean registerSensorListener(IInputSensorEventListener listener) { |
| try { |
| return mIm.registerSensorListener(listener); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Unregister input device sensor listener |
| * |
| * @hide |
| */ |
| public void unregisterSensorListener(IInputSensorEventListener listener) { |
| try { |
| mIm.unregisterSensorListener(listener); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Get the battery status of the input device |
| * @param deviceId The input device ID |
| * @hide |
| */ |
| public int getBatteryStatus(int deviceId) { |
| try { |
| return mIm.getBatteryStatus(deviceId); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Get the remaining battery capacity of the input device |
| * @param deviceId The input device ID |
| * @hide |
| */ |
| public int getBatteryCapacity(int deviceId) { |
| try { |
| return mIm.getBatteryCapacity(deviceId); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * 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. |
| * <p> |
| * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. |
| * </p> |
| * @hide |
| */ |
| 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. |
| * <p> |
| * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. |
| * </p> |
| * @hide |
| */ |
| public void removePortAssociation(@NonNull String inputPort) { |
| try { |
| mIm.removePortAssociation(inputPort); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Add a runtime association between the input device name and display, by unique id. Input |
| * device names are expected to be unique. |
| * @param inputDeviceName The name of the input device. |
| * @param displayUniqueId The unique id of the associated display. |
| * <p> |
| * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. |
| * </p> |
| * @hide |
| */ |
| public void addUniqueIdAssociation(@NonNull String inputDeviceName, |
| @NonNull String displayUniqueId) { |
| try { |
| mIm.addUniqueIdAssociation(inputDeviceName, displayUniqueId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Removes a runtime association between the input device and display. |
| * @param inputDeviceName The name of the input device. |
| * <p> |
| * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. |
| * </p> |
| * @hide |
| */ |
| public void removeUniqueIdAssociation(@NonNull String inputDeviceName) { |
| try { |
| mIm.removeUniqueIdAssociation(inputDeviceName); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| private void populateInputDevicesLocked() { |
| if (mInputDevicesChangedListener == null) { |
| final InputDevicesChangedListener listener = new InputDevicesChangedListener(); |
| try { |
| mIm.registerInputDevicesChangedListener(listener); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| mInputDevicesChangedListener = listener; |
| } |
| |
| if (mInputDevices == null) { |
| final int[] ids; |
| try { |
| ids = mIm.getInputDeviceIds(); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| |
| mInputDevices = new SparseArray<InputDevice>(); |
| for (int i = 0; i < ids.length; i++) { |
| mInputDevices.put(ids[i], null); |
| } |
| } |
| } |
| |
| private void onInputDevicesChanged(int[] deviceIdAndGeneration) { |
| if (DEBUG) { |
| Log.d(TAG, "Received input devices changed."); |
| } |
| |
| synchronized (mInputDevicesLock) { |
| for (int i = mInputDevices.size(); --i > 0; ) { |
| final int deviceId = mInputDevices.keyAt(i); |
| if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { |
| if (DEBUG) { |
| Log.d(TAG, "Device removed: " + deviceId); |
| } |
| mInputDevices.removeAt(i); |
| sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); |
| } |
| } |
| |
| for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { |
| final int deviceId = deviceIdAndGeneration[i]; |
| int index = mInputDevices.indexOfKey(deviceId); |
| if (index >= 0) { |
| final InputDevice device = mInputDevices.valueAt(index); |
| if (device != null) { |
| final int generation = deviceIdAndGeneration[i + 1]; |
| if (device.getGeneration() != generation) { |
| if (DEBUG) { |
| Log.d(TAG, "Device changed: " + deviceId); |
| } |
| mInputDevices.setValueAt(index, null); |
| sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); |
| } |
| } |
| } else { |
| if (DEBUG) { |
| Log.d(TAG, "Device added: " + deviceId); |
| } |
| mInputDevices.put(deviceId, null); |
| sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); |
| } |
| } |
| } |
| } |
| |
| private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { |
| final int numListeners = mInputDeviceListeners.size(); |
| for (int i = 0; i < numListeners; i++) { |
| InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); |
| listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); |
| } |
| } |
| |
| private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { |
| for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { |
| if (deviceIdAndGeneration[i] == deviceId) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| private void onTabletModeChanged(long whenNanos, boolean inTabletMode) { |
| if (DEBUG) { |
| Log.d(TAG, "Received tablet mode changed: " |
| + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode); |
| } |
| synchronized (mTabletModeLock) { |
| final int N = mOnTabletModeChangedListeners.size(); |
| for (int i = 0; i < N; i++) { |
| OnTabletModeChangedListenerDelegate listener = |
| mOnTabletModeChangedListeners.get(i); |
| listener.sendTabletModeChanged(whenNanos, inTabletMode); |
| } |
| } |
| } |
| |
| /** |
| * 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(this, deviceId, vibratorId); |
| } |
| |
| /** |
| * Gets a vibrator manager service associated with an input device, always creates a new |
| * instance. |
| * @return The vibrator manager, never null. |
| * @hide |
| */ |
| @NonNull |
| public VibratorManager getInputDeviceVibratorManager(int deviceId) { |
| return new InputDeviceVibratorManager(InputManager.this, deviceId); |
| } |
| |
| /* |
| * Get the list of device vibrators |
| * @return The list of vibrators IDs |
| */ |
| int[] getVibratorIds(int deviceId) { |
| try { |
| return mIm.getVibratorIds(deviceId); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /* |
| * Perform vibration effect |
| */ |
| void vibrate(int deviceId, VibrationEffect effect, IBinder token) { |
| try { |
| mIm.vibrate(deviceId, effect, token); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /* |
| * Perform combined vibration effect |
| */ |
| void vibrate(int deviceId, CombinedVibration effect, IBinder token) { |
| try { |
| mIm.vibrateCombined(deviceId, effect, token); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /* |
| * Cancel an ongoing vibration |
| */ |
| void cancelVibrate(int deviceId, IBinder token) { |
| try { |
| mIm.cancelVibrate(deviceId, token); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /* |
| * Check if input device is vibrating |
| */ |
| boolean isVibrating(int deviceId) { |
| try { |
| return mIm.isVibrating(deviceId); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Register input device vibrator state listener |
| */ |
| boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { |
| try { |
| return mIm.registerVibratorStateListener(deviceId, listener); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Unregister input device vibrator state listener |
| */ |
| boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { |
| try { |
| return mIm.unregisterVibratorStateListener(deviceId, listener); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Gets a sensor manager service associated with an input device, always creates a new instance. |
| * @return The sensor manager, never null. |
| * @hide |
| */ |
| @NonNull |
| public SensorManager getInputDeviceSensorManager(int deviceId) { |
| if (mInputDeviceSensorManager == null) { |
| mInputDeviceSensorManager = new InputDeviceSensorManager(this); |
| } |
| return mInputDeviceSensorManager.getSensorManager(deviceId); |
| } |
| |
| /** |
| * Gets a battery state object associated with an input device, assuming it has one. |
| * @return The battery, never null. |
| * @hide |
| */ |
| public InputDeviceBatteryState getInputDeviceBatteryState(int deviceId, boolean hasBattery) { |
| return new InputDeviceBatteryState(this, deviceId, hasBattery); |
| } |
| |
| /** |
| * Gets a lights manager associated with an input device, always creates a new instance. |
| * @return The lights manager, never null. |
| * @hide |
| */ |
| @NonNull |
| public LightsManager getInputDeviceLightsManager(int deviceId) { |
| return new InputDeviceLightsManager(InputManager.this, deviceId); |
| } |
| |
| /** |
| * Gets a list of light objects associated with an input device. |
| * @return The list of lights, never null. |
| */ |
| @NonNull List<Light> getLights(int deviceId) { |
| try { |
| return mIm.getLights(deviceId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Returns the state of an input device light. |
| * @return the light state |
| */ |
| @NonNull LightState getLightState(int deviceId, @NonNull Light light) { |
| try { |
| return mIm.getLightState(deviceId, light.getId()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Request to modify the states of multiple lights. |
| * |
| * @param request the settings for lights that should change |
| */ |
| void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) { |
| try { |
| List<Integer> lightIdList = request.getLights(); |
| int[] lightIds = new int[lightIdList.size()]; |
| for (int i = 0; i < lightIds.length; i++) { |
| lightIds[i] = lightIdList.get(i); |
| } |
| List<LightState> lightStateList = request.getLightStates(); |
| mIm.setLightStates(deviceId, lightIds, |
| lightStateList.toArray(new LightState[lightStateList.size()]), |
| token); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Open light session for input device manager |
| * |
| * @param token The token for the light session |
| */ |
| void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) { |
| try { |
| mIm.openLightSession(deviceId, opPkg, token); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Close light session |
| * |
| */ |
| void closeLightSession(int deviceId, @NonNull IBinder token) { |
| try { |
| mIm.closeLightSession(deviceId, token); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Listens for changes in input devices. |
| */ |
| public interface InputDeviceListener { |
| /** |
| * Called whenever an input device has been added to the system. |
| * Use {@link InputManager#getInputDevice} 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); |
| } |
| |
| private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { |
| @Override |
| public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { |
| InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); |
| } |
| } |
| |
| private static final class InputDeviceListenerDelegate extends Handler { |
| public final InputDeviceListener mListener; |
| |
| public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { |
| super(handler != null ? handler.getLooper() : Looper.myLooper()); |
| mListener = listener; |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_DEVICE_ADDED: |
| mListener.onInputDeviceAdded(msg.arg1); |
| break; |
| case MSG_DEVICE_REMOVED: |
| mListener.onInputDeviceRemoved(msg.arg1); |
| break; |
| case MSG_DEVICE_CHANGED: |
| mListener.onInputDeviceChanged(msg.arg1); |
| break; |
| } |
| } |
| } |
| |
| /** @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); |
| } |
| |
| private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub { |
| @Override |
| public void onTabletModeChanged(long whenNanos, boolean inTabletMode) { |
| InputManager.this.onTabletModeChanged(whenNanos, inTabletMode); |
| } |
| } |
| |
| private static final class OnTabletModeChangedListenerDelegate extends Handler { |
| private static final int MSG_TABLET_MODE_CHANGED = 0; |
| |
| public final OnTabletModeChangedListener mListener; |
| |
| public OnTabletModeChangedListenerDelegate( |
| OnTabletModeChangedListener listener, Handler handler) { |
| super(handler != null ? handler.getLooper() : Looper.myLooper()); |
| mListener = listener; |
| } |
| |
| public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.argi1 = (int) (whenNanos & 0xFFFFFFFF); |
| args.argi2 = (int) (whenNanos >> 32); |
| args.arg1 = (Boolean) inTabletMode; |
| obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget(); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_TABLET_MODE_CHANGED: |
| SomeArgs args = (SomeArgs) msg.obj; |
| long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32); |
| boolean inTabletMode = (boolean) args.arg1; |
| mListener.onTabletModeChanged(whenNanos, inTabletMode); |
| break; |
| } |
| } |
| } |
| } |