summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt1
-rw-r--r--core/java/android/hardware/Sensor.java25
-rw-r--r--core/java/android/hardware/SensorEvent.java13
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl15
-rw-r--r--core/java/android/hardware/input/IInputSensorEventListener.aidl27
-rw-r--r--core/java/android/hardware/input/InputDeviceSensorManager.java661
-rw-r--r--core/java/android/hardware/input/InputManager.java109
-rw-r--r--core/java/android/hardware/input/InputSensorInfo.aidl19
-rw-r--r--core/java/android/hardware/input/InputSensorInfo.java330
-rw-r--r--core/java/android/view/InputDevice.java46
-rw-r--r--core/jni/android_view_InputDevice.cpp21
-rw-r--r--core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java251
-rw-r--r--data/keyboards/Generic.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_05c4.idc35
-rw-r--r--data/keyboards/Vendor_054c_Product_05c4.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_09cc.idc35
-rw-r--r--data/keyboards/Vendor_054c_Product_09cc.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl8
-rw-r--r--data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl8
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java201
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp204
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java8
27 files changed, 2060 insertions, 16 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index c2f88d7161c7..495e09767950 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -46680,6 +46680,7 @@ package android.view {
method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges();
method public String getName();
method public int getProductId();
+ method @NonNull public android.hardware.SensorManager getSensorManager();
method public int getSources();
method public int getVendorId();
method public android.os.Vibrator getVibrator();
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index e913986ae792..f7c4c2c5ca15 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -19,6 +19,7 @@ package android.hardware;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.hardware.input.InputSensorInfo;
import android.os.Build;
/**
@@ -923,6 +924,30 @@ public final class Sensor {
}
/**
+ * Construct a sensor object from SensorInfo of an input device.
+ * This is only used for constructing an input device sensor object.
+ * @hide
+ */
+ public Sensor(InputSensorInfo sensorInfo) {
+ this.mName = sensorInfo.getName();
+ this.mVendor = sensorInfo.getVendor();
+ this.mVersion = sensorInfo.getVersion();
+ this.mHandle = sensorInfo.getHandle();
+ this.mType = sensorInfo.getType();
+ this.mMaxRange = sensorInfo.getMaxRange();
+ this.mResolution = sensorInfo.getResolution();
+ this.mPower = sensorInfo.getPower();
+ this.mMinDelay = sensorInfo.getMinDelay();
+ this.mFifoReservedEventCount = sensorInfo.getFifoReservedEventCount();
+ this.mFifoMaxEventCount = sensorInfo.getFifoMaxEventCount();
+ this.mStringType = sensorInfo.getStringType();
+ this.mRequiredPermission = sensorInfo.getRequiredPermission();
+ this.mMaxDelay = sensorInfo.getMaxDelay();
+ this.mFlags = sensorInfo.getFlags();
+ this.mId = sensorInfo.getId();
+ }
+
+ /**
* @return name string of the sensor.
*/
public String getName() {
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 236fab0275cf..232f23429720 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -16,6 +16,7 @@
package android.hardware;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
/**
@@ -667,4 +668,16 @@ public class SensorEvent {
SensorEvent(int valueSize) {
values = new float[valueSize];
}
+
+ /**
+ * Construct a sensor event object by sensor object, accuracy, timestamp and values.
+ * This is only used for constructing an input device sensor event object.
+ * @hide
+ */
+ public SensorEvent(@NonNull Sensor sensor, int accuracy, long timestamp, float[] values) {
+ this.sensor = sensor;
+ this.accuracy = accuracy;
+ this.timestamp = timestamp;
+ this.values = values;
+ }
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 3f3c3bf4e2df..b39df4d2b6bf 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -23,6 +23,8 @@ import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.TouchCalibration;
import android.os.CombinedVibrationEffect;
+import android.hardware.input.IInputSensorEventListener;
+import android.hardware.input.InputSensorInfo;
import android.os.IBinder;
import android.os.VibrationEffect;
import android.view.InputDevice;
@@ -105,4 +107,17 @@ interface IInputManager {
// Remove the runtime association between the input port and the display port. Any existing
// static association for the cleared input port will be restored.
void removePortAssociation(in String inputPort);
+
+ InputSensorInfo[] getSensorList(int deviceId);
+
+ boolean registerSensorListener(IInputSensorEventListener listener);
+
+ void unregisterSensorListener(IInputSensorEventListener listener);
+
+ boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs,
+ int maxBatchReportLatencyUs);
+
+ void disableSensor(int deviceId, int sensorType);
+
+ boolean flushSensor(int deviceId, int sensorType);
}
diff --git a/core/java/android/hardware/input/IInputSensorEventListener.aidl b/core/java/android/hardware/input/IInputSensorEventListener.aidl
new file mode 100644
index 000000000000..508f59000e6e
--- /dev/null
+++ b/core/java/android/hardware/input/IInputSensorEventListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 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;
+
+/** @hide */
+interface IInputSensorEventListener {
+ /* Called when there is a new sensor event. */
+ oneway void onInputSensorChanged(int deviceId, int sensorId, int accuracy, long timestamp,
+ in float[] values);
+
+ /* Called when the accuracy of the registered sensor has changed. */
+ oneway void onInputSensorAccuracyChanged(int deviceId, int sensorId, int accuracy);
+}
diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java
new file mode 100644
index 000000000000..56c2cddcbf15
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceSensorManager.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2020 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.annotation.NonNull;
+import android.hardware.HardwareBuffer;
+import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
+import android.hardware.SensorDirectChannel;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEventListener;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.MemoryFile;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.InputDevice;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Sensor manager implementation that communicates with the input device
+ * sensors.
+ * @hide
+ */
+public class InputDeviceSensorManager implements InputManager.InputDeviceListener {
+ private static final String TAG = "InputDeviceSensorManager";
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_SENSOR_ACCURACY_CHANGED = 1;
+ private static final int MSG_SENSOR_CHANGED = 2;
+
+ private InputManager mInputManager;
+
+ // sensor map from device id to sensor list
+ @GuardedBy("mInputSensorLock")
+ private final Map<Integer, List<Sensor>> mSensors = new HashMap<>();
+
+ private final Object mInputSensorLock = new Object();
+ private InputSensorEventListener mInputServiceSensorListener;
+ @GuardedBy("mInputSensorLock")
+ private final ArrayList<InputSensorEventListenerDelegate> mInputSensorEventListeners =
+ new ArrayList<InputSensorEventListenerDelegate>();
+ private HandlerThread mSensorThread = null;
+ private Handler mSensorHandler = null;
+
+ public InputDeviceSensorManager(InputManager inputManager) {
+ mInputManager = inputManager;
+
+ mSensorThread = new HandlerThread("SensorThread");
+ mSensorThread.start();
+ mSensorHandler = new Handler(mSensorThread.getLooper());
+
+ // Register the input device listener
+ mInputManager.registerInputDeviceListener(this, mSensorHandler);
+ // Initialize the sensor list
+ initializeSensors();
+ }
+
+ /*
+ * Get SensorManager object for specific input device
+ *
+ * @param deviceId Input device ID
+ * @return SensorManager object for input device
+ */
+ SensorManager getSensorManager(int deviceId) {
+ return new InputSensorManager(deviceId);
+ }
+
+ /*
+ * Update input device sensor info for specified input device ID.
+ */
+ private void updateInputDeviceSensorInfoLocked(int deviceId) {
+ final InputDevice inputDevice = InputDevice.getDevice(deviceId);
+ if (inputDevice.hasSensor()) {
+ final InputSensorInfo[] sensorInfos =
+ mInputManager.getSensorList(deviceId);
+ populateSensorsForInputDeviceLocked(deviceId, sensorInfos);
+ }
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ synchronized (mInputSensorLock) {
+ if (!mSensors.containsKey(deviceId)) {
+ updateInputDeviceSensorInfoLocked(deviceId);
+ } else {
+ Slog.e(TAG, "Received 'device added' notification for device " + deviceId
+ + ", but it is already in the list");
+ }
+ }
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ synchronized (mInputSensorLock) {
+ mSensors.remove(deviceId);
+ }
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ synchronized (mInputSensorLock) {
+ if (mSensors.containsKey(deviceId)) {
+ mSensors.remove(deviceId);
+ }
+ updateInputDeviceSensorInfoLocked(deviceId);
+ }
+ }
+
+ private static boolean sensorEquals(@NonNull Sensor lhs, @NonNull Sensor rhs) {
+ if (lhs.getType() == rhs.getType() && lhs.getId() == rhs.getId()) {
+ return true;
+ }
+ return false;
+ }
+
+ private void populateSensorsForInputDeviceLocked(int deviceId, InputSensorInfo[] sensorInfos) {
+ List<Sensor> sensors = new ArrayList<Sensor>();
+ for (int i = 0; i < sensorInfos.length; i++) {
+ Sensor sensor = new Sensor(sensorInfos[i]);
+ if (DEBUG) {
+ Slog.d(TAG, "Device " + deviceId + " sensor " + sensor.getStringType() + " added");
+ }
+ sensors.add(sensor);
+ }
+ mSensors.put(deviceId, sensors);
+ }
+
+ private void initializeSensors() {
+ synchronized (mInputSensorLock) {
+ mSensors.clear();
+ int[] deviceIds = mInputManager.getInputDeviceIds();
+ for (int i = 0; i < deviceIds.length; i++) {
+ final int deviceId = deviceIds[i];
+ updateInputDeviceSensorInfoLocked(deviceId);
+ }
+ }
+ }
+
+ /**
+ * Get a sensor object for input device, with specific sensor type.
+ * @param deviceId The input devicd ID
+ * @param sensorType The sensor type
+ * @return The sensor object if exists or null
+ */
+ @GuardedBy("mInputSensorLock")
+ private Sensor getInputDeviceSensorLocked(int deviceId, int sensorType) {
+ List<Sensor> sensors = mSensors.get(deviceId);
+ for (Sensor sensor : sensors) {
+ if (sensor.getType() == sensorType) {
+ return sensor;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mInputSensorLock")
+ private int findSensorEventListenerLocked(SensorEventListener listener) {
+ for (int i = 0; i < mInputSensorEventListeners.size(); i++) {
+ if (mInputSensorEventListeners.get(i).getListener() == listener) {
+ return i;
+ }
+ }
+ return Integer.MIN_VALUE;
+ }
+
+ private void onInputSensorChanged(int deviceId, int sensorType, int accuracy, long timestamp,
+ float[] values) {
+ if (DEBUG) {
+ Slog.d(TAG, "Sensor changed: deviceId =" + deviceId
+ + " timestamp=" + timestamp + " sensorType=" + sensorType);
+ }
+ synchronized (mInputSensorLock) {
+ SensorEvent event = createSensorEvent(
+ InputDevice.getDevice(deviceId), sensorType, accuracy, timestamp, values);
+ if (event == null) {
+ Slog.wtf(TAG, "Failed to create SensorEvent.");
+ return;
+ }
+ for (int i = 0; i < mInputSensorEventListeners.size(); i++) {
+ InputSensorEventListenerDelegate listener =
+ mInputSensorEventListeners.get(i);
+ if (listener.hasSensorRegistered(deviceId, sensorType)) {
+ listener.sendSensorChanged(event);
+ }
+ }
+ }
+ }
+
+ private void onInputSensorAccuracyChanged(int deviceId, int sensorType, int accuracy) {
+ if (DEBUG) {
+ Slog.d(TAG, "Sensor accuracy changed: "
+ + "accuracy=" + accuracy + ", sensorType=" + sensorType);
+ }
+ synchronized (mInputSensorLock) {
+ for (int i = 0; i < mInputSensorEventListeners.size(); i++) {
+ InputSensorEventListenerDelegate listener =
+ mInputSensorEventListeners.get(i);
+ if (listener.hasSensorRegistered(deviceId, sensorType)) {
+ listener.sendSensorAccuracyChanged(deviceId, sensorType, accuracy);
+ }
+ }
+ }
+ }
+
+ private final class InputSensorEventListener extends IInputSensorEventListener.Stub {
+ @Override
+ public void onInputSensorChanged(int deviceId, int sensorType, int accuracy, long timestamp,
+ float[] values) throws RemoteException {
+ InputDeviceSensorManager.this.onInputSensorChanged(
+ deviceId, sensorType, accuracy, timestamp, values);
+ }
+
+ @Override
+ public void onInputSensorAccuracyChanged(int deviceId, int sensorType, int accuracy)
+ throws RemoteException {
+ InputDeviceSensorManager.this.onInputSensorAccuracyChanged(deviceId, sensorType,
+ accuracy);
+ }
+
+ }
+
+ private static final class InputSensorEventListenerDelegate extends Handler {
+ private final SensorEventListener mListener;
+ private final int mDelayUs;
+ private final int mMaxBatchReportLatencyUs;
+ private List<Sensor> mSensors = new ArrayList<Sensor>();
+
+ InputSensorEventListenerDelegate(SensorEventListener listener, Sensor sensor,
+ int delayUs, int maxBatchReportLatencyUs, Handler handler) {
+ super(handler != null ? handler.getLooper() : Looper.myLooper());
+ mListener = listener;
+ mSensors.add(sensor);
+ mDelayUs = delayUs;
+ mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+ }
+
+ public List<Sensor> getSensors() {
+ return mSensors;
+ }
+
+ public boolean isEmpty() {
+ return mSensors.isEmpty();
+ }
+
+ /**
+ * Remove sensor from sensor list for listener
+ */
+ public void removeSensor(Sensor sensor) {
+ // If sensor is not specified the listener will be unregistered for all sensors
+ // and the sensor list is cleared.
+ if (sensor == null) {
+ mSensors.clear();
+ }
+ for (Sensor s : mSensors) {
+ if (sensorEquals(s, sensor)) {
+ mSensors.remove(sensor);
+ }
+ }
+ }
+
+ /**
+ * Add a sensor to listener's sensor list
+ */
+ public void addSensor(@NonNull Sensor sensor) {
+ for (Sensor s : mSensors) {
+ if (sensorEquals(s, sensor)) {
+ Slog.w(TAG, "Adding sensor " + sensor + " already exist!");
+ return;
+ }
+ }
+ mSensors.add(sensor);
+ }
+
+ /**
+ * Check if the listener has been registered to the sensor
+ * @param deviceId The input device ID of the sensor
+ * @param sensorType The sensor type of the sensor
+ * @return true if specified sensor is registered for the listener.
+ */
+ public boolean hasSensorRegistered(int deviceId, int sensorType) {
+ for (Sensor sensor : mSensors) {
+ if (sensor.getType() == sensorType && sensor.getId() == deviceId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get listener handle for the delegate
+ */
+ public SensorEventListener getListener() {
+ return mListener;
+ }
+
+ /**
+ * Send sensor changed message
+ */
+ public void sendSensorChanged(SensorEvent event) {
+ SomeArgs args = SomeArgs.obtain();
+ obtainMessage(MSG_SENSOR_CHANGED, event).sendToTarget();
+ }
+
+ /**
+ * Send sensor accuracy changed message
+ */
+ public void sendSensorAccuracyChanged(int deviceId, int sensorType, int accuracy) {
+ SomeArgs args = SomeArgs.obtain();
+ obtainMessage(MSG_SENSOR_ACCURACY_CHANGED, deviceId, sensorType, accuracy)
+ .sendToTarget();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SENSOR_ACCURACY_CHANGED: {
+ final int deviceId = msg.arg1;
+ final int sensorType = msg.arg2;
+ final int accuracy = (int) msg.obj;
+ for (Sensor sensor : mSensors) {
+ if (sensor.getId() == deviceId && sensor.getType() == sensorType) {
+ mListener.onAccuracyChanged(sensor, accuracy);
+ }
+ }
+ break;
+ }
+ case MSG_SENSOR_CHANGED: {
+ SensorEvent event = (SensorEvent) msg.obj;
+ mListener.onSensorChanged(event);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Create SensorEvent object for input device, with specified device ID, sensor Type,
+ * sensor event timestamp, accuracy, and sensor values.
+ */
+ private SensorEvent createSensorEvent(InputDevice inputDevice, int sensorType, int accuracy,
+ long timestamp, float[] values) {
+ synchronized (mInputSensorLock) {
+ Sensor sensor = getInputDeviceSensorLocked(inputDevice.getId(), sensorType);
+ if (sensor == null) {
+ Slog.wtf(TAG, "Can't get sensor type " + sensorType + " for input device "
+ + inputDevice);
+ }
+ SensorEvent event = new SensorEvent(sensor, accuracy, timestamp, values);
+ if (event == null) {
+ Slog.wtf(TAG, "Failed to create SensorEvent.");
+ }
+ return event;
+ }
+ }
+
+ /**
+ * Return the default sensor object for input device, for specific sensor type.
+ */
+ private Sensor getSensorForInputDevice(int deviceId, int type) {
+ synchronized (mInputSensorLock) {
+ for (Map.Entry<Integer, List<Sensor>> entry : mSensors.entrySet()) {
+ for (Sensor sensor : entry.getValue()) {
+ if (sensor.getId() == deviceId && sensor.getType() == type) {
+ if (DEBUG) {
+ Slog.d(TAG, "Device " + deviceId + " sensor " + sensor.getStringType());
+ }
+ return sensor;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return list of sensors that belong to an input device, specified by input device ID.
+ */
+ private List<Sensor> getFullSensorListForDevice(int deviceId) {
+ List<Sensor> sensors = new ArrayList<Sensor>();
+ synchronized (mInputSensorLock) {
+ for (Map.Entry<Integer, List<Sensor>> entry : mSensors.entrySet()) {
+ for (Sensor sensor : entry.getValue()) {
+ if (sensor.getId() == deviceId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Device " + deviceId + " sensor " + sensor.getStringType());
+ }
+ sensors.add(sensor);
+ }
+ }
+ }
+ }
+ return sensors;
+ }
+
+ private boolean registerListenerInternal(SensorEventListener listener, Sensor sensor,
+ int delayUs, int maxBatchReportLatencyUs, Handler handler) {
+ if (DEBUG) {
+ Slog.d(TAG, "registerListenerImpl listener=" + listener + " sensor=" + sensor
+ + " delayUs=" + delayUs
+ + " maxBatchReportLatencyUs=" + maxBatchReportLatencyUs);
+ }
+ if (listener == null) {
+ Slog.e(TAG, "listener is null");
+ return false;
+ }
+
+ if (sensor == null) {
+ Slog.e(TAG, "sensor is null");
+ return false;
+ }
+
+ // Trigger Sensors should use the requestTriggerSensor call.
+ if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ Slog.e(TAG, "Trigger Sensors should use the requestTriggerSensor.");
+ return false;
+ }
+ if (maxBatchReportLatencyUs < 0 || delayUs < 0) {
+ Slog.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative");
+ return false;
+ }
+
+ if (getSensorForInputDevice(sensor.getId(), sensor.getType()) != null) {
+ synchronized (mInputSensorLock) {
+ final int deviceId = sensor.getId();
+ InputDevice inputDevice = InputDevice.getDevice(deviceId);
+ if (!inputDevice.hasSensor()) {
+ Slog.e(TAG, "The device doesn't have the sensor:" + sensor);
+ return false;
+ }
+ if (!mInputManager.enableSensor(deviceId, sensor.getType(), delayUs,
+ maxBatchReportLatencyUs)) {
+ Slog.e(TAG, "Can't enable the sensor:" + sensor);
+ return false;
+ }
+ }
+ }
+
+ synchronized (mInputSensorLock) {
+ // Register the InputManagerService sensor listener if not yet.
+ if (mInputServiceSensorListener == null) {
+ mInputServiceSensorListener = new InputSensorEventListener();
+ if (!mInputManager.registerSensorListener(mInputServiceSensorListener)) {
+ Slog.e(TAG, "Failed registering the sensor listener");
+ return false;
+ }
+ }
+
+ int idx = findSensorEventListenerLocked(listener);
+ if (idx < 0) {
+ InputSensorEventListenerDelegate d =
+ new InputSensorEventListenerDelegate(listener, sensor, delayUs,
+ maxBatchReportLatencyUs,
+ handler == null ? mSensorHandler : handler);
+ mInputSensorEventListeners.add(d);
+ } else {
+ // The listener is already registered, see if it wants to listen to more sensors.
+ mInputSensorEventListeners.get(idx).addSensor(sensor);
+ }
+ }
+
+ return true;
+ }
+
+ private void unregisterListenerInternal(SensorEventListener listener, Sensor sensor) {
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterListenerImpl listener=" + listener + " sensor=" + sensor);
+ }
+ if (listener == null) { // it's OK for the sensor to be null
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ synchronized (mInputSensorLock) {
+ int idx = findSensorEventListenerLocked(listener);
+ // Track the sensor types and the device Id the listener has registered.
+ final List<Sensor> sensorsRegistered;
+ if (idx >= 0) {
+ InputSensorEventListenerDelegate delegate =
+ mInputSensorEventListeners.get(idx);
+ sensorsRegistered = new ArrayList<Sensor>(delegate.getSensors());
+ // Get the sensor types the listener is listening to
+ delegate.removeSensor(sensor);
+ if (delegate.isEmpty()) {
+ // If no sensors to listen, remove the listener delegate
+ mInputSensorEventListeners.remove(idx);
+ }
+ } else {
+ Slog.e(TAG, "Listener is not registered");
+ return;
+ }
+ // If no delegation remains, unregister the listener to input service
+ if (mInputServiceSensorListener != null && mInputSensorEventListeners.size() == 0) {
+ mInputManager.unregisterSensorListener(mInputServiceSensorListener);
+ mInputServiceSensorListener = null;
+ }
+ // For each sensor type check if it is still in use by other listeners.
+ for (Sensor s : sensorsRegistered) {
+ final int deviceId = s.getId();
+ final int sensorType = s.getType();
+ // See if we can disable the sensor
+ boolean enableSensor = false;
+ for (int i = 0; i < mInputSensorEventListeners.size(); i++) {
+ InputSensorEventListenerDelegate delegate =
+ mInputSensorEventListeners.get(i);
+ if (delegate.hasSensorRegistered(deviceId, sensorType)) {
+ enableSensor = true;
+ Slog.w(TAG, "device " + deviceId + " still uses sensor " + sensorType);
+ break;
+ }
+ }
+ // Sensor is not listened, disable it.
+ if (!enableSensor) {
+ if (DEBUG) {
+ Slog.d(TAG, "device " + deviceId + " sensor " + sensorType + " disabled");
+ }
+ mInputManager.disableSensor(deviceId, sensorType);
+ }
+ }
+ }
+ }
+
+ private boolean flush(SensorEventListener listener) {
+ synchronized (mInputSensorLock) {
+ int idx = findSensorEventListenerLocked(listener);
+ if (idx < 0) {
+ return false;
+ }
+ for (Sensor sensor : mInputSensorEventListeners.get(idx).getSensors()) {
+ final int deviceId = sensor.getId();
+ if (!mInputManager.flushSensor(deviceId, sensor.getType())) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Sensor Manager class associated with specific input device
+ */
+ public class InputSensorManager extends SensorManager {
+ // Input device ID that the sensors belong to
+ final int mId;
+
+ InputSensorManager(int deviceId) {
+ mId = deviceId;
+ }
+
+ @Override
+ public Sensor getDefaultSensor(int type) {
+ return getSensorForInputDevice(mId, type);
+ }
+
+ @Override
+ protected List<Sensor> getFullSensorList() {
+ return getFullSensorListForDevice(mId);
+ }
+
+ @Override
+ protected List<Sensor> getFullDynamicSensorList() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
+ int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) {
+ return registerListenerInternal(listener, sensor, delayUs,
+ maxBatchReportLatencyUs, handler);
+ }
+
+ @Override
+ protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
+ unregisterListenerInternal(listener, sensor);
+ }
+
+ @Override
+ protected boolean flushImpl(SensorEventListener listener) {
+ return flush(listener);
+ }
+
+ @Override
+ protected SensorDirectChannel createDirectChannelImpl(MemoryFile memoryFile,
+ HardwareBuffer hardwareBuffer) {
+ return null;
+ }
+
+ @Override
+ protected void destroyDirectChannelImpl(SensorDirectChannel channel) {
+
+ }
+
+ @Override
+ protected int configureDirectChannelImpl(SensorDirectChannel channel, Sensor s, int rate) {
+ return 0;
+ }
+
+ @Override
+ protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback,
+ Handler handler) {
+
+ }
+
+ @Override
+ protected void unregisterDynamicSensorCallbackImpl(
+ DynamicSensorCallback callback) {
+
+ }
+
+ @Override
+ protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+ return true;
+ }
+
+ @Override
+ protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
+ boolean disable) {
+ return true;
+ }
+
+ @Override
+ protected boolean initDataInjectionImpl(boolean enable) {
+ return false;
+ }
+
+ @Override
+ protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy,
+ long timestamp) {
+ return false;
+ }
+
+ @Override
+ protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
+ return false;
+ }
+ }
+
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index dd71cce39134..9d20f6d72d03 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -28,6 +28,7 @@ import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.SensorManager;
import android.os.BlockUntrustedTouchesMode;
import android.os.Build;
import android.os.CombinedVibrationEffect;
@@ -100,6 +101,7 @@ public final class InputManager {
private TabletModeChangedListener mTabletModeChangedListener;
private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
+ private InputDeviceSensorManager mInputDeviceSensorManager;
/**
* Broadcast Action: Query available keyboard layouts.
* <p>
@@ -291,6 +293,18 @@ public final class InputManager {
}
/**
+ * 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.
@@ -1152,6 +1166,86 @@ public final class InputManager {
}
/**
+ * 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();
+ }
+ }
+
+ /**
* 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.
@@ -1289,7 +1383,7 @@ public final class InputManager {
}
/**
- * Gets a vibrator service associated with an input device, assuming it has one.
+ * Gets a vibrator service associated with an input device, always create a new instance.
* @return The vibrator, never null.
* @hide
*/
@@ -1365,6 +1459,19 @@ public final class InputManager {
}
/**
+ * Gets a sensor manager service associated with an input device, always create 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);
+ }
+
+ /**
* Listens for changes in input devices.
*/
public interface InputDeviceListener {
diff --git a/core/java/android/hardware/input/InputSensorInfo.aidl b/core/java/android/hardware/input/InputSensorInfo.aidl
new file mode 100644
index 000000000000..d9a604ace8b1
--- /dev/null
+++ b/core/java/android/hardware/input/InputSensorInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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;
+
+parcelable InputSensorInfo;
diff --git a/core/java/android/hardware/input/InputSensorInfo.java b/core/java/android/hardware/input/InputSensorInfo.java
new file mode 100644
index 000000000000..99b18792aa33
--- /dev/null
+++ b/core/java/android/hardware/input/InputSensorInfo.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2020 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.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+/**
+ * This class represents a motion sensor for input devices.
+ *
+ * @hide
+ */
+@DataClass(
+ genToString = true,
+ genHiddenConstructor = true,
+ genHiddenConstDefs = true)
+public class InputSensorInfo implements Parcelable {
+
+ private @NonNull String mName;
+ private @NonNull String mVendor;
+ private int mVersion;
+ private int mHandle;
+ private int mType;
+ private float mMaxRange;
+ private float mResolution;
+ private float mPower;
+ private int mMinDelay;
+ private int mFifoReservedEventCount;
+ private int mFifoMaxEventCount;
+ private @NonNull String mStringType;
+ private @NonNull String mRequiredPermission;
+ private int mMaxDelay;
+ private int mFlags;
+ private int mId;
+
+
+
+ // Code below generated by codegen v1.0.20.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/InputSensorInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new InputSensorInfo.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public InputSensorInfo(
+ @NonNull String name,
+ @NonNull String vendor,
+ int version,
+ int handle,
+ int type,
+ float maxRange,
+ float resolution,
+ float power,
+ int minDelay,
+ int fifoReservedEventCount,
+ int fifoMaxEventCount,
+ @NonNull String stringType,
+ @NonNull String requiredPermission,
+ int maxDelay,
+ int flags,
+ int id) {
+ this.mName = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mName);
+ this.mVendor = vendor;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mVendor);
+ this.mVersion = version;
+ this.mHandle = handle;
+ this.mType = type;
+ this.mMaxRange = maxRange;
+ this.mResolution = resolution;
+ this.mPower = power;
+ this.mMinDelay = minDelay;
+ this.mFifoReservedEventCount = fifoReservedEventCount;
+ this.mFifoMaxEventCount = fifoMaxEventCount;
+ this.mStringType = stringType;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mStringType);
+ this.mRequiredPermission = requiredPermission;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mRequiredPermission);
+ this.mMaxDelay = maxDelay;
+ this.mFlags = flags;
+ this.mId = id;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getVendor() {
+ return mVendor;
+ }
+
+ @DataClass.Generated.Member
+ public int getVersion() {
+ return mVersion;
+ }
+
+ @DataClass.Generated.Member
+ public int getHandle() {
+ return mHandle;
+ }
+
+ @DataClass.Generated.Member
+ public int getType() {
+ return mType;
+ }
+
+ @DataClass.Generated.Member
+ public float getMaxRange() {
+ return mMaxRange;
+ }
+
+ @DataClass.Generated.Member
+ public float getResolution() {
+ return mResolution;
+ }
+
+ @DataClass.Generated.Member
+ public float getPower() {
+ return mPower;
+ }
+
+ @DataClass.Generated.Member
+ public int getMinDelay() {
+ return mMinDelay;
+ }
+
+ @DataClass.Generated.Member
+ public int getFifoReservedEventCount() {
+ return mFifoReservedEventCount;
+ }
+
+ @DataClass.Generated.Member
+ public int getFifoMaxEventCount() {
+ return mFifoMaxEventCount;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getStringType() {
+ return mStringType;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getRequiredPermission() {
+ return mRequiredPermission;
+ }
+
+ @DataClass.Generated.Member
+ public int getMaxDelay() {
+ return mMaxDelay;
+ }
+
+ @DataClass.Generated.Member
+ public int getFlags() {
+ return mFlags;
+ }
+
+ @DataClass.Generated.Member
+ public int getId() {
+ return mId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "InputSensorInfo { " +
+ "name = " + mName + ", " +
+ "vendor = " + mVendor + ", " +
+ "version = " + mVersion + ", " +
+ "handle = " + mHandle + ", " +
+ "type = " + mType + ", " +
+ "maxRange = " + mMaxRange + ", " +
+ "resolution = " + mResolution + ", " +
+ "power = " + mPower + ", " +
+ "minDelay = " + mMinDelay + ", " +
+ "fifoReservedEventCount = " + mFifoReservedEventCount + ", " +
+ "fifoMaxEventCount = " + mFifoMaxEventCount + ", " +
+ "stringType = " + mStringType + ", " +
+ "requiredPermission = " + mRequiredPermission + ", " +
+ "maxDelay = " + mMaxDelay + ", " +
+ "flags = " + mFlags + ", " +
+ "id = " + mId +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeString(mName);
+ dest.writeString(mVendor);
+ dest.writeInt(mVersion);
+ dest.writeInt(mHandle);
+ dest.writeInt(mType);
+ dest.writeFloat(mMaxRange);
+ dest.writeFloat(mResolution);
+ dest.writeFloat(mPower);
+ dest.writeInt(mMinDelay);
+ dest.writeInt(mFifoReservedEventCount);
+ dest.writeInt(mFifoMaxEventCount);
+ dest.writeString(mStringType);
+ dest.writeString(mRequiredPermission);
+ dest.writeInt(mMaxDelay);
+ dest.writeInt(mFlags);
+ dest.writeInt(mId);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected InputSensorInfo(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ String name = in.readString();
+ String vendor = in.readString();
+ int version = in.readInt();
+ int handle = in.readInt();
+ int type = in.readInt();
+ float maxRange = in.readFloat();
+ float resolution = in.readFloat();
+ float power = in.readFloat();
+ int minDelay = in.readInt();
+ int fifoReservedEventCount = in.readInt();
+ int fifoMaxEventCount = in.readInt();
+ String stringType = in.readString();
+ String requiredPermission = in.readString();
+ int maxDelay = in.readInt();
+ int flags = in.readInt();
+ int id = in.readInt();
+
+ this.mName = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mName);
+ this.mVendor = vendor;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mVendor);
+ this.mVersion = version;
+ this.mHandle = handle;
+ this.mType = type;
+ this.mMaxRange = maxRange;
+ this.mResolution = resolution;
+ this.mPower = power;
+ this.mMinDelay = minDelay;
+ this.mFifoReservedEventCount = fifoReservedEventCount;
+ this.mFifoMaxEventCount = fifoMaxEventCount;
+ this.mStringType = stringType;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mStringType);
+ this.mRequiredPermission = requiredPermission;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mRequiredPermission);
+ this.mMaxDelay = maxDelay;
+ this.mFlags = flags;
+ this.mId = id;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<InputSensorInfo> CREATOR
+ = new Parcelable.Creator<InputSensorInfo>() {
+ @Override
+ public InputSensorInfo[] newArray(int size) {
+ return new InputSensorInfo[size];
+ }
+
+ @Override
+ public InputSensorInfo createFromParcel(@NonNull Parcel in) {
+ return new InputSensorInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1605294854951L,
+ codegenVersion = "1.0.20",
+ sourceFile = "frameworks/base/core/java/android/hardware/input/InputSensorInfo.java",
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mVendor\nprivate int mVersion\nprivate int mHandle\nprivate int mType\nprivate float mMaxRange\nprivate float mResolution\nprivate float mPower\nprivate int mMinDelay\nprivate int mFifoReservedEventCount\nprivate int mFifoMaxEventCount\nprivate @android.annotation.NonNull java.lang.String mStringType\nprivate @android.annotation.NonNull java.lang.String mRequiredPermission\nprivate int mMaxDelay\nprivate int mFlags\nprivate int mId\nclass InputSensorInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index df96dc332ff0..f4b90e1f7b44 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -22,6 +22,7 @@ import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.SensorManager;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.os.Build;
@@ -71,13 +72,18 @@ public final class InputDevice implements Parcelable {
private final boolean mHasVibrator;
private final boolean mHasMicrophone;
private final boolean mHasButtonUnderPad;
+ private final boolean mHasSensor;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
+ @GuardedBy("mMotionRanges")
private Vibrator mVibrator; // guarded by mMotionRanges during initialization
@GuardedBy("mMotionRanges")
private VibratorManager mVibratorManager;
+ @GuardedBy("mMotionRanges")
+ private SensorManager mSensorManager;
+
/**
* A mask for input source classes.
*
@@ -442,7 +448,7 @@ public final class InputDevice implements Parcelable {
public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMicrophone,
- boolean hasButtonUnderPad) {
+ boolean hasButtonUnderPad, boolean hasSensor) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
@@ -457,10 +463,12 @@ public final class InputDevice implements Parcelable {
mHasVibrator = hasVibrator;
mHasMicrophone = hasMicrophone;
mHasButtonUnderPad = hasButtonUnderPad;
+ mHasSensor = hasSensor;
mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId);
}
private InputDevice(Parcel in) {
+ mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
mId = in.readInt();
mGeneration = in.readInt();
mControllerNumber = in.readInt();
@@ -471,10 +479,10 @@ public final class InputDevice implements Parcelable {
mIsExternal = in.readInt() != 0;
mSources = in.readInt();
mKeyboardType = in.readInt();
- mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
mHasVibrator = in.readInt() != 0;
mHasMicrophone = in.readInt() != 0;
mHasButtonUnderPad = in.readInt() != 0;
+ mHasSensor = in.readInt() != 0;
mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId);
int numRanges = in.readInt();
@@ -822,6 +830,26 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Gets the sensor manager service associated with the input device.
+ * Even if the device does not have a sensor, the result is never null.
+ * Use {@link SensorManager#getSensorList} to get a full list of all supported sensors.
+ *
+ * Note that the sensors associated with the device may be different from
+ * the system sensors, as typically they are builtin sensors physically attached to
+ * input devices.
+ *
+ * @return The sensor manager service associated with the device, never null.
+ */
+ public @NonNull SensorManager getSensorManager() {
+ synchronized (mMotionRanges) {
+ if (mSensorManager == null) {
+ mSensorManager = InputManager.getInstance().getInputDeviceSensorManager(mId);
+ }
+ }
+ return mSensorManager;
+ }
+
+ /**
* Returns true if input device is enabled.
* @return Whether the input device is enabled.
*/
@@ -869,6 +897,15 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Reports whether the device has a sensor.
+ * @return Whether the device has a sensor.
+ * @hide
+ */
+ public boolean hasSensor() {
+ return mHasSensor;
+ }
+
+ /**
* Sets the current pointer type.
* @param pointerType the type of the pointer icon.
* @hide
@@ -999,6 +1036,7 @@ public final class InputDevice implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
+ mKeyCharacterMap.writeToParcel(out, flags);
out.writeInt(mId);
out.writeInt(mGeneration);
out.writeInt(mControllerNumber);
@@ -1009,10 +1047,10 @@ public final class InputDevice implements Parcelable {
out.writeInt(mIsExternal ? 1 : 0);
out.writeInt(mSources);
out.writeInt(mKeyboardType);
- mKeyCharacterMap.writeToParcel(out, flags);
out.writeInt(mHasVibrator ? 1 : 0);
out.writeInt(mHasMicrophone ? 1 : 0);
out.writeInt(mHasButtonUnderPad ? 1 : 0);
+ out.writeInt(mHasSensor ? 1 : 0);
final int numRanges = mMotionRanges.size();
out.writeInt(numRanges);
@@ -1057,6 +1095,8 @@ public final class InputDevice implements Parcelable {
description.append(" Has Vibrator: ").append(mHasVibrator).append("\n");
+ description.append(" Has Sensor: ").append(mHasSensor).append("\n");
+
description.append(" Has mic: ").append(mHasMicrophone).append("\n");
description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 9f4e3e516ada..4eaa016df6f2 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -60,13 +60,17 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
// Not sure why, but JNI is complaining when I pass this through directly.
jboolean hasMic = deviceInfo.hasMic() ? JNI_TRUE : JNI_FALSE;
- ScopedLocalRef<jobject> inputDeviceObj(env, env->NewObject(gInputDeviceClassInfo.clazz,
- gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
- deviceInfo.getControllerNumber(), nameObj.get(),
- static_cast<int32_t>(ident.vendor), static_cast<int32_t>(ident.product),
- descriptorObj.get(), deviceInfo.isExternal(), deviceInfo.getSources(),
- deviceInfo.getKeyboardType(), kcmObj.get(), deviceInfo.hasVibrator(),
- hasMic, deviceInfo.hasButtonUnderPad()));
+ ScopedLocalRef<jobject>
+ inputDeviceObj(env,
+ env->NewObject(gInputDeviceClassInfo.clazz, gInputDeviceClassInfo.ctor,
+ deviceInfo.getId(), deviceInfo.getGeneration(),
+ deviceInfo.getControllerNumber(), nameObj.get(),
+ static_cast<int32_t>(ident.vendor),
+ static_cast<int32_t>(ident.product), descriptorObj.get(),
+ deviceInfo.isExternal(), deviceInfo.getSources(),
+ deviceInfo.getKeyboardType(), kcmObj.get(),
+ deviceInfo.hasVibrator(), hasMic,
+ deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor()));
const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (const InputDeviceInfo::MotionRange& range: ranges) {
@@ -87,7 +91,8 @@ int register_android_view_InputDevice(JNIEnv* env)
gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
- "(IIILjava/lang/String;IILjava/lang/String;ZIILandroid/view/KeyCharacterMap;ZZZ)V");
+ "(IIILjava/lang/String;IILjava/lang/"
+ "String;ZIILandroid/view/KeyCharacterMap;ZZZZ)V");
gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFFF)V");
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
new file mode 100644
index 000000000000..b319886ae466
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.view.InputDevice;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.annotations.GuardedBy;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link InputDeviceSensorManager}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:InputDeviceSensorManagerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class InputDeviceSensorManagerTest {
+ private static final String TAG = "InputDeviceSensorManagerTest";
+
+ private static final int DEVICE_ID = 1000;
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private TestLooper mTestLooper;
+ private ContextWrapper mContextSpy;
+ private InputManager mInputManager;
+ private InputDeviceSensorManager mSensorManager;
+ private IInputSensorEventListener mIInputSensorEventListener;
+ private final Object mLock = new Object();
+
+ @Mock private IInputManager mIInputManagerMock;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+ InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
+
+ when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
+
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+
+ when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ createInputDeviceWithSensor(DEVICE_ID));
+
+ when(mIInputManagerMock.getSensorList(eq(DEVICE_ID))).thenReturn(new InputSensorInfo[] {
+ createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
+ createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
+
+ when(mIInputManagerMock.enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
+ .thenReturn(true);
+
+ when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true);
+
+ mInputManager = mContextSpy.getSystemService(InputManager.class);
+ }
+
+ @After
+ public void tearDown() {
+ InputManager.clearInstance();
+ }
+
+ private class InputTestSensorEventListener implements SensorEventListener {
+ @GuardedBy("mLock")
+ private final BlockingQueue<SensorEvent> mEvents = new LinkedBlockingQueue<>();
+ InputTestSensorEventListener() {
+ super();
+ }
+
+ public SensorEvent waitForSensorEvent() {
+ try {
+ return mEvents.poll(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ fail("unexpectedly interrupted while waiting for SensorEvent");
+ return null;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (mLock) {
+ try {
+ mEvents.put(event);
+ } catch (InterruptedException ex) {
+ fail("interrupted while adding a SensorEvent to the queue");
+ }
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ }
+
+ private InputDevice createInputDeviceWithSensor(int id) {
+ InputDevice d = new InputDevice(id, 0 /* generation */, 0 /* controllerNumber */, "name",
+ 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
+ 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
+ false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
+ true /* hasSensor */);
+ assertTrue(d.hasSensor());
+ return d;
+ }
+
+ private InputSensorInfo createInputSensorInfo(int id, int type) {
+ InputSensorInfo info = new InputSensorInfo("name", "vendor", 0 /* version */,
+ 0 /* handle */, type, 100.0f /*maxRange */, 0.02f /* resolution */,
+ 0.8f /* power */, 1000 /* minDelay */, 0 /* fifoReservedEventCount */,
+ 0 /* fifoMaxEventCount */, "" /* stringType */, "" /* requiredPermission */,
+ 0 /* maxDelay */, 0 /* flags */, id);
+ return info;
+ }
+
+ private InputDevice getSensorDevice(int[] deviceIds) {
+ for (int i = 0; i < deviceIds.length; i++) {
+ InputDevice device = mInputManager.getInputDevice(deviceIds[i]);
+ if (device.hasSensor()) {
+ return device;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void getInputDeviceSensors_withExpectedType() throws Exception {
+ InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
+ assertTrue(device != null);
+
+ SensorManager sensorManager = device.getSensorManager();
+ List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(1, accelList.size());
+ assertEquals(DEVICE_ID, accelList.get(0).getId());
+ assertEquals(Sensor.TYPE_ACCELEROMETER, accelList.get(0).getType());
+
+ List<Sensor> gyroList = sensorManager.getSensorList(Sensor.TYPE_GYROSCOPE);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(1, gyroList.size());
+ assertEquals(DEVICE_ID, gyroList.get(0).getId());
+ assertEquals(Sensor.TYPE_GYROSCOPE, gyroList.get(0).getType());
+
+ }
+
+ @Test
+ public void getInputDeviceSensors_withUnexpectedType() throws Exception {
+ InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
+
+ assertTrue(device != null);
+ SensorManager sensorManager = device.getSensorManager();
+
+ List<Sensor> gameRotationList = sensorManager.getSensorList(
+ Sensor.TYPE_GAME_ROTATION_VECTOR);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(0, gameRotationList.size());
+
+ List<Sensor> gravityList = sensorManager.getSensorList(Sensor.TYPE_GRAVITY);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(0, gravityList.size());
+ }
+
+ @Test
+ public void testInputDeviceSensorListener() throws Exception {
+ InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
+ assertTrue(device != null);
+
+ SensorManager sensorManager = device.getSensorManager();
+ Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ assertEquals(Sensor.TYPE_ACCELEROMETER, sensor.getType());
+
+ doAnswer(invocation -> {
+ mIInputSensorEventListener = invocation.getArgument(0);
+ assertNotNull(mIInputSensorEventListener);
+ return true;
+ }).when(mIInputManagerMock).registerSensorListener(any());
+
+ InputTestSensorEventListener listener = new InputTestSensorEventListener();
+ assertTrue(sensorManager.registerListener(listener, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL));
+ verify(mIInputManagerMock).registerSensorListener(any());
+ verify(mIInputManagerMock).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
+ anyInt(), anyInt());
+
+ float[] values = new float[] {0.12f, 9.8f, 0.2f};
+ mIInputSensorEventListener.onInputSensorChanged(DEVICE_ID, Sensor.TYPE_ACCELEROMETER,
+ SensorManager.SENSOR_STATUS_ACCURACY_HIGH, /* timestamp */ 0x1234abcd, values);
+
+ SensorEvent event = listener.waitForSensorEvent();
+ assertNotNull(event);
+ assertEquals(0x1234abcd, event.timestamp);
+ assertEquals(values.length, event.values.length);
+ for (int i = 0; i < values.length; i++) {
+ assertEquals(values[i], event.values[i], 0.001f);
+ }
+
+ sensorManager.unregisterListener(listener);
+ verify(mIInputManagerMock).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
+ }
+
+}
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 6ac73b1972d4..bd0e56a39e03 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -445,3 +445,11 @@ led 0x07 MUTE
led 0x08 MISC
led 0x09 MAIL
led 0x0a CHARGING
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_05c4.idc b/data/keyboards/Vendor_054c_Product_05c4.idc
new file mode 100644
index 000000000000..2cb3f7b90fed
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4.idc
@@ -0,0 +1,35 @@
+# Copyright (C) 2020 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.
+
+#
+# Sony DS4 motion sensor configuration file.
+#
+
+# reporting mode 0 - continuous
+sensor.accelerometer.reportingMode = 0
+# The delay between sensor events corresponding to the lowest frequency in microsecond
+sensor.accelerometer.maxDelay = 100000
+# The minimum delay allowed between two events in microsecond
+sensor.accelerometer.minDelay = 5000
+# The power in mA used by this sensor while in use
+sensor.accelerometer.power = 1.5
+
+# reporting mode 0 - continuous
+sensor.gyroscope.reportingMode = 0
+# The delay between sensor events corresponding to the lowest frequency in microsecond
+sensor.gyroscope.maxDelay = 100000
+# The minimum delay allowed between two events in microsecond
+sensor.gyroscope.minDelay = 5000
+# The power in mA used by this sensor while in use
+sensor.gyroscope.power = 0.8
diff --git a/data/keyboards/Vendor_054c_Product_05c4.kl b/data/keyboards/Vendor_054c_Product_05c4.kl
index cd7ab1faec2e..c8b4fc363f9f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4.kl
@@ -68,3 +68,11 @@ key 0x13c BUTTON_MODE
# and this button will be equivalent to left mouse button
# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
key 0x13d BUTTON_1
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
index 19fcb86eb403..a877c4cc755f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
@@ -66,3 +66,11 @@ key 0x13c BUTTON_MODE
# In kernel versions >= 4.10, the touchpad is a separate input device,
# so the touchpad button click will not be covered by this layout.
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
index 19fcb86eb403..a877c4cc755f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
@@ -66,3 +66,11 @@ key 0x13c BUTTON_MODE
# In kernel versions >= 4.10, the touchpad is a separate input device,
# so the touchpad button click will not be covered by this layout.
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
index d38bdec5dada..1473c4e535f1 100644
--- a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
@@ -66,3 +66,11 @@ key 0x13c BUTTON_MODE
# In kernel versions >= 4.10, the touchpad is a separate input device,
# so the touchpad button click will not be covered by this layout.
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_09cc.idc b/data/keyboards/Vendor_054c_Product_09cc.idc
new file mode 100644
index 000000000000..2cb3f7b90fed
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc.idc
@@ -0,0 +1,35 @@
+# Copyright (C) 2020 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.
+
+#
+# Sony DS4 motion sensor configuration file.
+#
+
+# reporting mode 0 - continuous
+sensor.accelerometer.reportingMode = 0
+# The delay between sensor events corresponding to the lowest frequency in microsecond
+sensor.accelerometer.maxDelay = 100000
+# The minimum delay allowed between two events in microsecond
+sensor.accelerometer.minDelay = 5000
+# The power in mA used by this sensor while in use
+sensor.accelerometer.power = 1.5
+
+# reporting mode 0 - continuous
+sensor.gyroscope.reportingMode = 0
+# The delay between sensor events corresponding to the lowest frequency in microsecond
+sensor.gyroscope.maxDelay = 100000
+# The minimum delay allowed between two events in microsecond
+sensor.gyroscope.minDelay = 5000
+# The power in mA used by this sensor while in use
+sensor.gyroscope.power = 0.8
diff --git a/data/keyboards/Vendor_054c_Product_09cc.kl b/data/keyboards/Vendor_054c_Product_09cc.kl
index cd7ab1faec2e..c8b4fc363f9f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc.kl
@@ -68,3 +68,11 @@ key 0x13c BUTTON_MODE
# and this button will be equivalent to left mouse button
# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
key 0x13d BUTTON_1
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
index 19fcb86eb403..a877c4cc755f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
@@ -66,3 +66,11 @@ key 0x13c BUTTON_MODE
# In kernel versions >= 4.10, the touchpad is a separate input device,
# so the touchpad button click will not be covered by this layout.
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
index 19fcb86eb403..a877c4cc755f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
@@ -66,3 +66,11 @@ key 0x13c BUTTON_MODE
# In kernel versions >= 4.10, the touchpad is a separate input device,
# so the touchpad button click will not be covered by this layout.
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
index d38bdec5dada..1473c4e535f1 100644
--- a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
@@ -66,3 +66,11 @@ key 0x13c BUTTON_MODE
# In kernel versions >= 4.10, the touchpad is a separate input device,
# so the touchpad button click will not be covered by this layout.
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 122e282530a6..8b09c99d5200 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -41,11 +41,13 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
+import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.hardware.input.InputManagerInternal.LidSwitchCallback;
+import android.hardware.input.InputSensorInfo;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.media.AudioManager;
@@ -117,6 +119,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -184,12 +187,24 @@ public class InputManagerService extends IInputManager.Stub
private final List<TabletModeChangedListenerRecord> mTempTabletModeChangedListenersToNotify =
new ArrayList<>();
+ private final Object mSensorEventLock = new Object();
+ // List of currently registered sensor event listeners by process id
+ @GuardedBy("mSensorEventLock")
+ private final SparseArray<SensorEventListenerRecord> mSensorEventListeners =
+ new SparseArray<>();
+ private final List<SensorEventListenerRecord> mSensorEventListenersToNotify =
+ new ArrayList<>();
+ private final List<SensorEventListenerRecord> mSensorAccuracyListenersToNotify =
+ new ArrayList<>();
+
// Persistent data store. Must be locked each time during use.
private final PersistentDataStore mDataStore = new PersistentDataStore();
// List of currently registered input devices changed listeners by process id.
private Object mInputDevicesLock = new Object();
+ @GuardedBy("mInputDevicesLock")
private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock
+ @GuardedBy("mInputDevicesLock")
private InputDevice[] mInputDevices = new InputDevice[0];
private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners =
new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock
@@ -293,6 +308,11 @@ public class InputManagerService extends IInputManager.Stub
private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId);
private static native void nativeNotifyPortAssociationsChanged(long ptr);
private static native void nativeSetMotionClassifierEnabled(long ptr, boolean enabled);
+ private static native InputSensorInfo[] nativeGetSensorList(long ptr, int deviceId);
+ private static native boolean nativeFlushSensor(long ptr, int deviceId, int sensorType);
+ private static native boolean nativeEnableSensor(long ptr, int deviceId, int sensorType,
+ int samplingPeriodUs, int maxBatchReportLatencyUs);
+ private static native void nativeDisableSensor(long ptr, int deviceId, int sensorType);
// Maximum number of milliseconds to wait for input event injection.
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -2047,6 +2067,97 @@ public class InputManagerService extends IInputManager.Stub
nativeNotifyPortAssociationsChanged(mPtr);
}
+ @Override // Binder call
+ public InputSensorInfo[] getSensorList(int deviceId) {
+ InputSensorInfo[] sensors = nativeGetSensorList(mPtr, deviceId);
+ return sensors;
+ }
+
+ @Override // Binder call
+ public boolean registerSensorListener(IInputSensorEventListener listener) {
+ if (DEBUG) {
+ Slog.d(TAG, "registerSensorListener: listener=" + listener + " callingPid="
+ + Binder.getCallingPid());
+ }
+ if (listener == null) {
+ Slog.e(TAG, "listener must not be null");
+ return false;
+ }
+
+ synchronized (mInputDevicesLock) {
+ int callingPid = Binder.getCallingPid();
+ if (mSensorEventListeners.get(callingPid) != null) {
+ Slog.e(TAG, "The calling process " + callingPid + " has already "
+ + "registered an InputSensorEventListener.");
+ return false;
+ }
+
+ SensorEventListenerRecord record =
+ new SensorEventListenerRecord(callingPid, listener);
+ try {
+ IBinder binder = listener.asBinder();
+ binder.linkToDeath(record, 0);
+ } catch (RemoteException ex) {
+ // give up
+ throw new RuntimeException(ex);
+ }
+
+ mSensorEventListeners.put(callingPid, record);
+ }
+ return true;
+ }
+
+ @Override // Binder call
+ public void unregisterSensorListener(IInputSensorEventListener listener) {
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterSensorListener: listener=" + listener + " callingPid="
+ + Binder.getCallingPid());
+ }
+
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mInputDevicesLock) {
+ int callingPid = Binder.getCallingPid();
+ if (mSensorEventListeners.get(callingPid) != null) {
+ SensorEventListenerRecord record = mSensorEventListeners.get(callingPid);
+ if (record.getListener().asBinder() != listener.asBinder()) {
+ throw new IllegalArgumentException("listener is not registered");
+ }
+ mSensorEventListeners.remove(callingPid);
+ }
+ }
+ }
+
+ @Override // Binder call
+ public boolean flushSensor(int deviceId, int sensorType) {
+ synchronized (mInputDevicesLock) {
+ int callingPid = Binder.getCallingPid();
+ SensorEventListenerRecord listener = mSensorEventListeners.get(callingPid);
+ if (listener != null) {
+ return nativeFlushSensor(mPtr, deviceId, sensorType);
+ }
+ return false;
+ }
+ }
+
+ @Override // Binder call
+ public boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs,
+ int maxBatchReportLatencyUs) {
+ synchronized (mInputDevicesLock) {
+ return nativeEnableSensor(mPtr, deviceId, sensorType, samplingPeriodUs,
+ maxBatchReportLatencyUs);
+ }
+ }
+
+ @Override // Binder call
+ public void disableSensor(int deviceId, int sensorType) {
+ synchronized (mInputDevicesLock) {
+ nativeDisableSensor(mPtr, deviceId, sensorType);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -2245,6 +2356,46 @@ public class InputManagerService extends IInputManager.Stub
}
// Native callback.
+ private void notifySensorEvent(int deviceId, int sensorType, int accuracy, long timestamp,
+ float[] values) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifySensorEvent: deviceId=" + deviceId + " sensorType="
+ + sensorType + " values=" + Arrays.toString(values));
+ }
+ mSensorEventListenersToNotify.clear();
+ final int numListeners;
+ synchronized (mSensorEventLock) {
+ numListeners = mSensorEventListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mSensorEventListenersToNotify.add(
+ mSensorEventListeners.valueAt(i));
+ }
+ }
+ for (int i = 0; i < numListeners; i++) {
+ mSensorEventListenersToNotify.get(i).notifySensorEvent(deviceId, sensorType,
+ accuracy, timestamp, values);
+ }
+ mSensorEventListenersToNotify.clear();
+ }
+
+ // Native callback.
+ private void notifySensorAccuracy(int deviceId, int sensorType, int accuracy) {
+ mSensorAccuracyListenersToNotify.clear();
+ final int numListeners;
+ synchronized (mSensorEventLock) {
+ numListeners = mSensorEventListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mSensorAccuracyListenersToNotify.add(mSensorEventListeners.valueAt(i));
+ }
+ }
+ for (int i = 0; i < numListeners; i++) {
+ mSensorAccuracyListenersToNotify.get(i).notifySensorAccuracy(
+ deviceId, sensorType, accuracy);
+ }
+ mSensorAccuracyListenersToNotify.clear();
+ }
+
+ // Native callback.
final boolean filterInputEvent(InputEvent event, int policyFlags) {
synchronized (mInputFilterLock) {
if (mInputFilter != null) {
@@ -2776,6 +2927,56 @@ public class InputManagerService extends IInputManager.Stub
}
}
+ private void onSensorEventListenerDied(int pid) {
+ synchronized (mSensorEventLock) {
+ mSensorEventListeners.remove(pid);
+ }
+ }
+
+ private final class SensorEventListenerRecord implements DeathRecipient {
+ private final int mPid;
+ private final IInputSensorEventListener mListener;
+
+ SensorEventListenerRecord(int pid, IInputSensorEventListener listener) {
+ mPid = pid;
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Sensor event listener for pid " + mPid + " died.");
+ }
+ onSensorEventListenerDied(mPid);
+ }
+
+ public IInputSensorEventListener getListener() {
+ return mListener;
+ }
+
+ public void notifySensorEvent(int deviceId, int sensorType, int accuracy, long timestamp,
+ float[] values) {
+ try {
+ mListener.onInputSensorChanged(deviceId, sensorType, accuracy, timestamp,
+ values);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid
+ + " that sensor event notified, assuming it died.", ex);
+ binderDied();
+ }
+ }
+
+ public void notifySensorAccuracy(int deviceId, int sensorType, int accuracy) {
+ try {
+ mListener.onInputSensorAccuracyChanged(deviceId, sensorType, accuracy);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid
+ + " that sensor accuracy notified, assuming it died.", ex);
+ binderDied();
+ }
+ }
+ }
+
private final class VibratorToken implements DeathRecipient {
public final int mDeviceId;
public final IBinder mToken;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 27634bfb51ec..793d9fe607d9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -62,11 +62,11 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
-#include "com_android_server_power_PowerManagerService.h"
+#include "android_hardware_display_DisplayViewport.h"
#include "android_hardware_input_InputApplicationHandle.h"
#include "android_hardware_input_InputWindowHandle.h"
-#include "android_hardware_display_DisplayViewport.h"
#include "android_util_Binder.h"
+#include "com_android_server_power_PowerManagerService.h"
#include <vector>
@@ -101,6 +101,8 @@ static struct {
jmethodID notifyConnectionUnresponsive;
jmethodID notifyConnectionResponsive;
jmethodID notifyFocusChanged;
+ jmethodID notifySensorEvent;
+ jmethodID notifySensorAccuracy;
jmethodID notifyUntrustedTouch;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
@@ -157,6 +159,29 @@ static struct {
jmethodID size;
} gSparseArrayClassInfo;
+struct InputSensorInfoOffsets {
+ jclass clazz;
+ // fields
+ jfieldID name;
+ jfieldID vendor;
+ jfieldID version;
+ jfieldID handle;
+ jfieldID maxRange;
+ jfieldID resolution;
+ jfieldID power;
+ jfieldID minDelay;
+ jfieldID fifoReservedEventCount;
+ jfieldID fifoMaxEventCount;
+ jfieldID stringType;
+ jfieldID requiredPermission;
+ jfieldID maxDelay;
+ jfieldID flags;
+ jfieldID type;
+ jfieldID id;
+ // methods
+ jmethodID init;
+} gInputSensorInfo;
+
// --- Global functions ---
template<typename T>
@@ -267,6 +292,11 @@ public:
void notifyConnectionResponsive(const sp<IBinder>& token) override;
void notifyInputChannelBroken(const sp<IBinder>& token) override;
void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
+ void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+ const std::vector<float>& values) override;
+ void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) override;
void notifyUntrustedTouch(const std::string& obscuringPackage) override;
bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
@@ -820,6 +850,35 @@ void NativeInputManager::notifyFocusChanged(const sp<IBinder>& oldToken,
checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
}
+void NativeInputManager::notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+ const std::vector<float>& values) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ ALOGD("notifySensorEvent");
+#endif
+ ATRACE_CALL();
+ JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
+ jfloatArray arr = env->NewFloatArray(values.size());
+ env->SetFloatArrayRegion(arr, 0, values.size(), values.data());
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifySensorEvent, deviceId,
+ static_cast<jint>(sensorType), accuracy, timestamp, arr);
+ checkAndClearExceptionFromCallback(env, "notifySensorEvent");
+}
+
+void NativeInputManager::notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ ALOGD("notifySensorAccuracy");
+#endif
+ ATRACE_CALL();
+ JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifySensorAccuracy, deviceId,
+ static_cast<jint>(sensorType), accuracy);
+ checkAndClearExceptionFromCallback(env, "notifySensorAccuracy");
+}
+
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -1911,6 +1970,111 @@ static void nativeSetMotionClassifierEnabled(JNIEnv* /* env */, jclass /* clazz
im->setMotionClassifierEnabled(enabled);
}
+static jobject createInputSensorInfo(JNIEnv* env, jstring name, jstring vendor, jint version,
+ jint handle, jint type, jfloat maxRange, jfloat resolution,
+ jfloat power, jfloat minDelay, jint fifoReservedEventCount,
+ jint fifoMaxEventCount, jstring stringType,
+ jstring requiredPermission, jint maxDelay, jint flags,
+ jint id) {
+ // SensorInfo sensorInfo = new Sensor();
+ jobject sensorInfo = env->NewObject(gInputSensorInfo.clazz, gInputSensorInfo.init, "");
+
+ if (sensorInfo != NULL) {
+ env->SetObjectField(sensorInfo, gInputSensorInfo.name, name);
+ env->SetObjectField(sensorInfo, gInputSensorInfo.vendor, vendor);
+ env->SetIntField(sensorInfo, gInputSensorInfo.version, version);
+ env->SetIntField(sensorInfo, gInputSensorInfo.handle, handle);
+ env->SetFloatField(sensorInfo, gInputSensorInfo.maxRange, maxRange);
+ env->SetFloatField(sensorInfo, gInputSensorInfo.resolution, resolution);
+ env->SetFloatField(sensorInfo, gInputSensorInfo.power, power);
+ env->SetIntField(sensorInfo, gInputSensorInfo.minDelay, minDelay);
+ env->SetIntField(sensorInfo, gInputSensorInfo.fifoReservedEventCount,
+ fifoReservedEventCount);
+ env->SetIntField(sensorInfo, gInputSensorInfo.fifoMaxEventCount, fifoMaxEventCount);
+ env->SetObjectField(sensorInfo, gInputSensorInfo.requiredPermission, requiredPermission);
+ env->SetIntField(sensorInfo, gInputSensorInfo.maxDelay, maxDelay);
+ env->SetIntField(sensorInfo, gInputSensorInfo.flags, flags);
+ env->SetObjectField(sensorInfo, gInputSensorInfo.stringType, stringType);
+ env->SetIntField(sensorInfo, gInputSensorInfo.type, type);
+ env->SetIntField(sensorInfo, gInputSensorInfo.id, id);
+ }
+ return sensorInfo;
+}
+
+static jobjectArray nativeGetSensorList(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ std::vector<InputDeviceInfo> devices = im->getInputManager()->getReader()->getInputDevices();
+ // Find the input device by deviceId
+ auto it = std::find_if(devices.begin(), devices.end(),
+ [deviceId](InputDeviceInfo& info) { return info.getId() == deviceId; });
+
+ if (it == devices.end()) {
+ // Return an array of size 0
+ return env->NewObjectArray(0, gInputSensorInfo.clazz, nullptr);
+ }
+
+ std::vector<InputDeviceSensorType> types = it->getSensorTypes();
+ jobjectArray arr = env->NewObjectArray(types.size(), gInputSensorInfo.clazz, nullptr);
+ for (int i = 0; i < types.size(); i++) {
+ const InputDeviceSensorInfo* sensorInfo = it->getSensorInfo(types[i]);
+ if (sensorInfo == nullptr) {
+ ALOGW("Failed to get input device %d sensor info for type %s", deviceId,
+ NamedEnum::string(types[i]).c_str());
+ continue;
+ }
+
+ jobject info =
+ createInputSensorInfo(env, env->NewStringUTF(sensorInfo->name.c_str()),
+ env->NewStringUTF(sensorInfo->vendor.c_str()),
+ (jint)sensorInfo->version, 0 /* handle */,
+ (jint)sensorInfo->type, (jfloat)sensorInfo->maxRange,
+ (jfloat)sensorInfo->resolution, (jfloat)sensorInfo->power,
+ (jfloat)sensorInfo->minDelay,
+ (jint)sensorInfo->fifoReservedEventCount,
+ (jint)sensorInfo->fifoMaxEventCount,
+ env->NewStringUTF(sensorInfo->stringType.c_str()),
+ env->NewStringUTF("") /* requiredPermission */,
+ (jint)sensorInfo->maxDelay, (jint)sensorInfo->flags,
+ (jint)sensorInfo->id);
+ env->SetObjectArrayElement(arr, i, info);
+ env->DeleteLocalRef(info);
+ }
+ return arr;
+}
+
+static jboolean nativeEnableSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint sensorType, jint samplingPeriodUs,
+ jint maxBatchReportLatencyUs) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ return im->getInputManager()
+ ->getReader()
+ ->enableSensor(deviceId, static_cast<InputDeviceSensorType>(sensorType),
+ std::chrono::microseconds(samplingPeriodUs),
+ std::chrono::microseconds(maxBatchReportLatencyUs));
+ return true;
+}
+
+static void nativeDisableSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint sensorType) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->disableSensor(deviceId,
+ static_cast<InputDeviceSensorType>(
+ sensorType));
+}
+
+static jboolean nativeFlushSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint sensorType) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->flushSensor(deviceId,
+ static_cast<InputDeviceSensorType>(sensorType));
+ return im->getInputManager()->getDispatcher()->flushSensor(deviceId,
+ static_cast<InputDeviceSensorType>(
+ sensorType));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -1976,6 +2140,11 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeCanDispatchToDisplay", "(JII)Z", (void*)nativeCanDispatchToDisplay},
{"nativeNotifyPortAssociationsChanged", "(J)V", (void*)nativeNotifyPortAssociationsChanged},
{"nativeSetMotionClassifierEnabled", "(JZ)V", (void*)nativeSetMotionClassifierEnabled},
+ {"nativeGetSensorList", "(JI)[Landroid/hardware/input/InputSensorInfo;",
+ (void*)nativeGetSensorList},
+ {"nativeEnableSensor", "(JIIII)Z", (void*)nativeEnableSensor},
+ {"nativeDisableSensor", "(JII)V", (void*)nativeDisableSensor},
+ {"nativeFlushSensor", "(JII)Z", (void*)nativeFlushSensor},
};
#define FIND_CLASS(var, className) \
@@ -2021,6 +2190,10 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
"notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V");
+ GET_METHOD_ID(gServiceClassInfo.notifySensorEvent, clazz, "notifySensorEvent", "(IIIJ[F)V");
+
+ GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
+
GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
"(Ljava/lang/String;)V");
@@ -2145,6 +2318,33 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gSparseArrayClassInfo.valueAt, gSparseArrayClassInfo.clazz, "valueAt",
"(I)Ljava/lang/Object;");
GET_METHOD_ID(gSparseArrayClassInfo.size, gSparseArrayClassInfo.clazz, "size", "()I");
+ // InputSensorInfo
+ // android.hardware.input.InputDeviceSensorInfo
+ FIND_CLASS(clazz, "android/hardware/input/InputSensorInfo");
+ gInputSensorInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
+
+ GET_FIELD_ID(gInputSensorInfo.name, gInputSensorInfo.clazz, "mName", "Ljava/lang/String;");
+ GET_FIELD_ID(gInputSensorInfo.vendor, gInputSensorInfo.clazz, "mVendor", "Ljava/lang/String;");
+ GET_FIELD_ID(gInputSensorInfo.version, gInputSensorInfo.clazz, "mVersion", "I");
+ GET_FIELD_ID(gInputSensorInfo.handle, gInputSensorInfo.clazz, "mHandle", "I");
+ GET_FIELD_ID(gInputSensorInfo.maxRange, gInputSensorInfo.clazz, "mMaxRange", "F");
+ GET_FIELD_ID(gInputSensorInfo.resolution, gInputSensorInfo.clazz, "mResolution", "F");
+ GET_FIELD_ID(gInputSensorInfo.power, gInputSensorInfo.clazz, "mPower", "F");
+ GET_FIELD_ID(gInputSensorInfo.minDelay, gInputSensorInfo.clazz, "mMinDelay", "I");
+ GET_FIELD_ID(gInputSensorInfo.fifoReservedEventCount, gInputSensorInfo.clazz,
+ "mFifoReservedEventCount", "I");
+ GET_FIELD_ID(gInputSensorInfo.fifoMaxEventCount, gInputSensorInfo.clazz, "mFifoMaxEventCount",
+ "I");
+ GET_FIELD_ID(gInputSensorInfo.stringType, gInputSensorInfo.clazz, "mStringType",
+ "Ljava/lang/String;");
+ GET_FIELD_ID(gInputSensorInfo.requiredPermission, gInputSensorInfo.clazz, "mRequiredPermission",
+ "Ljava/lang/String;");
+ GET_FIELD_ID(gInputSensorInfo.maxDelay, gInputSensorInfo.clazz, "mMaxDelay", "I");
+ GET_FIELD_ID(gInputSensorInfo.flags, gInputSensorInfo.clazz, "mFlags", "I");
+ GET_FIELD_ID(gInputSensorInfo.type, gInputSensorInfo.clazz, "mType", "I");
+ GET_FIELD_ID(gInputSensorInfo.id, gInputSensorInfo.clazz, "mId", "I");
+
+ GET_METHOD_ID(gInputSensorInfo.init, gInputSensorInfo.clazz, "<init>", "()V");
return 0;
}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 68adacd8478f..32ca7b58c48c 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -172,6 +172,7 @@ public class VibratorServiceTest {
@After
public void tearDown() throws Exception {
+ InputManager.clearInstance();
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
}
@@ -838,7 +839,7 @@ public class VibratorServiceTest {
private InputDevice createInputDeviceWithVibrator(int id) {
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, /* hasVibrator= */ true, false, false);
+ null, /* hasVibrator= */ true, false, false, false /* hasSensor */);
}
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index fa8e36741bcc..ac93ff691925 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -44,6 +44,7 @@ import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -89,6 +90,11 @@ public class InputDeviceDelegateTest {
mContextSpy, new Handler(mTestLooper.getLooper()));
}
+ @After
+ public void tearDown() throws Exception {
+ InputManager.clearInstance();
+ }
+
@Test
public void onInputDeviceAdded_withSettingsDisabled_ignoresNewDevice() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
@@ -286,6 +292,6 @@ public class InputDeviceDelegateTest {
private InputDevice createInputDevice(int id, boolean hasVibrator) {
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, hasVibrator, false, false);
+ null, hasVibrator, false, false, false /* hasSensor */);
}
}