Loading core/java/android/hardware/SensorAdditionalInfo.java +72 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,64 @@ public class SensorAdditionalInfo { */ 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; Loading @@ -139,4 +197,18 @@ public class SensorAdditionalInfo { 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}); } } core/java/android/hardware/SensorManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1927,4 +1927,12 @@ public abstract class SensorManager { } return delay; } /** @hide */ public boolean setOperationParameter(SensorAdditionalInfo parameter) { return setOperationParameterImpl(parameter); } /** @hide */ protected abstract boolean setOperationParameterImpl(SensorAdditionalInfo parameter); } core/java/android/hardware/SystemSensorManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -67,6 +67,9 @@ public class SystemSensorManager extends SensorManager { 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; Loading Loading @@ -928,4 +931,9 @@ public class SystemSensorManager extends SensorManager { } } protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) { return nativeSetOperationParameter( mNativeInstance, parameter.type, parameter.floatValues, parameter.intValues) == 0; } } core/jni/android_hardware_SensorManager.cpp +23 −0 Original line number Diff line number Diff line Loading @@ -282,6 +282,25 @@ static jint nativeConfigDirectChannel(JNIEnv *_env, jclass _this, jlong sensorMa 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 { Loading Loading @@ -499,6 +518,10 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeConfigDirectChannel", "(JIII)I", (void*)nativeConfigDirectChannel }, {"nativeSetOperationParameter", "(JI[F[I)I", (void*)nativeSetOperationParameter }, }; static const JNINativeMethod gBaseEventQueueMethods[] = { Loading services/core/java/com/android/server/SensorNotificationService.java +96 −7 Original line number Diff line number Diff line Loading @@ -20,25 +20,46 @@ import android.content.BroadcastReceiver; 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; Loading @@ -50,7 +71,6 @@ public class SensorNotificationService extends SystemService implements SensorEv 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) { Loading @@ -60,13 +80,28 @@ public class SensorNotificationService extends SystemService implements SensorEv 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 Loading @@ -77,8 +112,62 @@ public class SensorNotificationService extends SystemService implements SensorEv } @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")); } } Loading
core/java/android/hardware/SensorAdditionalInfo.java +72 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,64 @@ public class SensorAdditionalInfo { */ 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; Loading @@ -139,4 +197,18 @@ public class SensorAdditionalInfo { 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}); } }
core/java/android/hardware/SensorManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1927,4 +1927,12 @@ public abstract class SensorManager { } return delay; } /** @hide */ public boolean setOperationParameter(SensorAdditionalInfo parameter) { return setOperationParameterImpl(parameter); } /** @hide */ protected abstract boolean setOperationParameterImpl(SensorAdditionalInfo parameter); }
core/java/android/hardware/SystemSensorManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -67,6 +67,9 @@ public class SystemSensorManager extends SensorManager { 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; Loading Loading @@ -928,4 +931,9 @@ public class SystemSensorManager extends SensorManager { } } protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) { return nativeSetOperationParameter( mNativeInstance, parameter.type, parameter.floatValues, parameter.intValues) == 0; } }
core/jni/android_hardware_SensorManager.cpp +23 −0 Original line number Diff line number Diff line Loading @@ -282,6 +282,25 @@ static jint nativeConfigDirectChannel(JNIEnv *_env, jclass _this, jlong sensorMa 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 { Loading Loading @@ -499,6 +518,10 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeConfigDirectChannel", "(JIII)I", (void*)nativeConfigDirectChannel }, {"nativeSetOperationParameter", "(JI[F[I)I", (void*)nativeSetOperationParameter }, }; static const JNINativeMethod gBaseEventQueueMethods[] = { Loading
services/core/java/com/android/server/SensorNotificationService.java +96 −7 Original line number Diff line number Diff line Loading @@ -20,25 +20,46 @@ import android.content.BroadcastReceiver; 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; Loading @@ -50,7 +71,6 @@ public class SensorNotificationService extends SystemService implements SensorEv 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) { Loading @@ -60,13 +80,28 @@ public class SensorNotificationService extends SystemService implements SensorEv 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 Loading @@ -77,8 +112,62 @@ public class SensorNotificationService extends SystemService implements SensorEv } @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")); } }