Merge "Local geomagnetic field update to sensor" into oc-dev

am: 33bbdda3d3

Change-Id: I3c450d9d16a998918bc3963ad624455d6e2bb361
This commit is contained in:
Peng Xu
2017-04-06 00:11:52 +00:00
committed by android-build-merger
5 changed files with 207 additions and 7 deletions

View File

@@ -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;
@@ -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});
}
}

View File

@@ -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);
}

View File

@@ -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;
@@ -928,4 +931,9 @@ public class SystemSensorManager extends SensorManager {
}
}
protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
return nativeSetOperationParameter(
mNativeInstance, parameter.type, parameter.floatValues, parameter.intValues) == 0;
}
}

View File

@@ -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 {
@@ -499,6 +518,10 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeConfigDirectChannel",
"(JIII)I",
(void*)nativeConfigDirectChannel },
{"nativeSetOperationParameter",
"(JI[F[I)I",
(void*)nativeSetOperationParameter },
};
static const JNINativeMethod gBaseEventQueueMethods[] = {

View File

@@ -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;
@@ -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) {
@@ -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
@@ -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"));
}
}