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

Commit 39b2dab1 authored by Lais Andrade's avatar Lais Andrade
Browse files

Change VibratorInfo reload logic in VibratorManagerService

The VibratorController.getInfo method is reloading the VibratorInfo on
every call when at least one of the read calls to the vibrator HAL has
failed. This can force a device to enter a request loop when the HAL has
a broken getter implementation, and also has no control on how
frequently it requests HAL data.

This update will reload the VibratorInfo only once from
VibratorManagerService.systemReady, to give the controller a chance to
fetch the HAL data before the service becomes available to the system.
After this point that info (including defaults for failed fields) will
be considered the device vibrator profile and can be cached by client
calls and apps.

This is also adding more logs on failing to load primitive durations, to
help finding the root cause for the failed cts.

Bug: 195595741
Test: VibratorControllerTest and VibratorManagerServiceTest
Change-Id: Idd9457300c49d7e240f4d9a6326fdbe995e7089b
parent 21223c9b
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ public class SystemVibrator extends Vibrator {
            mRegisteredListeners = new ArrayMap<>();

    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private AllVibratorsInfo mVibratorInfo;

    @UnsupportedAppUsage
@@ -73,7 +74,15 @@ public class SystemVibrator extends Vibrator {
            int[] vibratorIds = mVibratorManager.getVibratorIds();
            VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
            for (int i = 0; i < vibratorIds.length; i++) {
                vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo();
                Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]);
                if (vibrator instanceof NullVibrator) {
                    Log.w(TAG, "Vibrator manager service not ready; "
                            + "Info not yet available for vibrator: " + vibratorIds[i]);
                    // This should never happen after the vibrator manager service is ready.
                    // Skip caching this vibrator until then.
                    return VibratorInfo.EMPTY_VIBRATOR_INFO;
                }
                vibratorInfos[i] = vibrator.getInfo();
            }
            return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
        }
+5 −1
Original line number Diff line number Diff line
@@ -165,7 +165,11 @@ public abstract class Vibrator {
        return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue;
    }

    /** @hide */
    /**
     * Get the info describing this vibrator.
     *
     * @hide
     */
    protected VibratorInfo getInfo() {
        return VibratorInfo.EMPTY_VIBRATOR_INFO;
    }
+2 −1
Original line number Diff line number Diff line
@@ -461,7 +461,8 @@ public class VibratorInfo implements Parcelable {
        int supportedPrimitivesCount = mSupportedPrimitives.size();
        String[] names = new String[supportedPrimitivesCount];
        for (int i = 0; i < supportedPrimitivesCount; i++) {
            names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
            names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i))
                    + "(" + mSupportedPrimitives.valueAt(i) + "ms)";
        }
        return names;
    }
+56 −25
Original line number Diff line number Diff line
@@ -41,12 +41,11 @@ final class VibratorController {

    private final Object mLock = new Object();
    private final NativeWrapper mNativeWrapper;
    private final VibratorInfo.Builder mVibratorInfoBuilder;

    @GuardedBy("mLock")
    private VibratorInfo mVibratorInfo;
    @GuardedBy("mLock")
    private boolean mVibratorInfoLoaded;
    private boolean mVibratorInfoLoadSuccessful;
    @GuardedBy("mLock")
    private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
            new RemoteCallbackList<>();
@@ -73,10 +72,16 @@ final class VibratorController {
            NativeWrapper nativeWrapper) {
        mNativeWrapper = nativeWrapper;
        mNativeWrapper.init(vibratorId, listener);
        mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
        mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
                mVibratorInfoBuilder);
        mVibratorInfo = mVibratorInfoBuilder.build();
        VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
        mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
                vibratorInfoBuilder);
        mVibratorInfo = vibratorInfoBuilder.build();

        if (!mVibratorInfoLoadSuccessful) {
            Slog.e(TAG,
                    "Vibrator controller initialization failed to load some HAL info for vibrator "
                            + vibratorId);
        }
    }

    /** Register state listener for this vibrator. */
@@ -108,15 +113,33 @@ final class VibratorController {
        }
    }

    /** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
    public void reloadVibratorInfoIfNeeded() {
        synchronized (mLock) {
            if (mVibratorInfoLoadSuccessful) {
                return;
            }
            int vibratorId = mVibratorInfo.getId();
            VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
            mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
                    vibratorInfoBuilder);
            mVibratorInfo = vibratorInfoBuilder.build();
            if (!mVibratorInfoLoadSuccessful) {
                Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId);
            }
        }
    }

    /** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */
    boolean isVibratorInfoLoadSuccessful() {
        synchronized (mLock) {
            return mVibratorInfoLoadSuccessful;
        }
    }

    /** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
    public VibratorInfo getVibratorInfo() {
        synchronized (mLock) {
            if (!mVibratorInfoLoaded) {
                // Try to load the vibrator metadata that has failed in the last attempt.
                mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
                        mVibratorInfoBuilder);
                mVibratorInfo = mVibratorInfoBuilder.build();
            }
            return mVibratorInfo;
        }
    }
@@ -164,8 +187,10 @@ final class VibratorController {
     * @return true if this vibrator has this capability, false otherwise
     */
    public boolean hasCapability(long capability) {
        synchronized (mLock) {
            return mVibratorInfo.hasCapability(capability);
        }
    }

    /** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
    public boolean isAvailable() {
@@ -178,10 +203,10 @@ final class VibratorController {
     * <p>This will affect the state of {@link #isUnderExternalControl()}.
     */
    public void setExternalControl(boolean externalControl) {
        synchronized (mLock) {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
                return;
            }
        synchronized (mLock) {
            mIsUnderExternalControl = externalControl;
            mNativeWrapper.setExternalControl(externalControl);
        }
@@ -192,10 +217,10 @@ final class VibratorController {
     * if given {@code effect} is {@code null}.
     */
    public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
        synchronized (mLock) {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
                return;
            }
        synchronized (mLock) {
            if (prebaked == null) {
                mNativeWrapper.alwaysOnDisable(id);
            } else {
@@ -268,10 +293,10 @@ final class VibratorController {
     * do not support the input or a negative number if the operation failed.
     */
    public long on(PrimitiveSegment[] primitives, long vibrationId) {
        synchronized (mLock) {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
                return 0;
            }
        synchronized (mLock) {
            long duration = mNativeWrapper.compose(primitives, vibrationId);
            if (duration > 0) {
                mCurrentAmplitude = -1;
@@ -290,10 +315,10 @@ final class VibratorController {
     * @return The duration of the effect playing, or 0 if unsupported.
     */
    public long on(RampSegment[] primitives, long vibrationId) {
        synchronized (mLock) {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
                return 0;
            }
        synchronized (mLock) {
            int braking = mVibratorInfo.getDefaultBraking();
            long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
            if (duration > 0) {
@@ -327,6 +352,7 @@ final class VibratorController {
        synchronized (mLock) {
            return "VibratorController{"
                    + "mVibratorInfo=" + mVibratorInfo
                    + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
                    + ", mIsVibrating=" + mIsVibrating
                    + ", mCurrentAmplitude=" + mCurrentAmplitude
                    + ", mIsUnderExternalControl=" + mIsUnderExternalControl
@@ -393,10 +419,15 @@ final class VibratorController {
         * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}.
         */
        private static native long getNativeFinalizer();

        private static native boolean isAvailable(long nativePtr);

        private static native long on(long nativePtr, long milliseconds, long vibrationId);

        private static native void off(long nativePtr);

        private static native void setAmplitude(long nativePtr, float amplitude);

        private static native long performEffect(long nativePtr, long effect, long strength,
                long vibrationId);

+26 −2
Original line number Diff line number Diff line
@@ -128,6 +128,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    private VibrationThread mNextVibration;
    @GuardedBy("mLock")
    private ExternalVibrationHolder mCurrentExternalVibration;
    @GuardedBy("mLock")
    private boolean mServiceReady;

    private final VibrationSettings mVibrationSettings;
    private final VibrationScaler mVibrationScaler;
@@ -201,6 +203,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
        mWakeLock.setReferenceCounted(true);

        // Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only
        // once and assumed unchanged for the lifecycle of this service. Each individual vibrator
        // can still retry loading each individual vibrator hardware spec once more at systemReady.
        mCapabilities = mNativeWrapper.getCapabilities();
        int[] vibratorIds = mNativeWrapper.getVibratorIds();
        if (vibratorIds == null) {
@@ -235,6 +240,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        Slog.v(TAG, "Initializing VibratorManager service...");
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "systemReady");
        try {
            // Will retry to load each vibrator's info, if any request have failed.
            for (int i = 0; i < mVibrators.size(); i++) {
                mVibrators.valueAt(i).reloadVibratorInfoIfNeeded();
            }

            mVibrationSettings.onSystemReady();
            mInputDeviceDelegate.onSystemReady();

@@ -243,6 +253,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            // Will update settings and input devices.
            updateServiceState();
        } finally {
            synchronized (mLock) {
                mServiceReady = true;
            }
            Slog.v(TAG, "VibratorManager service initialized");
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
@@ -256,8 +269,19 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    @Override // Binder call
    @Nullable
    public VibratorInfo getVibratorInfo(int vibratorId) {
        VibratorController controller = mVibrators.get(vibratorId);
        return controller == null ? null : controller.getVibratorInfo();
        final VibratorController controller = mVibrators.get(vibratorId);
        if (controller == null) {
            return null;
        }
        final VibratorInfo info = controller.getVibratorInfo();
        synchronized (mLock) {
            if (mServiceReady) {
                return info;
            }
        }
        // If the service is not ready and the load was unsuccessful then return null while waiting
        // for the service to be ready. It will retry to load the complete info from the HAL.
        return controller.isVibratorInfoLoadSuccessful() ? info : null;
    }

    @Override // Binder call
Loading