script-astra/Android/Sdk/sources/android-35/android/hardware/input/InputManagerGlobal.java

1529 lines
54 KiB
Java
Raw Permalink Normal View History

2025-01-20 15:15:20 +00:00
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.input;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.hardware.BatteryState;
import android.hardware.SensorManager;
import android.hardware.input.InputManager.InputDeviceBatteryListener;
import android.hardware.input.InputManager.InputDeviceListener;
import android.hardware.input.InputManager.KeyboardBacklightListener;
import android.hardware.input.InputManager.OnTabletModeChangedListener;
import android.hardware.input.InputManager.StickyModifierStateListener;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightsRequest;
import android.os.Binder;
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.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.PointerIcon;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Manages communication with the input manager service on behalf of
* an application process. You're probably looking for {@link InputManager}.
*
* @hide
*/
public final class InputManagerGlobal {
private static final String TAG = "InputManagerGlobal";
// To enable these logs, run: 'adb shell setprop log.tag.InputManagerGlobal DEBUG'
// (requires restart)
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@GuardedBy("mInputDeviceListeners")
@Nullable private SparseArray<InputDevice> mInputDevices;
@GuardedBy("mInputDeviceListeners")
@Nullable private InputDevicesChangedListener mInputDevicesChangedListener;
@GuardedBy("mInputDeviceListeners")
private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = new ArrayList<>();
@GuardedBy("mOnTabletModeChangedListeners")
private final ArrayList<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners =
new ArrayList<>();
private final Object mBatteryListenersLock = new Object();
// Maps a deviceId whose battery is currently being monitored to an entry containing the
// registered listeners for that device.
@GuardedBy("mBatteryListenersLock")
@Nullable private SparseArray<RegisteredBatteryListeners> mBatteryListeners;
@GuardedBy("mBatteryListenersLock")
@Nullable private IInputDeviceBatteryListener mInputDeviceBatteryListener;
private final Object mKeyboardBacklightListenerLock = new Object();
@GuardedBy("mKeyboardBacklightListenerLock")
@Nullable private ArrayList<KeyboardBacklightListenerDelegate> mKeyboardBacklightListeners;
@GuardedBy("mKeyboardBacklightListenerLock")
@Nullable private IKeyboardBacklightListener mKeyboardBacklightListener;
private final Object mStickyModifierStateListenerLock = new Object();
@GuardedBy("mStickyModifierStateListenerLock")
@Nullable
private ArrayList<StickyModifierStateListenerDelegate> mStickyModifierStateListeners;
@GuardedBy("mStickyModifierStateListenerLock")
@Nullable
private IStickyModifierStateListener mStickyModifierStateListener;
// InputDeviceSensorManager gets notified synchronously from the binder thread when input
// devices change, so it must be synchronized with the input device listeners.
@GuardedBy("mInputDeviceListeners")
@Nullable private InputDeviceSensorManager mInputDeviceSensorManager;
private static InputManagerGlobal sInstance;
private final String mVelocityTrackerStrategy;
private final IInputManager mIm;
public InputManagerGlobal(IInputManager im) {
mIm = im;
String strategy = null;
try {
strategy = mIm.getVelocityTrackerStrategy();
} catch (RemoteException ex) {
Log.w(TAG, "Could not get VelocityTracker strategy: " + ex);
}
mVelocityTrackerStrategy = strategy;
}
/**
* Gets an instance of the input manager global singleton.
*
* @return The input manager instance, may be null early in system startup
* before the input manager has been fully initialized.
*/
public static InputManagerGlobal getInstance() {
synchronized (InputManagerGlobal.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
if (b != null) {
sInstance = new InputManagerGlobal(IInputManager.Stub.asInterface(b));
}
}
return sInstance;
}
}
public IInputManager getInputManagerService() {
return mIm;
}
/**
* A test session tracker for InputManagerGlobal.
* @see #createTestSession(IInputManager)
*/
@VisibleForTesting
public interface TestSession extends AutoCloseable {
@Override
void close();
}
/**
* Create and set a test instance of InputManagerGlobal.
*
* @return The test session. The session must be {@link TestSession#close()}-ed at the end
* of the test.
*/
@VisibleForTesting
public static TestSession createTestSession(IInputManager inputManagerService) {
synchronized (InputManagerGlobal.class) {
final var oldInstance = sInstance;
sInstance = new InputManagerGlobal(inputManagerService);
return () -> sInstance = oldInstance;
}
}
/**
* Get the current VelocityTracker strategy.
* Only works when the system has fully booted up.
*/
public String getVelocityTrackerStrategy() {
return mVelocityTrackerStrategy;
}
/**
* @see InputManager#getInputDevice(int)
*/
@Nullable
public InputDevice getInputDevice(int id) {
synchronized (mInputDeviceListeners) {
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;
}
}
@GuardedBy("mInputDeviceListeners")
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<>();
for (int id : ids) {
mInputDevices.put(id, null);
}
}
}
private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
@Override
public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
InputManagerGlobal.this.onInputDevicesChanged(deviceIdAndGeneration);
}
}
private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
if (DEBUG) {
Log.d(TAG, "Received input devices changed.");
}
synchronized (mInputDeviceListeners) {
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);
if (mInputDeviceSensorManager != null) {
mInputDeviceSensorManager.onInputDeviceRemoved(deviceId);
}
sendMessageToInputDeviceListenersLocked(
InputDeviceListenerDelegate.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);
if (mInputDeviceSensorManager != null) {
mInputDeviceSensorManager.onInputDeviceChanged(deviceId);
}
sendMessageToInputDeviceListenersLocked(
InputDeviceListenerDelegate.MSG_DEVICE_CHANGED, deviceId);
}
}
} else {
if (DEBUG) {
Log.d(TAG, "Device added: " + deviceId);
}
mInputDevices.put(deviceId, null);
if (mInputDeviceSensorManager != null) {
mInputDeviceSensorManager.onInputDeviceAdded(deviceId);
}
sendMessageToInputDeviceListenersLocked(
InputDeviceListenerDelegate.MSG_DEVICE_ADDED, deviceId);
}
}
}
}
private static final class InputDeviceListenerDelegate extends Handler {
public final InputDeviceListener mListener;
static final int MSG_DEVICE_ADDED = 1;
static final int MSG_DEVICE_REMOVED = 2;
static final int MSG_DEVICE_CHANGED = 3;
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;
}
}
}
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;
}
@GuardedBy("mInputDeviceListeners")
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));
}
}
/**
* @see InputManager#registerInputDeviceListener
*/
public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
Objects.requireNonNull(listener, "listener must not be null");
synchronized (mInputDeviceListeners) {
populateInputDevicesLocked();
int index = findInputDeviceListenerLocked(listener);
if (index < 0) {
mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
}
}
}
/**
* @see InputManager#unregisterInputDeviceListener
*/
public void unregisterInputDeviceListener(InputDeviceListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
synchronized (mInputDeviceListeners) {
int index = findInputDeviceListenerLocked(listener);
if (index >= 0) {
InputDeviceListenerDelegate d = mInputDeviceListeners.get(index);
d.removeCallbacksAndMessages(null);
mInputDeviceListeners.remove(index);
}
}
}
@GuardedBy("mInputDeviceListeners")
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;
}
/**
* @see InputManager#getInputDeviceIds
*/
public int[] getInputDeviceIds() {
synchronized (mInputDeviceListeners) {
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;
}
}
/**
* @see InputManager#isInputDeviceEnabled(int)
*/
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();
}
}
/**
* @see InputManager#enableInputDevice(int)
*/
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();
}
}
/**
* @see InputManager#disableInputDevice(int)
*/
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();
}
}
/**
* @see InputManager#getInputDeviceByDescriptor
*/
InputDevice getInputDeviceByDescriptor(String descriptor) {
Objects.requireNonNull(descriptor, "descriptor must not be null.");
synchronized (mInputDeviceListeners) {
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;
}
}
/**
* @see InputManager#getHostUsiVersion
*/
@Nullable
HostUsiVersion getHostUsiVersion(@NonNull Display display) {
Objects.requireNonNull(display, "display should not be null");
// Return the first valid USI version reported by any input device associated with
// the display.
synchronized (mInputDeviceListeners) {
populateInputDevicesLocked();
for (int i = 0; i < mInputDevices.size(); i++) {
final InputDevice device = getInputDevice(mInputDevices.keyAt(i));
if (device != null && device.getAssociatedDisplayId() == display.getDisplayId()) {
if (device.getHostUsiVersion() != null) {
return device.getHostUsiVersion();
}
}
}
}
// If there are no input devices that report a valid USI version, see if there is a config
// that specifies the USI version for the display. This is to handle cases where the USI
// input device is not registered by the kernel/driver all the time.
try {
return mIm.getHostUsiVersionFromDisplayConfig(display.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
if (DEBUG) {
Log.d(TAG, "Received tablet mode changed: "
+ "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode);
}
synchronized (mOnTabletModeChangedListeners) {
final int numListeners = mOnTabletModeChangedListeners.size();
for (int i = 0; i < numListeners; i++) {
OnTabletModeChangedListenerDelegate listener =
mOnTabletModeChangedListeners.get(i);
listener.sendTabletModeChanged(whenNanos, inTabletMode);
}
}
}
private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub {
@Override
public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
InputManagerGlobal.this.onTabletModeChanged(whenNanos, inTabletMode);
}
}
private static final class OnTabletModeChangedListenerDelegate extends Handler {
private static final int MSG_TABLET_MODE_CHANGED = 0;
public final OnTabletModeChangedListener mListener;
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;
args.argi2 = (int) (whenNanos >> 32);
args.arg1 = inTabletMode;
obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget();
}
@Override
public void handleMessage(Message msg) {
if (msg.what == 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);
}
}
}
/**
* @see InputManager#registerInputDeviceListener(InputDeviceListener, Handler)
*/
void registerOnTabletModeChangedListener(
OnTabletModeChangedListener listener, Handler handler) {
Objects.requireNonNull(listener, "listener must not be null");
synchronized (mOnTabletModeChangedListeners) {
if (mOnTabletModeChangedListeners == null) {
initializeTabletModeListenerLocked();
}
int idx = findOnTabletModeChangedListenerLocked(listener);
if (idx < 0) {
OnTabletModeChangedListenerDelegate d =
new OnTabletModeChangedListenerDelegate(listener, handler);
mOnTabletModeChangedListeners.add(d);
}
}
}
/**
* @see InputManager#unregisterOnTabletModeChangedListener(OnTabletModeChangedListener)
*/
void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) {
Objects.requireNonNull(listener, "listener must not be null");
synchronized (mOnTabletModeChangedListeners) {
int idx = findOnTabletModeChangedListenerLocked(listener);
if (idx >= 0) {
OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx);
d.removeCallbacksAndMessages(null);
}
}
}
@GuardedBy("mOnTabletModeChangedListeners")
private void initializeTabletModeListenerLocked() {
final TabletModeChangedListener listener = new TabletModeChangedListener();
try {
mIm.registerTabletModeChangedListener(listener);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
@GuardedBy("mOnTabletModeChangedListeners")
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;
}
private static final class RegisteredBatteryListeners {
final List<InputDeviceBatteryListenerDelegate> mDelegates = new ArrayList<>();
IInputDeviceBatteryState mInputDeviceBatteryState;
}
private static final class InputDeviceBatteryListenerDelegate {
final InputDeviceBatteryListener mListener;
final Executor mExecutor;
InputDeviceBatteryListenerDelegate(InputDeviceBatteryListener listener, Executor executor) {
mListener = listener;
mExecutor = executor;
}
void notifyBatteryStateChanged(IInputDeviceBatteryState state) {
mExecutor.execute(() ->
mListener.onBatteryStateChanged(state.deviceId, state.updateTime,
new LocalBatteryState(state.isPresent, state.status, state.capacity)));
}
}
/**
* @see InputManager#addInputDeviceBatteryListener(int, Executor, InputDeviceBatteryListener)
*/
public void addInputDeviceBatteryListener(int deviceId, @NonNull Executor executor,
@NonNull InputDeviceBatteryListener listener) {
Objects.requireNonNull(executor, "executor should not be null");
Objects.requireNonNull(listener, "listener should not be null");
synchronized (mBatteryListenersLock) {
if (mBatteryListeners == null) {
mBatteryListeners = new SparseArray<>();
mInputDeviceBatteryListener = new LocalInputDeviceBatteryListener();
}
RegisteredBatteryListeners listenersForDevice = mBatteryListeners.get(deviceId);
if (listenersForDevice == null) {
// The deviceId is currently not being monitored for battery changes.
// Start monitoring the device.
listenersForDevice = new RegisteredBatteryListeners();
mBatteryListeners.put(deviceId, listenersForDevice);
try {
mIm.registerBatteryListener(deviceId, mInputDeviceBatteryListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
// The deviceId is already being monitored for battery changes.
// Ensure that the listener is not already registered.
final int numDelegates = listenersForDevice.mDelegates.size();
for (int i = 0; i < numDelegates; i++) {
InputDeviceBatteryListener registeredListener =
listenersForDevice.mDelegates.get(i).mListener;
if (Objects.equals(listener, registeredListener)) {
throw new IllegalArgumentException(
"Attempting to register an InputDeviceBatteryListener that has "
+ "already been registered for deviceId: "
+ deviceId);
}
}
}
final InputDeviceBatteryListenerDelegate delegate =
new InputDeviceBatteryListenerDelegate(listener, executor);
listenersForDevice.mDelegates.add(delegate);
// Notify the listener immediately if we already have the latest battery state.
if (listenersForDevice.mInputDeviceBatteryState != null) {
delegate.notifyBatteryStateChanged(listenersForDevice.mInputDeviceBatteryState);
}
}
}
/**
* @see InputManager#removeInputDeviceBatteryListener(int, InputDeviceBatteryListener)
*/
void removeInputDeviceBatteryListener(int deviceId,
@NonNull InputDeviceBatteryListener listener) {
Objects.requireNonNull(listener, "listener should not be null");
synchronized (mBatteryListenersLock) {
if (mBatteryListeners == null) {
return;
}
RegisteredBatteryListeners listenersForDevice = mBatteryListeners.get(deviceId);
if (listenersForDevice == null) {
// The deviceId is not currently being monitored.
return;
}
final List<InputDeviceBatteryListenerDelegate> delegates =
listenersForDevice.mDelegates;
for (int i = 0; i < delegates.size();) {
if (Objects.equals(listener, delegates.get(i).mListener)) {
delegates.remove(i);
continue;
}
i++;
}
if (!delegates.isEmpty()) {
return;
}
// There are no more battery listeners for this deviceId. Stop monitoring this device.
mBatteryListeners.remove(deviceId);
try {
mIm.unregisterBatteryListener(deviceId, mInputDeviceBatteryListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
if (mBatteryListeners.size() == 0) {
// There are no more devices being monitored, so the registered
// IInputDeviceBatteryListener will be automatically dropped by the server.
mBatteryListeners = null;
mInputDeviceBatteryListener = null;
}
}
}
private class LocalInputDeviceBatteryListener extends IInputDeviceBatteryListener.Stub {
@Override
public void onBatteryStateChanged(IInputDeviceBatteryState state) {
synchronized (mBatteryListenersLock) {
if (mBatteryListeners == null) return;
final RegisteredBatteryListeners entry = mBatteryListeners.get(state.deviceId);
if (entry == null) return;
entry.mInputDeviceBatteryState = state;
final int numDelegates = entry.mDelegates.size();
for (int i = 0; i < numDelegates; i++) {
entry.mDelegates.get(i)
.notifyBatteryStateChanged(entry.mInputDeviceBatteryState);
}
}
}
}
/**
* @see #getInputDeviceBatteryState(int, boolean)
*/
@NonNull
public BatteryState getInputDeviceBatteryState(int deviceId, boolean hasBattery) {
if (!hasBattery) {
return new LocalBatteryState();
}
try {
final IInputDeviceBatteryState state = mIm.getBatteryState(deviceId);
return new LocalBatteryState(state.isPresent, state.status, state.capacity);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
// Implementation of the android.hardware.BatteryState interface used to report the battery
// state via the InputDevice#getBatteryState() and InputDeviceBatteryListener interfaces.
private static final class LocalBatteryState extends BatteryState {
private final boolean mIsPresent;
private final int mStatus;
private final float mCapacity;
LocalBatteryState() {
this(false /*isPresent*/, BatteryState.STATUS_UNKNOWN, Float.NaN /*capacity*/);
}
LocalBatteryState(boolean isPresent, int status, float capacity) {
mIsPresent = isPresent;
mStatus = status;
mCapacity = capacity;
}
@Override
public boolean isPresent() {
return mIsPresent;
}
@Override
public int getStatus() {
return mStatus;
}
@Override
public float getCapacity() {
return mCapacity;
}
}
private static final class KeyboardBacklightListenerDelegate {
final InputManager.KeyboardBacklightListener mListener;
final Executor mExecutor;
KeyboardBacklightListenerDelegate(KeyboardBacklightListener listener, Executor executor) {
mListener = listener;
mExecutor = executor;
}
void notifyKeyboardBacklightChange(int deviceId, IKeyboardBacklightState state,
boolean isTriggeredByKeyPress) {
mExecutor.execute(() ->
mListener.onKeyboardBacklightChanged(deviceId,
new LocalKeyboardBacklightState(state.brightnessLevel,
state.maxBrightnessLevel), isTriggeredByKeyPress));
}
}
private class LocalKeyboardBacklightListener extends IKeyboardBacklightListener.Stub {
@Override
public void onBrightnessChanged(int deviceId, IKeyboardBacklightState state,
boolean isTriggeredByKeyPress) {
synchronized (mKeyboardBacklightListenerLock) {
if (mKeyboardBacklightListeners == null) return;
final int numListeners = mKeyboardBacklightListeners.size();
for (int i = 0; i < numListeners; i++) {
mKeyboardBacklightListeners.get(i)
.notifyKeyboardBacklightChange(deviceId, state, isTriggeredByKeyPress);
}
}
}
}
// Implementation of the android.hardware.input.KeyboardBacklightState interface used to report
// the keyboard backlight state via the KeyboardBacklightListener interfaces.
private static final class LocalKeyboardBacklightState extends KeyboardBacklightState {
private final int mBrightnessLevel;
private final int mMaxBrightnessLevel;
LocalKeyboardBacklightState(int brightnessLevel, int maxBrightnessLevel) {
mBrightnessLevel = brightnessLevel;
mMaxBrightnessLevel = maxBrightnessLevel;
}
@Override
public int getBrightnessLevel() {
return mBrightnessLevel;
}
@Override
public int getMaxBrightnessLevel() {
return mMaxBrightnessLevel;
}
}
/**
* @see InputManager#registerKeyboardBacklightListener(Executor, KeyboardBacklightListener)
*/
@RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT)
void registerKeyboardBacklightListener(@NonNull Executor executor,
@NonNull KeyboardBacklightListener listener) throws IllegalArgumentException {
Objects.requireNonNull(executor, "executor should not be null");
Objects.requireNonNull(listener, "listener should not be null");
synchronized (mKeyboardBacklightListenerLock) {
if (mKeyboardBacklightListener == null) {
mKeyboardBacklightListeners = new ArrayList<>();
mKeyboardBacklightListener = new LocalKeyboardBacklightListener();
try {
mIm.registerKeyboardBacklightListener(mKeyboardBacklightListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
final int numListeners = mKeyboardBacklightListeners.size();
for (int i = 0; i < numListeners; i++) {
if (mKeyboardBacklightListeners.get(i).mListener == listener) {
throw new IllegalArgumentException("Listener has already been registered!");
}
}
KeyboardBacklightListenerDelegate delegate =
new KeyboardBacklightListenerDelegate(listener, executor);
mKeyboardBacklightListeners.add(delegate);
}
}
/**
* @see InputManager#unregisterKeyboardBacklightListener(KeyboardBacklightListener)
*/
@RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT)
void unregisterKeyboardBacklightListener(
@NonNull KeyboardBacklightListener listener) {
Objects.requireNonNull(listener, "listener should not be null");
synchronized (mKeyboardBacklightListenerLock) {
if (mKeyboardBacklightListeners == null) {
return;
}
mKeyboardBacklightListeners.removeIf((delegate) -> delegate.mListener == listener);
if (mKeyboardBacklightListeners.isEmpty()) {
try {
mIm.unregisterKeyboardBacklightListener(mKeyboardBacklightListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mKeyboardBacklightListeners = null;
mKeyboardBacklightListener = null;
}
}
}
private static final class StickyModifierStateListenerDelegate {
final InputManager.StickyModifierStateListener mListener;
final Executor mExecutor;
StickyModifierStateListenerDelegate(StickyModifierStateListener listener,
Executor executor) {
mListener = listener;
mExecutor = executor;
}
void notifyStickyModifierStateChange(int modifierState, int lockedModifierState) {
mExecutor.execute(() ->
mListener.onStickyModifierStateChanged(
new LocalStickyModifierState(modifierState, lockedModifierState)));
}
}
private class LocalStickyModifierStateListener extends IStickyModifierStateListener.Stub {
@Override
public void onStickyModifierStateChanged(int modifierState, int lockedModifierState) {
synchronized (mStickyModifierStateListenerLock) {
if (mStickyModifierStateListeners == null) return;
final int numListeners = mStickyModifierStateListeners.size();
for (int i = 0; i < numListeners; i++) {
mStickyModifierStateListeners.get(i)
.notifyStickyModifierStateChange(modifierState, lockedModifierState);
}
}
}
}
// Implementation of the android.hardware.input.StickyModifierState interface used to report
// the sticky modifier state via the StickyModifierStateListener interfaces.
private static final class LocalStickyModifierState extends StickyModifierState {
private final int mModifierState;
private final int mLockedModifierState;
LocalStickyModifierState(int modifierState, int lockedModifierState) {
mModifierState = modifierState;
mLockedModifierState = lockedModifierState;
}
@Override
public boolean isShiftModifierOn() {
return (mModifierState & KeyEvent.META_SHIFT_ON) != 0;
}
@Override
public boolean isShiftModifierLocked() {
return (mLockedModifierState & KeyEvent.META_SHIFT_ON) != 0;
}
@Override
public boolean isCtrlModifierOn() {
return (mModifierState & KeyEvent.META_CTRL_ON) != 0;
}
@Override
public boolean isCtrlModifierLocked() {
return (mLockedModifierState & KeyEvent.META_CTRL_ON) != 0;
}
@Override
public boolean isMetaModifierOn() {
return (mModifierState & KeyEvent.META_META_ON) != 0;
}
@Override
public boolean isMetaModifierLocked() {
return (mLockedModifierState & KeyEvent.META_META_ON) != 0;
}
@Override
public boolean isAltModifierOn() {
return (mModifierState & KeyEvent.META_ALT_LEFT_ON) != 0;
}
@Override
public boolean isAltModifierLocked() {
return (mLockedModifierState & KeyEvent.META_ALT_LEFT_ON) != 0;
}
@Override
public boolean isAltGrModifierOn() {
return (mModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0;
}
@Override
public boolean isAltGrModifierLocked() {
return (mLockedModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0;
}
}
/**
* @see InputManager#registerStickyModifierStateListener(Executor, StickyModifierStateListener)
*/
@RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
void registerStickyModifierStateListener(@NonNull Executor executor,
@NonNull StickyModifierStateListener listener) throws IllegalArgumentException {
Objects.requireNonNull(executor, "executor should not be null");
Objects.requireNonNull(listener, "listener should not be null");
synchronized (mStickyModifierStateListenerLock) {
if (mStickyModifierStateListener == null) {
mStickyModifierStateListeners = new ArrayList<>();
mStickyModifierStateListener = new LocalStickyModifierStateListener();
try {
mIm.registerStickyModifierStateListener(mStickyModifierStateListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
final int numListeners = mStickyModifierStateListeners.size();
for (int i = 0; i < numListeners; i++) {
if (mStickyModifierStateListeners.get(i).mListener == listener) {
throw new IllegalArgumentException("Listener has already been registered!");
}
}
StickyModifierStateListenerDelegate delegate =
new StickyModifierStateListenerDelegate(listener, executor);
mStickyModifierStateListeners.add(delegate);
}
}
/**
* @see InputManager#unregisterStickyModifierStateListener(StickyModifierStateListener)
*/
@RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
void unregisterStickyModifierStateListener(
@NonNull StickyModifierStateListener listener) {
Objects.requireNonNull(listener, "listener should not be null");
synchronized (mStickyModifierStateListenerLock) {
if (mStickyModifierStateListeners == null) {
return;
}
mStickyModifierStateListeners.removeIf((delegate) -> delegate.mListener == listener);
if (mStickyModifierStateListeners.isEmpty()) {
try {
mIm.unregisterStickyModifierStateListener(mStickyModifierStateListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mStickyModifierStateListeners = null;
mStickyModifierStateListener = null;
}
}
}
/**
* TODO(b/330517633): Cleanup the unsupported API
*/
@NonNull
public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
@NonNull InputDeviceIdentifier identifier) {
return new KeyboardLayout[0];
}
/**
* TODO(b/330517633): Cleanup the unsupported API
*/
public void setCurrentKeyboardLayoutForInputDevice(
@NonNull InputDeviceIdentifier identifier,
@NonNull String keyboardLayoutDescriptor) {}
/**
* @see InputDevice#getSensorManager()
*/
@NonNull
public SensorManager getInputDeviceSensorManager(int deviceId) {
synchronized (mInputDeviceListeners) {
if (mInputDeviceSensorManager == null) {
mInputDeviceSensorManager = new InputDeviceSensorManager(this);
}
return mInputDeviceSensorManager.getSensorManager(deviceId);
}
}
/**
* Get information about all of the sensors supported by an input device
* @see InputDeviceSensorManager
*/
InputSensorInfo[] getSensorList(int deviceId) {
try {
return mIm.getSensorList(deviceId);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputDeviceSensorManager
*/
boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs,
int maxBatchReportLatencyUs) {
try {
return mIm.enableSensor(deviceId, sensorType, samplingPeriodUs,
maxBatchReportLatencyUs);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputDeviceSensorManager
*/
void disableSensor(int deviceId, int sensorType) {
try {
mIm.disableSensor(deviceId, sensorType);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputDeviceSensorManager
*/
boolean flushSensor(int deviceId, int sensorType) {
try {
return mIm.flushSensor(deviceId, sensorType);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputDeviceSensorManager
*/
boolean registerSensorListener(IInputSensorEventListener listener) {
try {
return mIm.registerSensorListener(listener);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputDeviceSensorManager
*/
void unregisterSensorListener(IInputSensorEventListener listener) {
try {
mIm.unregisterSensorListener(listener);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputDevice#getLightsManager()
*/
@NonNull
public LightsManager getInputDeviceLightsManager(int deviceId) {
return new InputDeviceLightsManager(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[0]),
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();
}
}
/**
* @see InputManager#getInputDeviceVibrator(int, int)
*/
public Vibrator getInputDeviceVibrator(int deviceId, int vibratorId) {
return new InputDeviceVibrator(deviceId, vibratorId);
}
/**
* @see InputDevice#getVibratorManager()
*/
@NonNull
public VibratorManager getInputDeviceVibratorManager(int deviceId) {
return new InputDeviceVibratorManager(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();
}
}
/**
* @see InputManager#deviceHasKeys(int[])
*/
public boolean[] deviceHasKeys(int[] keyCodes) {
return deviceHasKeys(-1, keyCodes);
}
/**
* @see InputManager#deviceHasKeys(int, int[])
*/
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;
}
/**
* @see InputManager#getKeyCodeForKeyLocation(int, int)
*/
public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) {
try {
return mIm.getKeyCodeForKeyLocation(deviceId, locationKeyCode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns KeyCharacterMap for the provided Keyboard layout. If provided layout is null it will
* return KeyCharacter map for the default layout {@code Generic.kl}.
*/
public KeyCharacterMap getKeyCharacterMap(@Nullable KeyboardLayout keyboardLayout) {
if (keyboardLayout == null) {
return KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
}
try {
return mIm.getKeyCharacterMap(keyboardLayout.getDescriptor());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @see InputManager#injectInputEvent(InputEvent, int, int)
*/
public boolean injectInputEvent(InputEvent event, int mode, int targetUid) {
Objects.requireNonNull(event , "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.injectInputEventToTarget(event, mode, targetUid);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputManager#injectInputEvent(InputEvent, int)
*/
public boolean injectInputEvent(InputEvent event, int mode) {
return injectInputEvent(event, mode, Process.INVALID_UID);
}
/**
* @see InputManager#setPointerIcon(PointerIcon, int, int, int, IBinder)
*/
public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId,
IBinder inputToken) {
try {
return mIm.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputManager#requestPointerCapture(IBinder, boolean)
*/
public void requestPointerCapture(IBinder windowToken, boolean enable) {
try {
mIm.requestPointerCapture(windowToken, enable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputManager#monitorGestureInput(String, int)
*/
public InputMonitor monitorGestureInput(String name, int displayId) {
try {
return mIm.monitorGestureInput(new Binder(), name, displayId);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* @see InputManager#addUniqueIdAssociationByPort(String, String)
*/
public void addUniqueIdAssociationByPort(@NonNull String inputPort,
@NonNull String displayUniqueId) {
try {
mIm.addUniqueIdAssociationByPort(inputPort, displayUniqueId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @see InputManager#removeUniqueIdAssociationByPort(String)
*/
public void removeUniqueIdAssociationByPort(@NonNull String inputPort) {
try {
mIm.removeUniqueIdAssociationByPort(inputPort);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @see InputManager#addUniqueIdAssociationByDescriptor(String, String)
*/
public void addUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor,
@NonNull String displayUniqueId) {
try {
mIm.addUniqueIdAssociationByDescriptor(inputDeviceDescriptor, displayUniqueId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @see InputManager#removeUniqueIdAssociationByDescriptor(String)
*/
public void removeUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor) {
try {
mIm.removeUniqueIdAssociationByDescriptor(inputDeviceDescriptor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @see InputManager#getInputDeviceBluetoothAddress(int)
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
@Nullable
public String getInputDeviceBluetoothAddress(int deviceId) {
try {
return mIm.getInputDeviceBluetoothAddress(deviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @see InputManager#cancelCurrentTouch()
*/
public void cancelCurrentTouch() {
try {
mIm.cancelCurrentTouch();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @see InputManager#pilferPointers(IBinder)
*/
@RequiresPermission(Manifest.permission.MONITOR_INPUT)
public void pilferPointers(IBinder inputChannelToken) {
try {
mIm.pilferPointers(inputChannelToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}