Loading services/core/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -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", Loading services/core/java/com/android/server/VibratorManagerService.java +87 −14 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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); Loading @@ -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]; Loading @@ -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}. */ Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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(); Loading Loading @@ -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 = Loading @@ -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); } } Loading services/core/java/com/android/server/VibratorService.java +8 −2 Original line number Diff line number Diff line Loading @@ -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 Loading services/core/java/com/android/server/vibrator/VibrationThread.java +116 −19 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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") Loading @@ -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); Loading Loading @@ -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)) { Loading @@ -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; } Loading @@ -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) { Loading @@ -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. * Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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. */ Loading Loading @@ -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()); } Loading services/core/jni/com_android_server_VibratorManagerService.cpp +90 −11 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -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", Loading
services/core/java/com/android/server/VibratorManagerService.java +87 −14 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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); Loading @@ -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]; Loading @@ -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}. */ Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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(); Loading Loading @@ -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 = Loading @@ -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); } } Loading
services/core/java/com/android/server/VibratorService.java +8 −2 Original line number Diff line number Diff line Loading @@ -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 Loading
services/core/java/com/android/server/vibrator/VibrationThread.java +116 −19 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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") Loading @@ -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); Loading Loading @@ -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)) { Loading @@ -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; } Loading @@ -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) { Loading @@ -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. * Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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. */ Loading Loading @@ -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()); } Loading
services/core/jni/com_android_server_VibratorManagerService.cpp +90 −11 File changed.Preview size limit exceeded, changes collapsed. Show changes