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

Commit 18ebf732 authored by Aravind Akella's avatar Aravind Akella
Browse files

Add hidden SensorManager APIs for injecting sensor data.

Change-Id: If49467ced1a719c49bb5ac7ab670e86ff4642eaa
parent fb6f8b4f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -13236,6 +13236,7 @@ package android.hardware {
    method public int getType();
    method public java.lang.String getVendor();
    method public int getVersion();
    method public boolean isDataInjectionSupported();
    method public boolean isWakeUpSensor();
    field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
    field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
@@ -13311,6 +13312,7 @@ package android.hardware {
  public abstract class SensorManager {
    method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
    method public boolean enableDataInjectionMode(boolean);
    method public boolean flush(android.hardware.SensorEventListener);
    method public static float getAltitude(float, float);
    method public static void getAngleChange(float[], float[], float[]);
@@ -13323,6 +13325,7 @@ 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 boolean injectSensorData(android.hardware.Sensor, float[], int, long);
    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);
+18 −0
Original line number Diff line number Diff line
@@ -580,6 +580,10 @@ public final class Sensor {
    private static final int REPORTING_MODE_MASK = 0xE;
    private static final int REPORTING_MODE_SHIFT = 1;

    // MASK for LSB fifth bit. Used to know whether the sensor supports data injection or not.
    private static final int DATA_INJECTION_MASK = 0x10;
    private static final int DATA_INJECTION_SHIFT = 4;

    // TODO(): The following arrays are fragile and error-prone. This needs to be refactored.

    // Note: This needs to be updated, whenever a new sensor is added.
@@ -822,6 +826,20 @@ public final class Sensor {
        return (mFlags & SENSOR_FLAG_WAKE_UP_SENSOR) != 0;
    }

    /**
     * Returns true if the sensor supports data injection when the
     * HAL is set to data injection mode.
     *
     * @return <code>true</code> if the sensor supports data
     *         injection when the HAL is set in injection mode,
     *         false otherwise.
     * @hide
     */
    @SystemApi
    public boolean isDataInjectionSupported() {
        return (((mFlags & DATA_INJECTION_MASK) >> DATA_INJECTION_SHIFT)) != 0;
    }

    void setRange(float max, float res) {
        mMaxRange = max;
        mResolution = res;
+100 −0
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@

package android.hardware;

import android.annotation.SystemApi;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;

@@ -1555,6 +1558,103 @@ public abstract class SensorManager {
            Sensor sensor, boolean disable);


    /**
     * For testing purposes only. Not for third party applications.
     *
     * Enable data injection mode in sensor service. This mode is
     * expected to be used only for testing purposes. If the HAL is
     * set to data injection mode, it will ignore the input from
     * physical sensors and read sensor data that is injected from
     * the test application. This mode is used for testing vendor
     * implementations for various algorithms like Rotation Vector,
     * Significant Motion, Step Counter etc.
     *
     * The tests which call this API need to have {@code
     * android.permission.HARDWARE_TEST} permission which isn't
     * available for third party applications.
     *
     * @param enable True to set the HAL in DATA_INJECTION mode.
     *               False to reset the HAL back to NORMAL mode.
     *
     * @return true if the HAL supports data injection and false
     *         otherwise.
     * @hide
     */
    @SystemApi
    public boolean enableDataInjectionMode(boolean enable) {
          return enableDataInjectionImpl(enable);
    }

    /**
     * @hide
     */
    protected abstract boolean enableDataInjectionImpl(boolean enable);

    /**
     * For testing purposes only. Not for third party applications.
     *
     * This method is used to inject raw sensor data into the HAL.
     * Call enableDataInjection before this method to set the HAL in
     * data injection mode. This method should be called only if a
     * previous call to enableDataInjection has been successful and
     * the HAL is already in data injection mode.
     *
     * The tests which call this API need to have {@code
     * android.permission.HARDWARE_TEST} permission which isn't
     * available for third party applications.
     *
     * @param sensor The sensor to inject.
     * @param values Sensor values to inject. The length of this
     *               array must be exactly equal to the number of
     *               values reported by the sensor type.
     * @param accuracy Accuracy of the sensor.
     * @param timestamp Sensor timestamp associated with the event.
     *
     * @return boolean True if the data injection succeeds, false
     *         otherwise.
     * @throws IllegalArgumentException when the sensor is null,
     *         data injection is not supported by the sensor, values
     *         are null, incorrect number of values for the sensor,
     *         sensor accuracy is incorrect or timestamps are
     *         invalid.
     * @hide
     */
    @SystemApi
    public boolean injectSensorData(Sensor sensor, float[] values, int accuracy,
                long timestamp) {
        if (sensor == null) {
            throw new IllegalArgumentException("sensor cannot be null");
        }
        if (!sensor.isDataInjectionSupported()) {
            throw new IllegalArgumentException("sensor does not support data injection");
        }
        if (values == null) {
            throw new IllegalArgumentException("sensor data cannot be null");
        }
        int expectedNumValues = Sensor.getMaxLengthValuesArray(sensor, Build.VERSION_CODES.MNC);
        if (values.length != expectedNumValues) {
            throw new  IllegalArgumentException ("Wrong number of values for sensor " +
                    sensor.getName() + " actual=" + values.length + " expected=" +
                                                  expectedNumValues);
        }
        if (accuracy < SENSOR_STATUS_NO_CONTACT || accuracy > SENSOR_STATUS_ACCURACY_HIGH) {
            throw new IllegalArgumentException("Invalid sensor accuracy");
        }
        if (timestamp <= 0) {
            throw new IllegalArgumentException("Negative or zero sensor timestamp");
        }
        if (timestamp > SystemClock.elapsedRealtimeNanos()) {
            throw new IllegalArgumentException("Sensor timestamp into the future");
        }
        return injectSensorDataImpl(sensor, values, accuracy, timestamp);
    }

    /**
     * @hide
     */
    protected abstract boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy,
                long timestamp);

    private LegacySensorManager getLegacySensorManager() {
        synchronized (mSensorListByType) {
            if (mLegacySensorManager == null) {
+105 −7
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package android.hardware;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
@@ -40,11 +42,14 @@ import java.util.List;
public class SystemSensorManager extends SensorManager {
    private static native void nativeClassInit();
    private static native int nativeGetNextSensor(Sensor sensor, int next);
    private static native int nativeEnableDataInjection(boolean enable);

    private static boolean sSensorModuleInitialized = false;
    private static final Object sSensorModuleLock = new Object();
    private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
    private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
    private static InjectEventQueue mInjectEventQueue = null;
    private static boolean mDataInjectionMode = false;

    // Listener list
    private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
@@ -56,6 +61,7 @@ public class SystemSensorManager extends SensorManager {
    private final Looper mMainLooper;
    private final int mTargetSdkLevel;
    private final String mPackageName;
    private final boolean mHasDataInjectionPermissions;

    /** {@hide} */
    public SystemSensorManager(Context context, Looper mainLooper) {
@@ -82,6 +88,8 @@ public class SystemSensorManager extends SensorManager {
                    }
                } while (i>0);
            }
            mHasDataInjectionPermissions = context.checkSelfPermission(
                    Manifest.permission.HARDWARE_TEST) == PackageManager.PERMISSION_GRANTED;
        }
    }

@@ -219,19 +227,72 @@ public class SystemSensorManager extends SensorManager {
        }
    }

    protected boolean enableDataInjectionImpl(boolean enable) {
        if (!mHasDataInjectionPermissions) {
            throw new SecurityException("Permission denial. Calling enableDataInjection without "
                    + Manifest.permission.HARDWARE_TEST);
        }
        synchronized (sSensorModuleLock) {
            int ret = nativeEnableDataInjection(enable);
            // The HAL does not support injection. Ignore.
            if (ret != 0) {
                Log.e(TAG, "HAL does not support data injection");
                return false;
            }
            mDataInjectionMode = enable;
            // If data injection is being disabled clean up the native resources.
            if (!enable && mInjectEventQueue != null) {
                mInjectEventQueue.dispose();
                mInjectEventQueue = null;
            }
            return true;
        }
    }

    protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy,
            long timestamp) {
        if (!mHasDataInjectionPermissions) {
            throw new SecurityException("Permission denial. Calling injectSensorData without "
                    + Manifest.permission.HARDWARE_TEST);
        }
        synchronized (sSensorModuleLock) {
            if (!mDataInjectionMode) {
                Log.e(TAG, "Data injection mode not activated before calling injectSensorData");
                return false;
            }
            if (mInjectEventQueue == null) {
                mInjectEventQueue = new InjectEventQueue(mMainLooper, this);
            }
            int ret = mInjectEventQueue.injectSensorData(sensor.getHandle(), values, accuracy,
                                                         timestamp);
            // If there are any errors in data injection clean up the native resources.
            if (ret != 0) {
                mInjectEventQueue.dispose();
                mInjectEventQueue = null;
                mDataInjectionMode = false;
            }
            return ret == 0;
        }
    }

    /*
     * BaseEventQueue is the communication channel with the sensor service,
     * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
     * the queues and the listeners.
     * the queues and the listeners. InjectEventQueue is also a sub-class which is a special case
     * where data is being injected into the sensor HAL through the sensor service. It is not
     * associated with any listener and there is one InjectEventQueue associated with a
     * SensorManager instance.
     */
    private static abstract class BaseEventQueue {
        private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak,
                MessageQueue msgQ, float[] scratch, String packageName);
               MessageQueue msgQ, float[] scratch, String packageName, int mode);
        private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
                int maxBatchReportLatencyUs);
        private static native int nativeDisableSensor(long eventQ, int handle);
        private static native void nativeDestroySensorEventQueue(long eventQ);
        private static native int nativeFlushSensor(long eventQ);
        private static native int nativeInjectSensorData(long eventQ, int handle,
                                                    float[] values,int accuracy, long timestamp);
        private long nSensorEventQueue;
        private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
        protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -240,10 +301,12 @@ public class SystemSensorManager extends SensorManager {
        private final float[] mScratch = new float[16];
        protected final SystemSensorManager mManager;

        BaseEventQueue(Looper looper, SystemSensorManager manager) {
        protected static final int OPERATING_MODE_NORMAL = 0;
        protected static final int OPERATING_MODE_DATA_INJECTION = 1;

        BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) {
            nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this),
                    looper.getQueue(), mScratch,
                    manager.mPackageName);
                    looper.getQueue(), mScratch, manager.mPackageName, mode);
            mCloseGuard.open("dispose");
            mManager = manager;
        }
@@ -340,6 +403,11 @@ public class SystemSensorManager extends SensorManager {
                    maxBatchReportLatencyUs);
        }

        protected int injectSensorDataBase(int handle, float[] values, int accuracy,
                                           long timestamp) {
            return nativeInjectSensorData(nSensorEventQueue, handle, values, accuracy, timestamp);
        }

        private int disableSensor(Sensor sensor) {
            if (nSensorEventQueue == 0) throw new NullPointerException();
            if (sensor == null) throw new NullPointerException();
@@ -359,7 +427,7 @@ public class SystemSensorManager extends SensorManager {

        public SensorEventQueue(SensorEventListener listener, Looper looper,
                SystemSensorManager manager) {
            super(looper, manager);
            super(looper, manager, OPERATING_MODE_NORMAL);
            mListener = listener;
        }

@@ -426,7 +494,7 @@ public class SystemSensorManager extends SensorManager {

        public TriggerEventQueue(TriggerEventListener listener, Looper looper,
                SystemSensorManager manager) {
            super(looper, manager);
            super(looper, manager, OPERATING_MODE_NORMAL);
            mListener = listener;
        }

@@ -477,4 +545,34 @@ public class SystemSensorManager extends SensorManager {
        protected void dispatchFlushCompleteEvent(int handle) {
        }
    }

    static final class InjectEventQueue extends BaseEventQueue {
        public InjectEventQueue(Looper looper, SystemSensorManager manager) {
            super(looper, manager, OPERATING_MODE_DATA_INJECTION);
        }

        int injectSensorData(int handle, float[] values,int accuracy, long timestamp) {
             return injectSensorDataBase(handle, values, accuracy, timestamp);
        }

        @SuppressWarnings("unused")
        protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
                long timestamp) {
        }

        @SuppressWarnings("unused")
        protected void dispatchFlushCompleteEvent(int handle) {

        }

        @SuppressWarnings("unused")
        protected void addSensorEvent(Sensor sensor) {

        }

        @SuppressWarnings("unused")
        protected void removeSensorEvent(Sensor sensor) {

        }
    }
}
+28 −5
Original line number Diff line number Diff line
@@ -174,6 +174,11 @@ nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next)
    return size_t(next) < count ? next : 0;
}

static int nativeEnableDataInjection(JNIEnv *_env, jclass _this, jboolean enable) {
     SensorManager& mgr(SensorManager::getInstance());
     return mgr.enableDataInjection(enable);
}

//----------------------------------------------------------------------------

class Receiver : public LooperCallback {
@@ -277,11 +282,11 @@ private:
};

static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQWeak, jobject msgQ,
        jfloatArray scratch, jstring packageName) {
        jfloatArray scratch, jstring packageName, jint mode) {
    SensorManager& mgr(SensorManager::getInstance());
    ScopedUtfChars packageUtf(env, packageName);
    String8 clientName(packageUtf.c_str());
    sp<SensorEventQueue> queue(mgr.createEventQueue(clientName));
    sp<SensorEventQueue> queue(mgr.createEventQueue(clientName, mode));

    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ);
    if (messageQueue == NULL) {
@@ -297,7 +302,6 @@ static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject event
static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, jint rate_us,
                               jint maxBatchReportLatency) {
    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));

    return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency,
                                                         0);
}
@@ -307,7 +311,7 @@ static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint ha
    return receiver->getSensorEventQueue()->disableSensor(handle);
}

static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ, jint handle) {
static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ) {
    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
    receiver->destroy();
    receiver->decStrong((void*)nativeInitSensorEventQueue);
@@ -318,6 +322,17 @@ static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jlong eventQ) {
    return receiver->getSensorEventQueue()->flush();
}

static jint nativeInjectSensorData(JNIEnv *env, jclass clazz, jlong eventQ, jint handle,
        jfloatArray values, jint accuracy, jlong timestamp) {
    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
    // Create a sensor_event from the above data which can be injected into the HAL.
    ASensorEvent sensor_event;
    memset(&sensor_event, 0, sizeof(sensor_event));
    sensor_event.sensor = handle;
    sensor_event.timestamp = timestamp;
    env->GetFloatArrayRegion(values, 0, env->GetArrayLength(values), sensor_event.data);
    return receiver->getSensorEventQueue()->injectSensorEvent(sensor_event);
}
//----------------------------------------------------------------------------

static JNINativeMethod gSystemSensorManagerMethods[] = {
@@ -328,11 +343,15 @@ static JNINativeMethod gSystemSensorManagerMethods[] = {
    {"nativeGetNextSensor",
            "(Landroid/hardware/Sensor;I)I",
            (void*)nativeGetNextSensor },

    {"nativeEnableDataInjection",
            "(Z)I",
            (void*)nativeEnableDataInjection },
};

static JNINativeMethod gBaseEventQueueMethods[] = {
    {"nativeInitBaseEventQueue",
     "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;)J",
     "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;I)J",
     (void*)nativeInitSensorEventQueue },

    {"nativeEnableSensor",
@@ -350,6 +369,10 @@ static JNINativeMethod gBaseEventQueueMethods[] = {
    {"nativeFlushSensor",
            "(J)I",
            (void*)nativeFlushSensor },

    {"nativeInjectSensorData",
            "(JI[FIJ)I",
            (void*)nativeInjectSensorData },
};

}; // namespace android