Merge "Dynamic Sensor Discovery Implementation"
diff --git a/api/current.txt b/api/current.txt
index 90ebb94d..50f8b21 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13358,6 +13358,7 @@
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
+ method public java.util.UUID getUuid();
method public java.lang.String getVendor();
method public int getVersion();
method public boolean isWakeUpSensor();
@@ -13446,6 +13447,7 @@
method public static void getAngleChange(float[], float[], float[]);
method public android.hardware.Sensor getDefaultSensor(int);
method public android.hardware.Sensor getDefaultSensor(int, boolean);
+ method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
method public static float getInclination(float[]);
method public static float[] getOrientation(float[], float[]);
method public static void getQuaternionFromVector(float[], float[]);
@@ -13453,6 +13455,8 @@
method public static void getRotationMatrixFromVector(float[], float[]);
method public java.util.List<android.hardware.Sensor> getSensorList(int);
method public deprecated int getSensors();
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
method public deprecated boolean registerListener(android.hardware.SensorListener, int);
method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13461,6 +13465,7 @@
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+ method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13525,6 +13530,12 @@
field public static final float STANDARD_GRAVITY = 9.80665f;
}
+ public static abstract class SensorManager.DynamicSensorConnectionCallback {
+ ctor public SensorManager.DynamicSensorConnectionCallback();
+ method public void onDynamicSensorConnected(android.hardware.Sensor);
+ method public void onDynamicSensorDisconnected(android.hardware.Sensor);
+ }
+
public final class TriggerEvent {
field public android.hardware.Sensor sensor;
field public long timestamp;
diff --git a/api/system-current.txt b/api/system-current.txt
index 7b74b1f..b876ca9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -13758,6 +13758,7 @@
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
+ method public java.util.UUID getUuid();
method public java.lang.String getVendor();
method public int getVersion();
method public boolean isDataInjectionSupported();
@@ -13768,6 +13769,7 @@
field public static final int REPORTING_MODE_SPECIAL_TRIGGER = 3; // 0x3
field public static final java.lang.String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer";
field public static final java.lang.String STRING_TYPE_AMBIENT_TEMPERATURE = "android.sensor.ambient_temperature";
+ field public static final java.lang.String STRING_TYPE_DYNAMIC_SENSOR_META = "android.sensor.dynamic_sensor_meta";
field public static final java.lang.String STRING_TYPE_GAME_ROTATION_VECTOR = "android.sensor.game_rotation_vector";
field public static final java.lang.String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR = "android.sensor.geomagnetic_rotation_vector";
field public static final java.lang.String STRING_TYPE_GRAVITY = "android.sensor.gravity";
@@ -13795,6 +13797,7 @@
field public static final int TYPE_ACCELEROMETER = 1; // 0x1
field public static final int TYPE_ALL = -1; // 0xffffffff
field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd
+ field public static final int TYPE_DYNAMIC_SENSOR_META = 32; // 0x20
field public static final int TYPE_GAME_ROTATION_VECTOR = 15; // 0xf
field public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; // 0x14
field public static final int TYPE_GRAVITY = 9; // 0x9
@@ -13849,6 +13852,7 @@
method public static void getAngleChange(float[], float[], float[]);
method public android.hardware.Sensor getDefaultSensor(int);
method public android.hardware.Sensor getDefaultSensor(int, boolean);
+ method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
method public static float getInclination(float[]);
method public static float[] getOrientation(float[], float[]);
method public static void getQuaternionFromVector(float[], float[]);
@@ -13858,6 +13862,8 @@
method public deprecated int getSensors();
method public boolean initDataInjection(boolean);
method public boolean injectSensorData(android.hardware.Sensor, float[], int, long);
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
method public deprecated boolean registerListener(android.hardware.SensorListener, int);
method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13866,6 +13872,7 @@
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+ method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13930,6 +13937,12 @@
field public static final float STANDARD_GRAVITY = 9.80665f;
}
+ public static abstract class SensorManager.DynamicSensorConnectionCallback {
+ ctor public SensorManager.DynamicSensorConnectionCallback();
+ method public void onDynamicSensorConnected(android.hardware.Sensor);
+ method public void onDynamicSensorDisconnected(android.hardware.Sensor);
+ }
+
public final class TriggerEvent {
field public android.hardware.Sensor sensor;
field public long timestamp;
diff --git a/api/test-current.txt b/api/test-current.txt
index 0000d3f..57332a2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -13366,6 +13366,7 @@
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
+ method public java.util.UUID getUuid();
method public java.lang.String getVendor();
method public int getVersion();
method public boolean isWakeUpSensor();
@@ -13454,6 +13455,7 @@
method public static void getAngleChange(float[], float[], float[]);
method public android.hardware.Sensor getDefaultSensor(int);
method public android.hardware.Sensor getDefaultSensor(int, boolean);
+ method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
method public static float getInclination(float[]);
method public static float[] getOrientation(float[], float[]);
method public static void getQuaternionFromVector(float[], float[]);
@@ -13461,6 +13463,8 @@
method public static void getRotationMatrixFromVector(float[], float[]);
method public java.util.List<android.hardware.Sensor> getSensorList(int);
method public deprecated int getSensors();
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
method public deprecated boolean registerListener(android.hardware.SensorListener, int);
method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13469,6 +13473,7 @@
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+ method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13533,6 +13538,12 @@
field public static final float STANDARD_GRAVITY = 9.80665f;
}
+ public static abstract class SensorManager.DynamicSensorConnectionCallback {
+ ctor public SensorManager.DynamicSensorConnectionCallback();
+ method public void onDynamicSensorConnected(android.hardware.Sensor);
+ method public void onDynamicSensorDisconnected(android.hardware.Sensor);
+ }
+
public final class TriggerEvent {
field public android.hardware.Sensor sensor;
field public long timestamp;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 426dafb..158a284 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2618,8 +2618,7 @@
* turned off</li>
* </ul>
*
- * <p class="note">This is a protected intent that can only be sent
- * by the system.
+ * <p class="note">This is a protected intent that can only be sent by the system.</p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE";
@@ -3210,6 +3209,18 @@
public static final String
ACTION_OPEN_EXTERNAL_DIRECTORY = "android.intent.action.OPEN_EXTERNAL_DIRECTORY";
+ /**
+ * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
+ * exisiting sensor being disconnected.
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.</p>
+ *
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String
+ ACTION_DYNAMIC_SENSOR_CHANGED = "android.intent.action.DYNAMIC_SENSOR_CHANGED";
+
/** {@hide} */
public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index c710f21..841e5b0 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -17,8 +17,10 @@
package android.hardware;
-import android.os.Build;
import android.annotation.SystemApi;
+import android.os.Build;
+
+import java.util.UUID;
/**
* Class representing a sensor. Use {@link SensorManager#getSensorList} to get
@@ -622,6 +624,29 @@
public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat";
/**
+ * A constant describing a dynamic sensor meta event sensor.
+ *
+ * A sensor event of this type is received when a dynamic sensor is added to or removed from
+ * the system. This sensor type should always use special trigger report mode ({@code
+ * SensorManager.REPORTING_MODE_SPECIAL_TRIGGER}).
+ *
+ * @hide This sensor is expected to be used only by system services.
+ */
+ @SystemApi
+ public static final int TYPE_DYNAMIC_SENSOR_META = 32;
+
+ /**
+ * A constant string describing a dynamic sensor meta event sensor.
+ *
+ * @see #TYPE_DYNAMIC_SENSOR_META
+ *
+ * @hide This sensor is expected to only be used by the system service
+ */
+ @SystemApi
+ public static final String STRING_TYPE_DYNAMIC_SENSOR_META =
+ "android.sensor.dynamic_sensor_meta";
+
+ /**
* A constant describing all sensor types.
*/
@@ -708,7 +733,11 @@
1, // SENSOR_TYPE_PICK_UP_GESTURE
1, // SENSOR_TYPE_WRIST_TILT_GESTURE
1, // SENSOR_TYPE_DEVICE_ORIENTATION
- 16, // SENSOR_TYPE_POSE_6DOF
+ 16,// SENSOR_TYPE_POSE_6DOF
+ 1, // SENSOR_TYPE_STATIONARY_DETECT
+ 1, // SENSOR_TYPE_MOTION_DETECT
+ 1, // SENSOR_TYPE_HEART_BEAT
+ 2, // SENSOR_TYPE_DYNAMIC_SENSOR_META
};
/**
@@ -734,12 +763,10 @@
}
int offset = sensor.mType;
if (offset >= sSensorReportingModes.length) {
- // we don't know about this sensor, so this is probably a
- // vendor-defined sensor, in that case, we don't know how many value
- // it has
- // so we return the maximum and assume the app will know.
- // FIXME: sensor HAL should advertise how much data is returned per
- // sensor
+ // we don't know about this sensor, so this is probably a vendor-defined sensor, in that
+ // case, we don't know how many value it has so we return the maximum and assume the app
+ // will know.
+ // FIXME: sensor HAL should advertise how much data is returned per sensor
return 16;
}
return sSensorReportingModes[offset];
@@ -763,6 +790,7 @@
private String mRequiredPermission;
private int mMaxDelay;
private int mFlags;
+ private UUID mUuid;
Sensor() {
}
@@ -851,6 +879,13 @@
}
/**
+ * @return The type of this sensor as a string.
+ */
+ public UUID getUuid() {
+ return mUuid;
+ }
+
+ /**
* @hide
* @return The permission required to access this sensor. If empty, no permission is required.
*/
@@ -1033,6 +1068,9 @@
case TYPE_DEVICE_ORIENTATION:
mStringType = STRING_TYPE_DEVICE_ORIENTATION;
return true;
+ case TYPE_DYNAMIC_SENSOR_META:
+ mStringType = STRING_TYPE_DYNAMIC_SENSOR_META;
+ return true;
default:
return false;
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 5d405f9..f0b17c30 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -377,6 +377,12 @@
protected abstract List<Sensor> getFullSensorList();
/**
+ * Gets the full list of dynamic sensors that are available.
+ * @hide
+ */
+ protected abstract List<Sensor> getFullDynamicSensorList();
+
+ /**
* @return available sensors.
* @deprecated This method is deprecated, use
* {@link SensorManager#getSensorList(int)} instead
@@ -430,6 +436,38 @@
}
/**
+ * Use this method to get a list of available dynamic sensors of a certain type.
+ * Make multiple calls to get sensors of different types or use
+ * {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all dynamic sensors.
+ *
+ * <p class="note">
+ * NOTE: Both wake-up and non wake-up sensors matching the given type are
+ * returned. Check {@link Sensor#isWakeUpSensor()} to know the wake-up properties
+ * of the returned {@link Sensor}.
+ * </p>
+ *
+ * @param type of sensors requested
+ *
+ * @return a list of dynamic sensors matching the requested type.
+ *
+ * @see Sensor
+ */
+ public List<Sensor> getDynamicSensorList(int type) {
+ // cache the returned lists the first time
+ final List<Sensor> fullList = getFullDynamicSensorList();
+ if (type == Sensor.TYPE_ALL) {
+ return Collections.unmodifiableList(fullList);
+ } else {
+ List<Sensor> list = new ArrayList();
+ for (Sensor i : fullList) {
+ if (i.getType() == type)
+ list.add(i);
+ }
+ return Collections.unmodifiableList(list);
+ }
+ }
+
+ /**
* Use this method to get the default sensor for a given type. Note that the
* returned sensor could be a composite sensor, and its data could be
* averaged or filtered. If you need to access the raw sensors use
@@ -841,6 +879,86 @@
/** @hide */
protected abstract boolean flushImpl(SensorEventListener listener);
+
+ /**
+ * Used for receiving notifications from the SensorManager when dynamic sensors are connected or
+ * disconnected.
+ */
+ public static abstract class DynamicSensorConnectionCallback {
+ /**
+ * Called when there is a dynamic sensor being connected to the system.
+ *
+ * @param sensor the newly connected sensor. See {@link android.hardware.Sensor Sensor}.
+ */
+ public void onDynamicSensorConnected(Sensor sensor) {}
+
+ /**
+ * Called when there is a dynamic sensor being disconnected from the system.
+ *
+ * @param sensor the disconnected sensor. See {@link android.hardware.Sensor Sensor}.
+ */
+ public void onDynamicSensorDisconnected(Sensor sensor) {}
+ }
+
+
+ /**
+ * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+ * registration with the already registered callback object will have no additional effect.
+ *
+ * @param callback An object that implements the
+ * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * DynamicSensorConnectionCallback}
+ * interface for receiving callbacks.
+ * @see #addDynamicSensorCallback(DynamicSensorConnectionCallback, Handler)
+ *
+ * @throws IllegalArgumentException when callback is null.
+ */
+ public void registerDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+ registerDynamicSensorCallback(callback, null);
+ }
+
+ /**
+ * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+ * registration with the already registered callback object will have no additional effect.
+ *
+ * @param callback An object that implements the
+ * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * DynamicSensorConnectionCallback} interface for receiving callbacks.
+ * @param handler The {@link android.os.Handler Handler} the {@link
+ * android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * sensor connection events} will be delivered to.
+ *
+ * @throws IllegalArgumentException when callback is null.
+ */
+ public void registerDynamicSensorCallback(
+ DynamicSensorConnectionCallback callback, Handler handler) {
+ registerDynamicSensorCallbackImpl(callback, handler);
+ }
+
+ /**
+ * Remove a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * DynamicSensorConnectionCallback} to stop sending dynamic sensor connection events to that
+ * callback.
+ *
+ * @param callback An object that implements the
+ * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * DynamicSensorConnectionCallback}
+ * interface for receiving callbacks.
+ */
+ public void unregisterDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+ unregisterDynamicSensorCallbackImpl(callback);
+ }
+
+ /** @hide */
+ protected abstract void registerDynamicSensorCallbackImpl(
+ DynamicSensorConnectionCallback callback, Handler handler);
+
+ /** @hide */
+ protected abstract void unregisterDynamicSensorCallbackImpl(
+ DynamicSensorConnectionCallback callback);
+
/**
* <p>
* Computes the inclination matrix <b>I</b> as well as the rotation matrix
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 2fe8fb6..e91195c 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -17,7 +17,10 @@
package android.hardware;
import android.Manifest;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
@@ -32,6 +35,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Sensor manager implementation that communicates with the built-in
@@ -40,10 +44,14 @@
* @hide
*/
public class SystemSensorManager extends SensorManager {
+ //TODO: disable extra logging before release
+ private static boolean DEBUG_DYNAMIC_SENSOR = true;
+
private static native void nativeClassInit();
private static native long nativeCreate(String opPackageName);
private static native boolean nativeGetSensorAtIndex(long nativeInstance,
Sensor sensor, int index);
+ private static native void nativeGetDynamicSensors(long nativeInstance, List<Sensor> list);
private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
private static boolean sSensorModuleInitialized = false;
@@ -52,7 +60,10 @@
private final Object mLock = new Object();
private final ArrayList<Sensor> mFullSensorsList = new ArrayList<>();
- private final SparseArray<Sensor> mHandleToSensor = new SparseArray<>();
+ private List<Sensor> mFullDynamicSensorsList = new ArrayList<>();
+ private boolean mDynamicSensorListDirty = true;
+
+ private final HashMap<Integer, Sensor> mHandleToSensor = new HashMap<>();
// Listener list
private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
@@ -60,6 +71,11 @@
private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
new HashMap<TriggerEventListener, TriggerEventQueue>();
+ // Dynamic Sensor callbacks
+ private HashMap<DynamicSensorConnectionCallback, Handler>
+ mDynamicSensorCallbacks = new HashMap<>();
+ private BroadcastReceiver mDynamicSensorBroadcastReceiver;
+
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
private final int mTargetSdkLevel;
@@ -84,7 +100,7 @@
Sensor sensor = new Sensor();
if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
mFullSensorsList.add(sensor);
- mHandleToSensor.append(sensor.getHandle(), sensor);
+ mHandleToSensor.put(sensor.getHandle(), sensor);
}
}
}
@@ -96,6 +112,15 @@
return mFullSensorsList;
}
+ /** @hide */
+ @Override
+ protected List<Sensor> getFullDynamicSensorList() {
+ // only set up broadcast receiver if the application tries to find dynamic sensors or
+ // explicitly register a DynamicSensorConnectionCallback
+ setupDynamicSensorBroadcastReceiver();
+ updateDynamicSensorList();
+ return mFullDynamicSensorsList;
+ }
/** @hide */
@Override
@@ -274,6 +299,187 @@
}
}
+ private void cleanupSensorConnection(Sensor sensor) {
+ mHandleToSensor.remove(sensor.getHandle());
+
+ if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ synchronized(mTriggerListeners) {
+ for (TriggerEventListener l: mTriggerListeners.keySet()) {
+ if (DEBUG_DYNAMIC_SENSOR){
+ Log.i(TAG, "removed trigger listener" + l.toString() +
+ " due to sensor disconnection");
+ }
+ cancelTriggerSensorImpl(l, sensor, true);
+ }
+ }
+ } else {
+ synchronized(mSensorListeners) {
+ for (SensorEventListener l: mSensorListeners.keySet()) {
+ if (DEBUG_DYNAMIC_SENSOR){
+ Log.i(TAG, "removed event listener" + l.toString() +
+ " due to sensor disconnection");
+ }
+ unregisterListenerImpl(l, sensor);
+ }
+ }
+ }
+ }
+
+ private void updateDynamicSensorList() {
+ synchronized(mFullDynamicSensorsList) {
+ if (mDynamicSensorListDirty) {
+ List<Sensor> list = new ArrayList<>();
+ nativeGetDynamicSensors(mNativeInstance, list);
+
+ final List<Sensor> updatedList = new ArrayList<>();
+ final List<Sensor> addedList = new ArrayList<>();
+ final List<Sensor> removedList = new ArrayList<>();
+
+ boolean changed = diffSortedSensorList(
+ mFullDynamicSensorsList, list, updatedList, addedList, removedList);
+
+ if (changed) {
+ if (DEBUG_DYNAMIC_SENSOR) {
+ Log.i(TAG, "DYNS dynamic sensor list cached should be updated");
+ }
+ mFullDynamicSensorsList = updatedList;
+
+ for (Sensor s: addedList) {
+ mHandleToSensor.put(s.getHandle(), s);
+ }
+
+ Handler mainHandler = new Handler(mContext.getMainLooper());
+
+ for (Map.Entry<DynamicSensorConnectionCallback, Handler> entry :
+ mDynamicSensorCallbacks.entrySet()) {
+ final DynamicSensorConnectionCallback callback = entry.getKey();
+ Handler handler =
+ entry.getValue() == null ? mainHandler : entry.getValue();
+
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (Sensor s: addedList) {
+ callback.onDynamicSensorConnected(s);
+ }
+ for (Sensor s: removedList) {
+ callback.onDynamicSensorDisconnected(s);
+ }
+ }
+ });
+ }
+
+ for (Sensor s: removedList) {
+ cleanupSensorConnection(s);
+ }
+ }
+
+ mDynamicSensorListDirty = false;
+ }
+ }
+ }
+
+ private void setupDynamicSensorBroadcastReceiver() {
+ if (mDynamicSensorBroadcastReceiver == null) {
+ mDynamicSensorBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction() == Intent.ACTION_DYNAMIC_SENSOR_CHANGED) {
+ if (DEBUG_DYNAMIC_SENSOR) {
+ Log.i(TAG, "DYNS received DYNAMIC_SENSOR_CHANED broadcast");
+ }
+ // Dynamic sensors probably changed
+ mDynamicSensorListDirty = true;
+ updateDynamicSensorList();
+ }
+ }
+ };
+
+ IntentFilter filter = new IntentFilter("dynamic_sensor_change");
+ filter.addAction(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
+ mContext.registerReceiver(mDynamicSensorBroadcastReceiver, filter);
+ }
+ }
+
+ private void teardownDynamicSensorBroadcastReceiver() {
+ mDynamicSensorCallbacks.clear();
+ mContext.unregisterReceiver(mDynamicSensorBroadcastReceiver);
+ mDynamicSensorBroadcastReceiver = null;
+ }
+
+ /** @hide */
+ protected void registerDynamicSensorCallbackImpl(
+ DynamicSensorConnectionCallback callback, Handler handler) {
+ if (DEBUG_DYNAMIC_SENSOR) {
+ Log.i(TAG, "DYNS Register dynamic sensor callback");
+ }
+
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (mDynamicSensorCallbacks.containsKey(callback)) {
+ // has been already registered, ignore
+ return;
+ }
+
+ setupDynamicSensorBroadcastReceiver();
+ mDynamicSensorCallbacks.put(callback, handler);
+ }
+
+ /** @hide */
+ protected void unregisterDynamicSensorCallbackImpl(
+ DynamicSensorConnectionCallback callback) {
+ if (DEBUG_DYNAMIC_SENSOR) {
+ Log.i(TAG, "Removing dynamic sensor listerner");
+ }
+ mDynamicSensorCallbacks.remove(callback);
+ }
+
+ /*
+ * Find the difference of two List<Sensor> assuming List are sorted by handle of sensor,
+ * assuming the input list is already sorted by handle. Inputs are ol and nl; outputs are
+ * updated, added and removed. Any of the output lists can be null in case the result is not
+ * interested.
+ */
+ private static boolean diffSortedSensorList(
+ List<Sensor> oldList, List<Sensor> newList, List<Sensor> updated,
+ List<Sensor> added, List<Sensor> removed) {
+
+ boolean changed = false;
+
+ int i = 0, j = 0;
+ while (true) {
+ if (j < oldList.size() && ( i >= newList.size() ||
+ newList.get(i).getHandle() > oldList.get(j).getHandle()) ) {
+ changed = true;
+ if (removed != null) {
+ removed.add(oldList.get(j));
+ }
+ ++j;
+ } else if (i < newList.size() && ( j >= oldList.size() ||
+ newList.get(i).getHandle() < oldList.get(j).getHandle())) {
+ changed = true;
+ if (added != null) {
+ added.add(newList.get(i));
+ }
+ if (updated != null) {
+ updated.add(newList.get(i));
+ }
+ ++i;
+ } else if (i < newList.size() && j < oldList.size() &&
+ newList.get(i).getHandle() == oldList.get(j).getHandle()) {
+ if (updated != null) {
+ updated.add(oldList.get(j));
+ }
+ ++i;
+ ++j;
+ } else {
+ break;
+ }
+ }
+ return changed;
+ }
+
/*
* BaseEventQueue is the communication channel with the sensor service,
* SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
@@ -297,7 +503,6 @@
private long nSensorEventQueue;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
- protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
private final CloseGuard mCloseGuard = CloseGuard.get();
private final float[] mScratch = new float[16];
protected final SystemSensorManager mManager;
@@ -348,7 +553,7 @@
mActiveSensors.put(handle, false);
removeSensorEvent(sensor);
} else {
- // it should never happen -- just ignore.
+ // sensor just disconnected -- just ignore.
}
}
}
@@ -456,6 +661,11 @@
protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
long timestamp) {
final Sensor sensor = mManager.mHandleToSensor.get(handle);
+ if (sensor == null) {
+ // sensor disconnected
+ return;
+ }
+
SensorEvent t = null;
synchronized (mSensorsEvents) {
t = mSensorsEvents.get(handle);
@@ -485,6 +695,10 @@
protected void dispatchFlushCompleteEvent(int handle) {
if (mListener instanceof SensorEventListener2) {
final Sensor sensor = mManager.mHandleToSensor.get(handle);
+ if (sensor == null) {
+ // sensor disconnected
+ return;
+ }
((SensorEventListener2)mListener).onFlushCompleted(sensor);
}
return;
@@ -523,6 +737,10 @@
protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
long timestamp) {
final Sensor sensor = mManager.mHandleToSensor.get(handle);
+ if (sensor == null) {
+ // sensor disconnected
+ return;
+ }
TriggerEvent t = null;
synchronized (mTriggerEvents) {
t = mTriggerEvents.get(handle);
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 2e5cda0..a582492 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -23,6 +23,7 @@
#include <utils/Log.h>
#include <utils/Looper.h>
+#include <utils/Vector.h>
#include <gui/Sensor.h>
#include <gui/SensorManager.h>
@@ -45,6 +46,7 @@
struct SensorOffsets
{
+ jclass clazz;
jfieldID name;
jfieldID vendor;
jfieldID version;
@@ -60,8 +62,13 @@
jfieldID maxDelay;
jfieldID flags;
jmethodID setType;
+ jmethodID init;
} gSensorOffsets;
+struct ListOffsets {
+ jclass clazz;
+ jmethodID add;
+} gListOffsets;
/*
* The method below are not thread-safe and not intended to be
@@ -70,8 +77,10 @@
static void
nativeClassInit (JNIEnv *_env, jclass _this)
{
- jclass sensorClass = _env->FindClass("android/hardware/Sensor");
+ //android.hardware.Sensor
SensorOffsets& sensorOffsets = gSensorOffsets;
+ jclass sensorClass = (jclass) _env->NewGlobalRef(_env->FindClass("android/hardware/Sensor"));
+ sensorOffsets.clazz = sensorClass;
sensorOffsets.name = _env->GetFieldID(sensorClass, "mName", "Ljava/lang/String;");
sensorOffsets.vendor = _env->GetFieldID(sensorClass, "mVendor", "Ljava/lang/String;");
sensorOffsets.version = _env->GetFieldID(sensorClass, "mVersion", "I");
@@ -88,7 +97,15 @@
"Ljava/lang/String;");
sensorOffsets.maxDelay = _env->GetFieldID(sensorClass, "mMaxDelay", "I");
sensorOffsets.flags = _env->GetFieldID(sensorClass, "mFlags", "I");
+
sensorOffsets.setType = _env->GetMethodID(sensorClass, "setType", "(I)Z");
+ sensorOffsets.init = _env->GetMethodID(sensorClass, "<init>", "()V");
+
+ // java.util.List;
+ ListOffsets& listOffsets = gListOffsets;
+ jclass listClass = (jclass) _env->NewGlobalRef(_env->FindClass("java/util/List"));
+ listOffsets.clazz = listClass;
+ listOffsets.add = _env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
}
/**
@@ -141,6 +158,46 @@
return (jlong) &SensorManager::getInstanceForPackage(String16(opPackageNameUtf.c_str()));
}
+static jobject
+translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nativeSensor) {
+ const SensorOffsets& sensorOffsets(gSensorOffsets);
+
+ if (sensor == NULL) {
+ // Sensor sensor = new Sensor();
+ sensor = env->NewObject(sensorOffsets.clazz, sensorOffsets.init, "");
+ }
+
+ if (sensor != NULL) {
+ jstring name = env->NewStringUTF(nativeSensor.getName().string());
+ jstring vendor = env->NewStringUTF(nativeSensor.getVendor().string());
+ jstring requiredPermission =
+ env->NewStringUTF(nativeSensor.getRequiredPermission().string());
+
+ env->SetObjectField(sensor, sensorOffsets.name, name);
+ env->SetObjectField(sensor, sensorOffsets.vendor, vendor);
+ env->SetIntField(sensor, sensorOffsets.version, nativeSensor.getVersion());
+ env->SetIntField(sensor, sensorOffsets.handle, nativeSensor.getHandle());
+ env->SetFloatField(sensor, sensorOffsets.range, nativeSensor.getMaxValue());
+ env->SetFloatField(sensor, sensorOffsets.resolution, nativeSensor.getResolution());
+ env->SetFloatField(sensor, sensorOffsets.power, nativeSensor.getPowerUsage());
+ env->SetIntField(sensor, sensorOffsets.minDelay, nativeSensor.getMinDelay());
+ env->SetIntField(sensor, sensorOffsets.fifoReservedEventCount,
+ nativeSensor.getFifoReservedEventCount());
+ env->SetIntField(sensor, sensorOffsets.fifoMaxEventCount,
+ nativeSensor.getFifoMaxEventCount());
+ env->SetObjectField(sensor, sensorOffsets.requiredPermission,
+ requiredPermission);
+ env->SetIntField(sensor, sensorOffsets.maxDelay, nativeSensor.getMaxDelay());
+ env->SetIntField(sensor, sensorOffsets.flags, nativeSensor.getFlags());
+ if (env->CallBooleanMethod(sensor, sensorOffsets.setType, nativeSensor.getType())
+ == JNI_FALSE) {
+ jstring stringType = getInternedString(env, &nativeSensor.getStringType());
+ env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
+ }
+ }
+ return sensor;
+}
+
static jboolean
nativeGetSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensor, jint index)
{
@@ -180,6 +237,24 @@
return true;
}
+static void
+nativeGetDynamicSensors(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensorList) {
+
+ SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+ const ListOffsets& listOffsets(gListOffsets);
+
+ Vector<Sensor> nativeList;
+
+ mgr->getDynamicSensorList(nativeList);
+
+ ALOGI("DYNS native SensorManager.getDynamicSensorList return %d sensors", nativeList.size());
+ for (size_t i = 0; i < nativeList.size(); ++i) {
+ jobject sensor = translateNativeSensorToJavaSensor(env, NULL, nativeList[i]);
+ // add to list
+ env->CallBooleanMethod(sensorList, listOffsets.add, sensor);
+ }
+}
+
static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong sensorManager) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
return mgr->isDataInjectionEnabled();
@@ -235,6 +310,11 @@
// step-counter returns a uint64, but the java API only deals with floats
float value = float(buffer[i].u64.step_counter);
env->SetFloatArrayRegion(mScratch, 0, 1, &value);
+ } else if (buffer[i].type == SENSOR_TYPE_DYNAMIC_SENSOR_META) {
+ float value[2];
+ value[0] = buffer[i].dynamic_sensor_meta.connected ? 1.f: 0.f;
+ value[1] = float(buffer[i].dynamic_sensor_meta.handle);
+ env->SetFloatArrayRegion(mScratch, 0, 2, value);
} else {
env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
}
@@ -355,6 +435,10 @@
"(JLandroid/hardware/Sensor;I)Z",
(void*)nativeGetSensorAtIndex },
+ {"nativeGetDynamicSensors",
+ "(JLjava/util/List;)V",
+ (void*)nativeGetDynamicSensors },
+
{"nativeIsDataInjectionEnabled",
"(J)Z",
(void*)nativeIsDataInjectionEnabled},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3aa7de5..1c3db10 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -420,6 +420,8 @@
<protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
<protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
+ <protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
new file mode 100644
index 0000000..0610464
--- /dev/null
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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 com.android.server;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+public class SensorNotificationService extends SystemService implements SensorEventListener {
+ //TODO: set DBG to false or remove Slog before release
+ private static final boolean DBG = true;
+ private static final String TAG = "SensorNotificationService";
+ private Context mContext;
+
+ private SensorManager mSensorManager;
+ private Sensor mMetaSensor;
+
+ public SensorNotificationService(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ public void onStart() {
+ LocalServices.addService(SensorNotificationService.class, this);
+ }
+
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ // start
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ mMetaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DYNAMIC_SENSOR_META);
+ if (mMetaSensor == null) {
+ if (DBG) Slog.d(TAG, "Cannot obtain dynamic meta sensor, not supported.");
+ } else {
+ mSensorManager.registerListener(this, mMetaSensor,
+ SensorManager.SENSOR_DELAY_FASTEST);
+ }
+ }
+ }
+
+ private void broadcastDynamicSensorChanged() {
+ Intent i = new Intent(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
+ i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); // avoid waking up manifest receivers
+ mContext.sendBroadcastAsUser(i, UserHandle.ALL);
+ if (DBG) Slog.d(TAG, "DYNS sent dynamic sensor broadcast");
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor == mMetaSensor) {
+ broadcastDynamicSensorChanged();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+ }
+}
+
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 361c251..9f320e4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -979,6 +979,7 @@
Slog.i(TAG, "Gesture Launcher Service");
mSystemServiceManager.startService(GestureLauncherService.class);
}
+ mSystemServiceManager.startService(SensorNotificationService.class);
}
traceBeginAndSlog("StartDiskStatsService");