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

Commit c4f27f30 authored by Lais Andrade's avatar Lais Andrade
Browse files

Use vibrator manager controller in VibratorManagerService

Add native methods to load manager capabilities and coordinate synced
vibrations in VibrationThread.

Bug: 167946816
Bug: 167946760
Test: VibratorManagerTest, VibrationThreadTest
Change-Id: I54dad9420be1e8cddae9023f8438581d3f553412
parent 02151de4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ java_library_static {
        "android.hardware.gnss-V1-java",
        "android.hardware.power-V1-java",
        "android.hardware.power-V1.0-java",
        "android.hardware.vibrator-V1-java",
        "android.hardware.vibrator-V2-java",
        "android.net.ipsec.ike.stubs.module_lib",
        "app-compat-annotations",
        "framework-tethering.stubs.module_lib",
+87 −14
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    private final AppOpsManager mAppOps;
    private final NativeWrapper mNativeWrapper;
    private final VibratorManagerRecords mVibratorManagerRecords;
    private final long mCapabilities;
    private final int[] mVibratorIds;
    private final SparseArray<VibratorController> mVibrators;
    private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
@@ -127,18 +128,28 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    private VibrationScaler mVibrationScaler;
    private InputDeviceDelegate mInputDeviceDelegate;

    static native long nativeInit();
    static native long nativeInit(OnSyncedVibrationCompleteListener listener);

    static native long nativeGetFinalizer();

    static native long nativeGetCapabilities(long nativeServicePtr);

    static native int[] nativeGetVibratorIds(long nativeServicePtr);

    static native boolean nativePrepareSynced(long nativeServicePtr, int[] vibratorIds);

    static native boolean nativeTriggerSynced(long nativeServicePtr, long vibrationId);

    static native void nativeCancelSynced(long nativeServicePtr);

    @VisibleForTesting
    VibratorManagerService(Context context, Injector injector) {
        mContext = context;
        mHandler = injector.createHandler(Looper.myLooper());

        VibrationCompleteListener listener = new VibrationCompleteListener(this);
        mNativeWrapper = injector.getNativeWrapper();
        mNativeWrapper.init();
        mNativeWrapper.init(listener);

        int dumpLimit = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
@@ -153,6 +164,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
        mWakeLock.setReferenceCounted(true);

        mCapabilities = mNativeWrapper.getCapabilities();
        int[] vibratorIds = mNativeWrapper.getVibratorIds();
        if (vibratorIds == null) {
            mVibratorIds = new int[0];
@@ -161,11 +173,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            // Keep original vibrator id order, which might be meaningful.
            mVibratorIds = vibratorIds;
            mVibrators = new SparseArray<>(mVibratorIds.length);
            VibrationCompleteListener listener = new VibrationCompleteListener(this);
            for (int vibratorId : vibratorIds) {
                mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener));
            }
        }

        // Reset the hardware to a default state, in case this is a runtime restart instead of a
        // fresh boot.
        mNativeWrapper.cancelSynced();
        for (int i = 0; i < mVibrators.size(); i++) {
            mVibrators.valueAt(i).off();
        }
    }

    /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
@@ -502,6 +520,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        }
    }

    private void onSyncedVibrationComplete(long vibrationId) {
        synchronized (mLock) {
            if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
                if (DEBUG) {
                    Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
                }
                mCurrentVibration.syncedVibrationComplete();
            }
        }
    }

    private void onVibrationComplete(int vibratorId, long vibrationId) {
        synchronized (mLock) {
            if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
@@ -839,13 +868,22 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {

        @Override
        public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
            // TODO(b/167946816): call IVibratorManager to prepare
        public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
            if ((mCapabilities & requiredCapabilities) != requiredCapabilities) {
                // This sync step requires capabilities this device doesn't have, skipping sync...
                return false;
            }
            return mNativeWrapper.prepareSynced(vibratorIds);
        }

        @Override
        public void triggerSyncedVibration(long vibrationId) {
            // TODO(b/167946816): call IVibratorManager to trigger
        public boolean triggerSyncedVibration(long vibrationId) {
            return mNativeWrapper.triggerSynced(vibrationId);
        }

        @Override
        public void cancelSyncedVibration() {
            mNativeWrapper.cancelSynced();
        }

        @Override
@@ -868,18 +906,33 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        }
    }

    /** Listener for synced vibration completion callbacks from native. */
    @VisibleForTesting
    public interface OnSyncedVibrationCompleteListener {

        /** Callback triggered when synced vibration is complete. */
        void onComplete(long vibrationId);
    }

    /**
     * Implementation of {@link VibratorController.OnVibrationCompleteListener} with a weak
     * reference to this service.
     * Implementation of listeners to native vibrators with a weak reference to this service.
     */
    private static final class VibrationCompleteListener implements
            VibratorController.OnVibrationCompleteListener {
            VibratorController.OnVibrationCompleteListener, OnSyncedVibrationCompleteListener {
        private WeakReference<VibratorManagerService> mServiceRef;

        VibrationCompleteListener(VibratorManagerService service) {
            mServiceRef = new WeakReference<>(service);
        }

        @Override
        public void onComplete(long vibrationId) {
            VibratorManagerService service = mServiceRef.get();
            if (service != null) {
                service.onSyncedVibrationComplete(vibrationId);
            }
        }

        @Override
        public void onComplete(int vibratorId, long vibrationId) {
            VibratorManagerService service = mServiceRef.get();
@@ -952,9 +1005,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        private long mNativeServicePtr = 0;

        /** Returns native pointer to newly created controller and connects with HAL service. */
        public void init() {
            mNativeServicePtr = VibratorManagerService.nativeInit();
            long finalizerPtr = VibratorManagerService.nativeGetFinalizer();
        public void init(OnSyncedVibrationCompleteListener listener) {
            mNativeServicePtr = nativeInit(listener);
            long finalizerPtr = nativeGetFinalizer();

            if (finalizerPtr != 0) {
                NativeAllocationRegistry registry =
@@ -964,9 +1017,29 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            }
        }

        /** Returns manager capabilities. */
        public long getCapabilities() {
            return nativeGetCapabilities(mNativeServicePtr);
        }

        /** Returns vibrator ids. */
        public int[] getVibratorIds() {
            return VibratorManagerService.nativeGetVibratorIds(mNativeServicePtr);
            return nativeGetVibratorIds(mNativeServicePtr);
        }

        /** Prepare vibrators for triggering vibrations in sync. */
        public boolean prepareSynced(@NonNull int[] vibratorIds) {
            return nativePrepareSynced(mNativeServicePtr, vibratorIds);
        }

        /** Trigger prepared synced vibration. */
        public boolean triggerSynced(long vibrationId) {
            return nativeTriggerSynced(mNativeServicePtr, vibrationId);
        }

        /** Cancel prepared synced vibration. */
        public void cancelSynced() {
            nativeCancelSynced(mNativeServicePtr);
        }
    }

+8 −2
Original line number Diff line number Diff line
@@ -119,11 +119,17 @@ public class VibratorService extends IVibratorService.Stub {
    private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {

        @Override
        public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
        public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
            return false;
        }

        @Override
        public boolean triggerSyncedVibration(long vibrationId) {
            return false;
        }

        @Override
        public void triggerSyncedVibration(long vibrationId) {
        public void cancelSyncedVibration() {
        }

        @Override
+116 −19
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.vibrator;

import android.annotation.Nullable;
import android.hardware.vibrator.IVibratorManager;
import android.os.CombinedVibrationEffect;
import android.os.IBinder;
import android.os.PowerManager;
@@ -65,10 +66,13 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
         *                             IVibratorManager.CAP_MIXED_TRIGGER_*.
         * @param vibratorIds          The id of the vibrators to be prepared.
         */
        void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds);
        boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);

        /** Callback triggered after synchronized vibrations were prepared. */
        void triggerSyncedVibration(long vibrationId);
        boolean triggerSyncedVibration(long vibrationId);

        /** Callback triggered to cancel a prepared synced vibration. */
        void cancelSyncedVibration();

        /** Callback triggered when vibration thread is complete. */
        void onVibrationEnded(long vibrationId, Vibration.Status status);
@@ -146,6 +150,20 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
        }
    }

    /** Notify current vibration that a synced step has completed. */
    public void syncedVibrationComplete() {
        synchronized (mLock) {
            if (DEBUG) {
                Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
            }
            if (mCurrentVibrateStep != null) {
                for (int i = 0; i < mVibrators.size(); i++) {
                    mCurrentVibrateStep.vibratorComplete(mVibrators.keyAt(i));
                }
            }
        }
    }

    /** Notify current vibration that a step has completed on given vibrator. */
    public void vibratorComplete(int vibratorId) {
        synchronized (mLock) {
@@ -530,7 +548,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
    /** Represent a synchronized vibration step on multiple vibrators. */
    private final class SyncedVibrateStep implements VibrateStep {
        private final SparseArray<VibrationEffect> mEffects;
        private final int mRequiredCapabilities;
        private final long mRequiredCapabilities;
        private final int[] mVibratorIds;

        @GuardedBy("mLock")
@@ -539,8 +557,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
        SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
            mEffects = effects;
            mActiveVibratorCounter = mEffects.size();
            // TODO(b/159207608): Calculate required capabilities for syncing this step.
            mRequiredCapabilities = 0;
            mRequiredCapabilities = calculateRequiredSyncCapabilities(effects);
            mVibratorIds = new int[effects.size()];
            for (int i = 0; i < effects.size(); i++) {
                mVibratorIds[i] = effects.keyAt(i);
@@ -574,18 +591,21 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
        @Override
        public Vibration.Status play() {
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep");
            long timeout = -1;
            long duration = -1;
            try {
                if (DEBUG) {
                    Slog.d(TAG, "SyncedVibrateStep starting...");
                }
                final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size());
                long startTime = SystemClock.uptimeMillis();
                mCallbacks.prepareSyncedVibration(mRequiredCapabilities, mVibratorIds);
                timeout = startVibrating(startTime, nextSteps);
                mCallbacks.triggerSyncedVibration(mVibration.id);
                noteVibratorOn(timeout);
                duration = startVibratingSynced(startTime, nextSteps);

                if (duration <= 0) {
                    // Vibrate step failed, vibrator could not be turned on for this step.
                    return Vibration.Status.IGNORED;
                }

                noteVibratorOn(duration);
                while (!nextSteps.isEmpty()) {
                    AmplitudeStep step = nextSteps.poll();
                    if (!waitUntil(step.startTime)) {
@@ -607,7 +627,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
                synchronized (mLock) {
                    // All OneShot and Waveform effects have finished. Just wait for the other
                    // effects to end via native callbacks before finishing this synced step.
                    final long wakeUpTime = startTime + timeout + CALLBACKS_EXTRA_TIMEOUT;
                    final long wakeUpTime = startTime + duration + CALLBACKS_EXTRA_TIMEOUT;
                    if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
                        return Vibration.Status.FINISHED;
                    }
@@ -617,7 +637,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
                    return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
                }
            } finally {
                if (timeout > 0) {
                if (duration > 0) {
                    noteVibratorOff();
                }
                if (DEBUG) {
@@ -627,6 +647,39 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
            }
        }

        /**
         * Starts playing effects on designated vibrators, in sync.
         *
         * @return A positive duration, in millis, to wait for the completion of this effect.
         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
         * returns the duration of a single run to be used as timeout for callbacks.
         */
        private long startVibratingSynced(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
            // This synchronization of vibrators should be executed one at a time, even if we are
            // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
            // one set of vibrators at a time.
            synchronized (mLock) {
                boolean hasPrepared = false;
                boolean hasTriggered = false;
                try {
                    hasPrepared = mCallbacks.prepareSyncedVibration(mRequiredCapabilities,
                            mVibratorIds);
                    long timeout = startVibrating(startTime, nextSteps);

                    // Check if preparation was successful, otherwise devices area already vibrating
                    if (hasPrepared) {
                        hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
                    }
                    return timeout;
                } finally {
                    if (hasPrepared && !hasTriggered) {
                        mCallbacks.cancelSyncedVibration();
                        return 0;
                    }
                }
            }
        }

        /**
         * Starts playing effects on designated vibrators.
         *
@@ -634,8 +687,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
         * effects, that should start in sync with all other effects in this step. The waveforms are
         * controlled by {@link AmplitudeStep} added to the {@code nextSteps} queue.
         *
         * @return A duration, in millis, to wait for the completion of all vibrations. This ignores
         * any repeating waveform duration and returns the duration of a single run.
         * @return A positive duration, in millis, to wait for the completion of this effect.
         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
         * returns the duration of a single run to be used as timeout for callbacks.
         */
        private long startVibrating(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
            long maxDuration = 0;
@@ -651,9 +705,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
        /**
         * Play a single effect on a single vibrator.
         *
         * @return A duration, in millis, to wait for the completion of this effect. This ignores
         * any repeating waveform duration and returns the duration of a single run to be used as
         * timeout for callbacks.
         * @return A positive duration, in millis, to wait for the completion of this effect.
         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
         * returns the duration of a single run to be used as timeout for callbacks.
         */
        private long startVibrating(VibratorController controller, VibrationEffect effect,
                long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
@@ -698,6 +752,49 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
                }
            }
        }

        /**
         * Return all capabilities required from the {@link IVibratorManager} to prepare and
         * trigger all given effects in sync.
         *
         * @return {@link IVibratorManager#CAP_SYNC} together with all required
         * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
         */
        private long calculateRequiredSyncCapabilities(SparseArray<VibrationEffect> effects) {
            long prepareCap = 0;
            for (int i = 0; i < effects.size(); i++) {
                VibrationEffect effect = effects.valueAt(i);
                if (effect instanceof VibrationEffect.OneShot
                        || effect instanceof VibrationEffect.Waveform) {
                    prepareCap |= IVibratorManager.CAP_PREPARE_ON;
                } else if (effect instanceof VibrationEffect.Prebaked) {
                    prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
                } else if (effect instanceof VibrationEffect.Composed) {
                    prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
                }
            }
            int triggerCap = 0;
            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
            }
            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
            }
            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
            }
            return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
        }

        /**
         * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
         * different ones, requiring a mixed trigger capability from the vibrator manager for
         * syncing all effects.
         */
        private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
            return (prepareCapabilities & capability) != 0
                    && (prepareCapabilities & ~capability) != 0;
        }
    }

    /** Represent a step to set amplitude on a single vibrator. */
@@ -794,12 +891,12 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
                // Waveform has ended, no more steps to run.
                return null;
            }
            long nextWakeUpTime = startTime + waveform.getTimings()[currentIndex];
            long nextStartTime = startTime + waveform.getTimings()[currentIndex];
            int nextIndex = currentIndex + 1;
            if (nextIndex >= waveform.getTimings().length) {
                nextIndex = waveform.getRepeatIndex();
            }
            return new AmplitudeStep(vibratorId, waveform, nextIndex, nextWakeUpTime,
            return new AmplitudeStep(vibratorId, waveform, nextIndex, nextStartTime,
                    nextVibratorStopTime());
        }

+90 −11

File changed.

Preview size limit exceeded, changes collapsed.

Loading