Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1cfde25e authored by Peng Xu's avatar Peng Xu
Browse files

Local geomagnetic field update to sensor

Local geomagnetic field strength, declination, inclination information
is useful for magnetometer calibration. It also benefits rotation
vector sensor implementation as it gives a baseline that aids detecting
magnetic field disturbance.

Bug: 30958130
Test: Tested with marlin. Modified hal implementation can get local
      geomagnetic field.

Change-Id: I373fe74d5a091a3adb80ff3c61e441edcf5a253b
parent 7e5d2c23
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -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});
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -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);
}
+8 −0
Original line number Diff line number Diff line
@@ -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;
    }
}
+23 −0
Original line number Diff line number Diff line
@@ -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[] = {
+96 −7
Original line number Diff line number Diff line
@@ -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"));
    }
}