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

Commit c88efaab authored by Mark Wheatley's avatar Mark Wheatley
Browse files

Add support for multiple data injection modes in Sensor Manager

In addition to the existing data injection mode, add support for the following
new data injection modes in Sensor Manager:
- Replay Data Injection
- HAL Bypass Replay Data Injection

Bug: 287257057
Bug: 303660857
Test: manual

Change-Id: I04dd97557e9432f3dc600c0c60ebfbd5bc980cc1
(cherry picked from commit 9e42b5a7f2be6da526de6ab3c3eb459f938a3468)
parent f2a089d3
Loading
Loading
Loading
Loading
+74 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.hardware;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -27,6 +28,8 @@ import android.os.MemoryFile;
import android.util.Log;
import android.util.SparseArray;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -1809,6 +1812,41 @@ public abstract class SensorManager {
    protected abstract boolean cancelTriggerSensorImpl(TriggerEventListener listener,
            Sensor sensor, boolean disable);

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({DATA_INJECTION, REPLAY_DATA_INJECTION, HAL_BYPASS_REPLAY_DATA_INJECTION})
    public @interface DataInjectionMode {}
    /**
     * This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
     * the HAL ignores the sensor data provided by physical sensors and accepts the data that is
     * injected from the SensorService as if it were the real sensor data. This mode is primarily
     * used for testing various algorithms like vendor provided SensorFusion, Step Counter and
     * Step Detector etc. Typically, in this mode, there is a client app which injects
     * sensor data into the HAL. Normal apps can register and unregister for any sensor
     * that supports injection. Registering to sensors that do not support injection will
     * give an error.
     * This is the default data injection mode.
     * @hide
     */
    public static final int DATA_INJECTION = 1;
    /**
     * Mostly equivalent to DATA_INJECTION with the difference being that the injected data is
     * delivered to all requesting apps rather than just the package allowed to inject data.
     * This mode is only allowed to be used on development builds.
     * @hide
     */
    public static final int REPLAY_DATA_INJECTION = 3;
    /**
     * Like REPLAY_DATA_INJECTION but injected data is not sent into the HAL. It is stored in a
     * buffer in the platform and played back to all requesting apps.
     * This is useful for playing back sensor data to test platform components without
     * relying on the HAL to support data injection.
     * @hide
     */
    public static final int HAL_BYPASS_REPLAY_DATA_INJECTION = 4;


    /**
     * For testing purposes only. Not for third party applications.
@@ -1833,13 +1871,47 @@ public abstract class SensorManager {
     */
    @SystemApi
    public boolean initDataInjection(boolean enable) {
        return initDataInjectionImpl(enable);
        return initDataInjectionImpl(enable, DATA_INJECTION);
    }

    /**
     * For testing purposes only. Not for third party applications.
     *
     * Initialize data injection mode and create a client for data injection. SensorService should
     * already be operating in one of DATA_INJECTION, REPLAY_DATA_INJECTION or
     * HAL_BYPASS_REPLAY_DATA_INJECTION modes for this call succeed. To set SensorService in
     * a Data Injection mode, use one of:
     *
     * <ul>
     *      <li>adb shell dumpsys sensorservice data_injection</li>
     *      <li>adb shell dumpsys sensorservice replay_data_injection package_name</li>
     *      <li>adb shell dumpsys sensorservice hal_bypass_replay_data_injection package_name</li>
     * </ul>
     *
     * Typically this is done using a host side test.  This mode is expected to be used
     * only for testing purposes. See {@link DataInjectionMode} for details of each data injection
     * mode. Once this method succeeds, the test can call
     * {@link #injectSensorData(Sensor, float[], int, long)} to inject sensor data into the HAL.
     * To put SensorService back into normal mode, use "adb shell dumpsys sensorservice enable"
     *
     * @param enable True to initialize a client in a data injection mode.
     *               False to clean up the native resources.
     *
     * @param mode One of DATA_INJECTION, REPLAY_DATA_INJECTION or HAL_BYPASS_DATA_INJECTION.
     *             See {@link DataInjectionMode} for details.
     *
     * @return true if the HAL supports data injection and false
     *         otherwise.
     * @hide
     */
    protected abstract boolean initDataInjectionImpl(boolean enable);
    public boolean initDataInjection(boolean enable, @DataInjectionMode int mode) {
        return initDataInjectionImpl(enable, mode);
    }

    /**
     * @hide
     */
    protected abstract boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode);

    /**
     * For testing purposes only. Not for third party applications.
@@ -1871,9 +1943,6 @@ public abstract class SensorManager {
        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");
        }
+45 −6
Original line number Diff line number Diff line
@@ -90,6 +90,8 @@ public class SystemSensorManager extends SensorManager {
    private static native void nativeGetRuntimeSensors(
            long nativeInstance, int deviceId, List<Sensor> list);
    private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
    private static native boolean nativeIsReplayDataInjectionEnabled(long nativeInstance);
    private static native boolean nativeIsHalBypassReplayDataInjectionEnabled(long nativeInstance);

    private static native int nativeCreateDirectChannel(
            long nativeInstance, int deviceId, long size, int channelType, int fd,
@@ -384,20 +386,41 @@ public class SystemSensorManager extends SensorManager {
        }
    }

    protected boolean initDataInjectionImpl(boolean enable) {
    protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
        synchronized (sLock) {
            boolean isDataInjectionModeEnabled = false;
            if (enable) {
                boolean isDataInjectionModeEnabled = nativeIsDataInjectionEnabled(mNativeInstance);
                switch (mode) {
                    case DATA_INJECTION:
                        isDataInjectionModeEnabled = nativeIsDataInjectionEnabled(mNativeInstance);
                        break;
                    case REPLAY_DATA_INJECTION:
                        isDataInjectionModeEnabled = nativeIsReplayDataInjectionEnabled(
                                mNativeInstance);
                        break;
                    case HAL_BYPASS_REPLAY_DATA_INJECTION:
                        isDataInjectionModeEnabled = nativeIsHalBypassReplayDataInjectionEnabled(
                                mNativeInstance);
                        break;
                    default:
                        break;
                }
                // The HAL does not support injection OR SensorService hasn't been set in DI mode.
                if (!isDataInjectionModeEnabled) {
                    Log.e(TAG, "Data Injection mode not enabled");
                    Log.e(TAG, "The correct Data Injection mode has not been enabled");
                    return false;
                }
                if (sInjectEventQueue != null && sInjectEventQueue.getDataInjectionMode() != mode) {
                    // The inject event queue has been initialized for a different type of DI
                    // close it and create a new one
                    sInjectEventQueue.dispose();
                    sInjectEventQueue = null;
                }
                // Initialize a client for data_injection.
                if (sInjectEventQueue == null) {
                    try {
                        sInjectEventQueue = new InjectEventQueue(
                                mMainLooper, this, mContext.getPackageName());
                                mMainLooper, this, mode, mContext.getPackageName());
                    } catch (RuntimeException e) {
                        Log.e(TAG, "Cannot create InjectEventQueue: " + e);
                    }
@@ -421,6 +444,12 @@ public class SystemSensorManager extends SensorManager {
                Log.e(TAG, "Data injection mode not activated before calling injectSensorData");
                return false;
            }
            if (sInjectEventQueue.getDataInjectionMode() != HAL_BYPASS_REPLAY_DATA_INJECTION
                    && !sensor.isDataInjectionSupported()) {
                // DI mode and Replay DI mode require support from the sensor HAL
                // HAL Bypass mode doesn't require this.
                throw new IllegalArgumentException("sensor does not support data injection");
            }
            int ret = sInjectEventQueue.injectSensorData(sensor.getHandle(), values, accuracy,
                                                         timestamp);
            // If there are any errors in data injection clean up the native resources.
@@ -825,6 +854,8 @@ public class SystemSensorManager extends SensorManager {

        protected static final int OPERATING_MODE_NORMAL = 0;
        protected static final int OPERATING_MODE_DATA_INJECTION = 1;
        protected static final int OPERATING_MODE_REPLAY_DATA_INJECTION = 3;
        protected static final int OPERATING_MODE_HAL_BYPASS_REPLAY_DATA_INJECTION = 4;

        BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
            if (packageName == null) packageName = "";
@@ -1134,8 +1165,12 @@ public class SystemSensorManager extends SensorManager {
    }

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

        private int mMode;
        public InjectEventQueue(Looper looper, SystemSensorManager manager,
                @DataInjectionMode int mode, String packageName) {
            super(looper, manager, mode, packageName);
            mMode = mode;
        }

        int injectSensorData(int handle, float[] values, int accuracy, long timestamp) {
@@ -1161,6 +1196,10 @@ public class SystemSensorManager extends SensorManager {
        protected void removeSensorEvent(Sensor sensor) {

        }

        int getDataInjectionMode() {
            return mMode;
        }
    }

    protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
+1 −1
Original line number Diff line number Diff line
@@ -644,7 +644,7 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene
        }

        @Override
        protected boolean initDataInjectionImpl(boolean enable) {
        protected boolean initDataInjectionImpl(boolean enable, int mode) {
            return false;
        }

+17 −0
Original line number Diff line number Diff line
@@ -265,6 +265,18 @@ static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong s
    return mgr->isDataInjectionEnabled();
}

static jboolean nativeIsReplayDataInjectionEnabled(JNIEnv *_env, jclass _this,
                                                   jlong sensorManager) {
    SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
    return mgr->isReplayDataInjectionEnabled();
}

static jboolean nativeIsHalBypassReplayDataInjectionEnabled(JNIEnv *_env, jclass _this,
                                                            jlong sensorManager) {
    SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
    return mgr->isHalBypassReplayDataInjectionEnabled();
}

static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
                                      jint deviceId, jlong size, jint channelType, jint fd,
                                      jobject hardwareBufferObj) {
@@ -533,6 +545,11 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = {

        {"nativeIsDataInjectionEnabled", "(J)Z", (void *)nativeIsDataInjectionEnabled},

        {"nativeIsReplayDataInjectionEnabled", "(J)Z", (void *)nativeIsReplayDataInjectionEnabled},

        {"nativeIsHalBypassReplayDataInjectionEnabled", "(J)Z",
         (void *)nativeIsHalBypassReplayDataInjectionEnabled},

        {"nativeCreateDirectChannel", "(JIJIILandroid/hardware/HardwareBuffer;)I",
         (void *)nativeCreateDirectChannel},

+1 −1
Original line number Diff line number Diff line
@@ -191,7 +191,7 @@ public class AsyncSensorManager extends SensorManager
    }

    @Override
    protected boolean initDataInjectionImpl(boolean enable) {
    protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
        throw new UnsupportedOperationException("not implemented");
    }

Loading