Merge changes from topic "SensorManager"
* changes: Add feature for inputdevice sensor manager. Add motion sensor configuration for Sony DS4.
This commit is contained in:
committed by
Android (Google) Code Review
commit
6656f0fc5c
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -922,6 +923,30 @@ public final class Sensor {
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
661
core/java/android/hardware/input/InputDeviceSensorManager.java
Normal file
661
core/java/android/hardware/input/InputDeviceSensorManager.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -290,6 +292,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.
|
||||
*
|
||||
@@ -1151,6 +1165,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.
|
||||
@@ -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
|
||||
*/
|
||||
@@ -1364,6 +1458,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.
|
||||
*/
|
||||
|
||||
19
core/java/android/hardware/input/InputSensorInfo.aidl
Normal file
19
core/java/android/hardware/input/InputSensorInfo.aidl
Normal file
@@ -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;
|
||||
330
core/java/android/hardware/input/InputSensorInfo.java
Normal file
330
core/java/android/hardware/input/InputSensorInfo.java
Normal file
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
@@ -821,6 +829,26 @@ public final class InputDevice implements Parcelable {
|
||||
return mVibratorManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -868,6 +896,15 @@ public final class InputDevice implements Parcelable {
|
||||
return mHasButtonUnderPad;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -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(" (");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
35
data/keyboards/Vendor_054c_Product_05c4.idc
Normal file
35
data/keyboards/Vendor_054c_Product_05c4.idc
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
35
data/keyboards/Vendor_054c_Product_09cc.idc
Normal file
35
data/keyboards/Vendor_054c_Product_09cc.idc
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -2244,6 +2355,46 @@ public class InputManagerService extends IInputManager.Stub
|
||||
mWindowManagerCallbacks.notifyWindowResponsive(token);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user