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

Commit 51723bac authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Use vibrator manager controller in VibratorManagerService" into sc-dev am: 8fddb7a4

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13417830

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Id021ba789b712847f9f4fa6a94ab43c5277b8c8e
parents 05339b08 8fddb7a4
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