diff options
| -rw-r--r-- | api/current.txt | 11 | ||||
| -rw-r--r-- | api/system-current.txt | 13 | ||||
| -rw-r--r-- | api/test-current.txt | 11 | ||||
| -rw-r--r-- | core/java/android/content/Intent.java | 15 | ||||
| -rw-r--r-- | core/java/android/hardware/Sensor.java | 54 | ||||
| -rw-r--r-- | core/java/android/hardware/SensorManager.java | 118 | ||||
| -rw-r--r-- | core/java/android/hardware/SystemSensorManager.java | 226 | ||||
| -rw-r--r-- | core/jni/android_hardware_SensorManager.cpp | 86 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/SensorNotificationService.java | 84 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 1 |
11 files changed, 606 insertions, 15 deletions
diff --git a/api/current.txt b/api/current.txt index 90ebb94dcfc7..50f8b21919c7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -13358,6 +13358,7 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 7b74b1fa5c4a..b876ca95715e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -13758,6 +13758,7 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 0000d3f289fa..57332a295b21 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -13366,6 +13366,7 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 @@ package android.hardware { 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 426dafb4190d..158a284112e7 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2618,8 +2618,7 @@ public class Intent implements Parcelable, Cloneable { * 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 class Intent implements Parcelable, Cloneable { 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 c710f2172ec5..841e5b0c9732 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 final class Sensor { 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 @@ public final class Sensor { 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 @@ public final class Sensor { } 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 @@ public final class Sensor { private String mRequiredPermission; private int mMaxDelay; private int mFlags; + private UUID mUuid; Sensor() { } @@ -851,6 +879,13 @@ public final class Sensor { } /** + * @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 @@ public final class Sensor { 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 5d405f92e380..f0b17c30d914 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -377,6 +377,12 @@ public abstract class SensorManager { 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 @@ public abstract class SensorManager { } /** + * 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 @@ public abstract class SensorManager { /** @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 2fe8fb6f71ce..e91195c2b87c 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.lang.ref.WeakReference; 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 @@ import java.util.List; * @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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { } } + 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { mActiveSensors.put(handle, false); removeSensorEvent(sensor); } else { - // it should never happen -- just ignore. + // sensor just disconnected -- just ignore. } } } @@ -456,6 +661,11 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { 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 @@ public class SystemSensorManager extends SensorManager { 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 2e5cda069538..a582492485cb 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 @@ namespace android { struct SensorOffsets { + jclass clazz; jfieldID name; jfieldID vendor; jfieldID version; @@ -60,8 +62,13 @@ struct SensorOffsets 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 @@ struct SensorOffsets 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 @@ nativeClassInit (JNIEnv *_env, jclass _this) "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 @@ nativeCreate 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 @@ nativeGetSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager, jobject s 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 @@ private: // 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 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { "(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 3aa7de57288b..1c3db104dc3c 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 000000000000..06104646cd87 --- /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 361c25143b9e..9f320e458ccd 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -979,6 +979,7 @@ public final class SystemServer { Slog.i(TAG, "Gesture Launcher Service"); mSystemServiceManager.startService(GestureLauncherService.class); } + mSystemServiceManager.startService(SensorNotificationService.class); } traceBeginAndSlog("StartDiskStatsService"); |