Merge "Local geomagnetic field update to sensor" into oc-dev
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 572a287..ea1d01b 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -131,6 +131,64 @@
*/
public static final int TYPE_SAMPLING = 0x10004;
+ /**
+ * Local geo-magnetic Field.
+ *
+ * Additional into to sensor hardware. Local geomagnetic field information based on
+ * device geo location. This type is primarily for for magnetic field calibration and rotation
+ * vector sensor fusion.
+ *
+ * float[3]: strength (uT), declination and inclination angle (rad).
+ * @hide
+ */
+ public static final int TYPE_LOCAL_GEOMAGNETIC_FIELD = 0x30000;
+
+ /**
+ * Local gravity acceleration strength.
+ *
+ * Additional info to sensor hardware for accelerometer calibration.
+ *
+ * float: gravitational acceleration norm in m/s^2.
+ * @hide
+ */
+ public static final int TYPE_LOCAL_GRAVITY = 0x30001;
+
+ /**
+ * Device dock state.
+ *
+ * Additional info to sensor hardware indicating dock states of device.
+ *
+ * int32_t: dock state following definition of {@link android.content.Intent#EXTRA_DOCK_STATE}.
+ * Undefined values are ignored.
+ * @hide
+ */
+ public static final int TYPE_DOCK_STATE = 0x30002;
+
+ /**
+ * High performance mode.
+ *
+ * Additional info to sensor hardware. Device is able to use up more power and take more
+ * resources to improve throughput and latency in high performance mode. One possible use case
+ * is virtual reality, when sensor latency need to be carefully controlled.
+ *
+ * int32_t: 1 or 0, denoting device is in or out of high performance mode, respectively.
+ * Other values are ignored.
+ * @hide
+ */
+ public static final int TYPE_HIGH_PERFORMANCE_MODE = 0x30003;
+
+ /**
+ * Magnetic field calibration hint.
+ *
+ * Additional info to sensor hardware. Device is notified when manually triggered magnetic field
+ * calibration procedure is started or stopped. The calibration procedure is assumed timed out
+ * after 1 minute from start, even if an explicit stop is not received.
+ *
+ * int32_t: 1 for calibration start, 0 for stop, other values are ignored.
+ * @hide
+ */
+ public static final int TYPE_MAGNETIC_FIELD_CALIBRATION = 0x30004;
+
SensorAdditionalInfo(
Sensor aSensor, int aType, int aSerial, int [] aIntValues, float [] aFloatValues) {
sensor = aSensor;
@@ -139,4 +197,18 @@
intValues = aIntValues;
floatValues = aFloatValues;
}
+
+ /** @hide */
+ public static SensorAdditionalInfo createLocalGeomagneticField(
+ float strength, float declination, float inclination) {
+ if (strength < 10 || strength > 100 // much beyond extreme values on earth
+ || declination < 0 || declination > Math.PI
+ || inclination < -Math.PI / 2 || inclination > Math.PI / 2) {
+ throw new IllegalArgumentException("Geomagnetic field info out of range");
+ }
+
+ return new SensorAdditionalInfo(
+ null, TYPE_LOCAL_GEOMAGNETIC_FIELD, 0,
+ null, new float[] { strength, declination, inclination});
+ }
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index a6930b0..1dc6478 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1927,4 +1927,12 @@
}
return delay;
}
+
+ /** @hide */
+ public boolean setOperationParameter(SensorAdditionalInfo parameter) {
+ return setOperationParameterImpl(parameter);
+ }
+
+ /** @hide */
+ protected abstract boolean setOperationParameterImpl(SensorAdditionalInfo parameter);
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 7029847..0677179 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -67,6 +67,9 @@
private static native int nativeConfigDirectChannel(
long nativeInstance, int channelHandle, int sensorHandle, int rate);
+ private static native int nativeSetOperationParameter(
+ long nativeInstance, int type, float[] floatValues, int[] intValues);
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static boolean sNativeClassInited = false;
@@ -928,4 +931,9 @@
}
}
+
+ protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
+ return nativeSetOperationParameter(
+ mNativeInstance, parameter.type, parameter.floatValues, parameter.intValues) == 0;
+ }
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index dae4310..520302e 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -282,6 +282,25 @@
return mgr->configureDirectChannel(channelHandle, sensorHandle, rate);
}
+static jint nativeSetOperationParameter(JNIEnv *_env, jclass _this, jlong sensorManager,
+ jint type, jfloatArray floats, jintArray ints) {
+ SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+ Vector<float> floatVector;
+ Vector<int32_t> int32Vector;
+
+ if (floats != nullptr) {
+ floatVector.resize(_env->GetArrayLength(floats));
+ _env->GetFloatArrayRegion(floats, 0, _env->GetArrayLength(floats), floatVector.editArray());
+ }
+
+ if (ints != nullptr) {
+ int32Vector.resize(_env->GetArrayLength(ints));
+ _env->GetIntArrayRegion(ints, 0, _env->GetArrayLength(ints), int32Vector.editArray());
+ }
+
+ return mgr->setOperationParameter(type, floatVector, int32Vector);
+}
+
//----------------------------------------------------------------------------
class Receiver : public LooperCallback {
@@ -499,6 +518,10 @@
{"nativeConfigDirectChannel",
"(JIII)I",
(void*)nativeConfigDirectChannel },
+
+ {"nativeSetOperationParameter",
+ "(JI[F[I)I",
+ (void*)nativeSetOperationParameter },
};
static const JNINativeMethod gBaseEventQueueMethods[] = {
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 0610464..7f5befa 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -20,25 +20,46 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.hardware.GeomagneticField;
import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
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;
+public class SensorNotificationService extends SystemService
+ implements SensorEventListener, LocationListener {
+ private static final boolean DBG = false;
private static final String TAG = "SensorNotificationService";
- private Context mContext;
+ private static final long MINUTE_IN_MS = 60 * 1000;
+ private static final long KM_IN_M = 1000;
+
+ private static final long LOCATION_MIN_TIME = 30 * MINUTE_IN_MS;
+ private static final long LOCATION_MIN_DISTANCE = 100 * KM_IN_M;
+
+ private static final String PROPERTY_USE_MOCKED_LOCATION =
+ "sensor.notification.use_mocked"; // max key length is 32
+
+ private static final long MILLIS_2010_1_1 = 1262358000000l;
+
+ private Context mContext;
private SensorManager mSensorManager;
+ private LocationManager mLocationManager;
private Sensor mMetaSensor;
+ // for rate limiting
+ private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
+
public SensorNotificationService(Context context) {
super(context);
mContext = context;
@@ -50,7 +71,6 @@
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) {
@@ -60,13 +80,28 @@
SensorManager.SENSOR_DELAY_FASTEST);
}
}
+
+ if (phase == PHASE_BOOT_COMPLETED) {
+ // LocationManagerService is initialized after PHASE_THIRD_PARTY_APPS_CAN_START
+ mLocationManager =
+ (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+ if (mLocationManager == null) {
+ if (DBG) Slog.d(TAG, "Cannot obtain location service.");
+ } else {
+ mLocationManager.requestLocationUpdates(
+ LocationManager.PASSIVE_PROVIDER,
+ LOCATION_MIN_TIME,
+ LOCATION_MIN_DISTANCE,
+ this);
+ }
+ }
}
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");
+ if (DBG) Slog.d(TAG, "dynamic sensor broadcast sent");
}
@Override
@@ -77,8 +112,62 @@
}
@Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ public void onLocationChanged(Location location) {
+ if (DBG) Slog.d(TAG, String.format(
+ "Location is (%f, %f), h %f, acc %f, mocked %b",
+ location.getLatitude(), location.getLongitude(),
+ location.getAltitude(), location.getAccuracy(),
+ location.isFromMockProvider()));
+ // lat long == 0 usually means invalid location
+ if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+ return;
+ }
+
+ // update too often, ignore
+ if (SystemClock.elapsedRealtime() - mLocalGeomagneticFieldUpdateTime < 10 * MINUTE_IN_MS) {
+ return;
+ }
+
+ long time = System.currentTimeMillis();
+ // Mocked location should not be used. Except in test, only use mocked location
+ // Wrong system clock also gives bad values so ignore as well.
+ if (useMockedLocation() == location.isFromMockProvider() || time < MILLIS_2010_1_1) {
+ return;
+ }
+
+ GeomagneticField field = new GeomagneticField(
+ (float) location.getLatitude(), (float) location.getLongitude(),
+ (float) location.getAltitude(), time);
+ if (DBG) Slog.d(TAG, String.format(
+ "Nominal mag field, norm %fuT, decline %f deg, incline %f deg",
+ field.getFieldStrength() / 1000, field.getDeclination(), field.getInclination()));
+
+ try {
+ SensorAdditionalInfo info = SensorAdditionalInfo.createLocalGeomagneticField(
+ field.getFieldStrength() / 1000, // convert from nT to uT
+ (float)(field.getDeclination() * Math.PI / 180), // from degree to rad
+ (float)(field.getInclination() * Math.PI / 180)); // from degree to rad
+ if (info != null) {
+ mSensorManager.setOperationParameter(info);
+ mLocalGeomagneticFieldUpdateTime = SystemClock.elapsedRealtime();
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Invalid local geomagnetic field, ignore.");
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+ @Override
+ public void onProviderEnabled(String provider) {}
+ @Override
+ public void onProviderDisabled(String provider) {}
+
+ private boolean useMockedLocation() {
+ return "false".equals(System.getProperty(PROPERTY_USE_MOCKED_LOCATION, "false"));
}
}