diff options
14 files changed, 159 insertions, 92 deletions
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java index a4817ae27fa5..1ab183626d2e 100644 --- a/core/java/android/hardware/input/InputDeviceVibrator.java +++ b/core/java/android/hardware/input/InputDeviceVibrator.java @@ -175,4 +175,9 @@ final class InputDeviceVibrator extends Vibrator { public void cancel() { mInputManager.cancelVibrate(mDeviceId, mToken); } + + @Override + public void cancel(int usageFilter) { + cancel(); + } } diff --git a/core/java/android/hardware/input/InputDeviceVibratorManager.java b/core/java/android/hardware/input/InputDeviceVibratorManager.java index ed0efffbb346..7caff7209cc5 100644 --- a/core/java/android/hardware/input/InputDeviceVibratorManager.java +++ b/core/java/android/hardware/input/InputDeviceVibratorManager.java @@ -134,4 +134,9 @@ public class InputDeviceVibratorManager extends VibratorManager public void cancel() { mInputManager.cancelVibrate(mDeviceId, mToken); } + + @Override + public void cancel(int usageFilter) { + cancel(); + } } diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl index c58cc4f9988f..a0d6ce1ba108 100644 --- a/core/java/android/os/IVibratorManagerService.aidl +++ b/core/java/android/os/IVibratorManagerService.aidl @@ -32,5 +32,5 @@ interface IVibratorManagerService { in CombinedVibration vibration, in VibrationAttributes attributes); void vibrate(int uid, String opPkg, in CombinedVibration vibration, in VibrationAttributes attributes, String reason, IBinder token); - void cancelVibrate(IBinder token); + void cancelVibrate(int usageFilter, IBinder token); } diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java index 6bb016519a84..7859b5cfe5df 100644 --- a/core/java/android/os/NullVibrator.java +++ b/core/java/android/os/NullVibrator.java @@ -54,4 +54,8 @@ public class NullVibrator extends Vibrator { @Override public void cancel() { } + + @Override + public void cancel(int usageFilter) { + } } diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index 2e8ecb59b0d3..70808594225f 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -256,6 +256,15 @@ public class SystemVibrator extends Vibrator { mVibratorManager.cancel(); } + @Override + public void cancel(int usageFilter) { + if (mVibratorManager == null) { + Log.w(TAG, "Failed to cancel vibrate; no vibrator manager."); + return; + } + mVibratorManager.cancel(usageFilter); + } + /** * Tries to unregister individual {@link android.os.Vibrator.OnVibratorStateChangedListener} * that were left registered to vibrators after failures to register them to all vibrators. diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java index 84a1016e3364..ba86c6f9ec1a 100644 --- a/core/java/android/os/SystemVibratorManager.java +++ b/core/java/android/os/SystemVibratorManager.java @@ -146,12 +146,21 @@ public class SystemVibratorManager extends VibratorManager { @Override public void cancel() { + cancelVibration(/* usageFilter= */ -1); + } + + @Override + public void cancel(int usageFilter) { + cancelVibration(usageFilter); + } + + private void cancelVibration(int usageFilter) { if (mService == null) { Log.w(TAG, "Failed to cancel vibration; no vibrator manager service."); return; } try { - mService.cancelVibrate(mToken); + mService.cancelVibrate(usageFilter, mToken); } catch (RemoteException e) { Log.w(TAG, "Failed to cancel vibration.", e); } @@ -232,54 +241,32 @@ public class SystemVibratorManager extends VibratorManager { @Override public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) { - if (mService == null) { - Log.w(TAG, "Failed to set always-on effect on vibrator " + mVibratorInfo.getId() - + "; no vibrator manager service."); - return false; - } - try { - VibrationAttributes attr = new VibrationAttributes.Builder( - attributes, effect).build(); - CombinedVibration combined = CombinedVibration.startParallel() - .addVibrator(mVibratorInfo.getId(), effect) - .combine(); - return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined, attr); - } catch (RemoteException e) { - Log.w(TAG, "Failed to set always-on effect on vibrator " + mVibratorInfo.getId()); - } - return false; + VibrationAttributes attr = new VibrationAttributes.Builder( + attributes, effect).build(); + CombinedVibration combined = CombinedVibration.startParallel() + .addVibrator(mVibratorInfo.getId(), effect) + .combine(); + return SystemVibratorManager.this.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined, + attr); } @Override public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, @NonNull VibrationAttributes attributes) { - if (mService == null) { - Log.w(TAG, "Failed to vibrate on vibrator " + mVibratorInfo.getId() - + "; no vibrator manager service."); - return; - } - try { - CombinedVibration combined = CombinedVibration.startParallel() - .addVibrator(mVibratorInfo.getId(), vibe) - .combine(); - mService.vibrate(uid, opPkg, combined, attributes, reason, mToken); - } catch (RemoteException e) { - Log.w(TAG, "Failed to vibrate.", e); - } + CombinedVibration combined = CombinedVibration.startParallel() + .addVibrator(mVibratorInfo.getId(), vibe) + .combine(); + SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes); } @Override public void cancel() { - if (mService == null) { - Log.w(TAG, "Failed to cancel vibration on vibrator " + mVibratorInfo.getId() - + "; no vibrator manager service."); - return; - } - try { - mService.cancelVibrate(mToken); - } catch (RemoteException e) { - Log.w(TAG, "Failed to cancel vibration on vibrator " + mVibratorInfo.getId(), e); - } + SystemVibratorManager.this.cancel(); + } + + @Override + public void cancel(int usageFilter) { + SystemVibratorManager.this.cancel(usageFilter); } @Override diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index a0f70c8fa526..54cb83f7caf1 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -494,6 +494,16 @@ public abstract class Vibrator { public abstract void cancel(); /** + * Cancel specific types of ongoing vibrations. + * + * @param usageFilter The type of vibration to be cancelled, represented as a bitwise + * combination of {@link VibrationAttributes.Usage} values. + * @hide + */ + @RequiresPermission(android.Manifest.permission.VIBRATE) + public abstract void cancel(int usageFilter); + + /** * Check whether the vibrator is vibrating. * * @return True if the hardware is vibrating, otherwise false. diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java index 7c911160dfa6..01cece39b922 100644 --- a/core/java/android/os/VibratorManager.java +++ b/core/java/android/os/VibratorManager.java @@ -136,4 +136,14 @@ public abstract class VibratorManager { */ @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel(); + + /** + * Cancel specific types of ongoing vibrations. + * + * @param usageFilter The type of vibration to be cancelled, represented as a bitwise + * combination of {@link VibrationAttributes.Usage} values. + * @hide + */ + @RequiresPermission(android.Manifest.permission.VIBRATE) + public abstract void cancel(int usageFilter); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bd442bdf9d63..21b93a74655f 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -212,6 +212,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.DeviceConfig; @@ -1076,23 +1077,8 @@ public class NotificationManagerService extends SystemService { (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; if (disableNotificationEffects(null) != null) { // cancel whatever's going on - final long identity = Binder.clearCallingIdentity(); - try { - final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); - if (player != null) { - player.stopAsync(); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(identity); - } - - final long identity2 = Binder.clearCallingIdentity(); - try { - mVibrator.cancel(); - } finally { - Binder.restoreCallingIdentity(identity2); - } + clearSoundLocked(); + clearVibrateLocked(); } } } @@ -1582,7 +1568,10 @@ public class NotificationManagerService extends SystemService { mVibrateNotificationKey = null; final long identity = Binder.clearCallingIdentity(); try { - mVibrator.cancel(); + // Stop all vibrations with usage of class alarm (ringtone, alarm, notification usages). + int usageFilter = + VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK; + mVibrator.cancel(usageFilter); } finally { Binder.restoreCallingIdentity(identity); } @@ -8301,29 +8290,12 @@ public class NotificationManagerService extends SystemService { // sound if (canceledKey.equals(mSoundNotificationKey)) { - mSoundNotificationKey = null; - final long identity = Binder.clearCallingIdentity(); - try { - final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); - if (player != null) { - player.stopAsync(); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(identity); - } + clearSoundLocked(); } // vibrate if (canceledKey.equals(mVibrateNotificationKey)) { - mVibrateNotificationKey = null; - final long identity = Binder.clearCallingIdentity(); - try { - mVibrator.cancel(); - } - finally { - Binder.restoreCallingIdentity(identity); - } + clearVibrateLocked(); } // light diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 5d2b1b169c4e..06a5077bec82 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -383,7 +383,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @Override // Binder call - public void cancelVibrate(IBinder token) { + public void cancelVibrate(int usageFilter, IBinder token) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelVibrate"); try { mContext.enforceCallingOrSelfPermission( @@ -392,16 +392,24 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { synchronized (mLock) { if (DEBUG) { - Slog.d(TAG, "Canceling vibration."); + Slog.d(TAG, "Canceling vibration"); } final long ident = Binder.clearCallingIdentity(); try { - mNextVibration = null; + if (mNextVibration != null + && shouldCancelVibration(mNextVibration.getVibration(), + usageFilter, token)) { + mNextVibration = null; + } if (mCurrentVibration != null - && mCurrentVibration.getVibration().token == token) { + && shouldCancelVibration(mCurrentVibration.getVibration(), + usageFilter, token)) { mCurrentVibration.cancel(); } - if (mCurrentExternalVibration != null) { + if (mCurrentExternalVibration != null + && shouldCancelVibration( + mCurrentExternalVibration.externalVibration.getVibrationAttributes(), + usageFilter)) { mCurrentExternalVibration.end(Vibration.Status.CANCELLED); mVibratorManagerRecords.record(mCurrentExternalVibration); mCurrentExternalVibration.externalVibration.mute(); @@ -693,6 +701,30 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } /** + * Return true if the vibration has the same token and usage belongs to given usage class. + * + * @param vib The ongoing or pending vibration to be cancelled. + * @param usageFilter The vibration usages to be cancelled, any bitwise combination of + * VibrationAttributes.USAGE_* values. + * @param token The binder token to identify the vibration origin. Only vibrations + * started with the same token can be cancelled with it. + */ + private boolean shouldCancelVibration(Vibration vib, int usageFilter, IBinder token) { + return (vib.token == token) && shouldCancelVibration(vib.attrs, usageFilter); + } + + /** + * Return true if the external vibration usage belongs to given usage class. + * + * @param attrs The attributes of an ongoing or pending vibration to be cancelled. + * @param usageFilter The vibration usages to be cancelled, any bitwise combination of + * VibrationAttributes.USAGE_* values. + */ + private boolean shouldCancelVibration(VibrationAttributes attrs, int usageFilter) { + return (usageFilter & attrs.getUsage()) == attrs.getUsage(); + } + + /** * Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and * {@code attrs}. This will return one of the AppOpsManager.MODE_*. */ @@ -1501,7 +1533,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } private int runCancel() { - cancelVibrate(mToken); + cancelVibrate(/* usageFilter= */ -1, mToken); return 0; } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java index 014bfd2d40e4..e739a7658a95 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java @@ -76,4 +76,8 @@ final class FakeVibrator extends Vibrator { @Override public void cancel() { } + + @Override + public void cancel(int usageFilter) { + } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index e367b7448299..b4c1de1c68f5 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -234,7 +234,7 @@ public class VibratorManagerServiceTest { CombinedVibration effect = CombinedVibration.createParallel( VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS); - service.cancelVibrate(service); + service.cancelVibrate(/* usageFilter= */ -1, service); assertTrue(service.setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); @@ -880,17 +880,42 @@ public class VibratorManagerServiceTest { } @Test - public void cancelVibrate_stopsVibrating() throws Exception { + public void cancelVibrate_withoutUsageFilter_stopsVibrating() throws Exception { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); - service.cancelVibrate(service); + service.cancelVibrate(/* usageFilter= */ -1, service); assertFalse(service.isVibrating(1)); - vibrate(service, VibrationEffect.createOneShot(10_000, 100), ALARM_ATTRS); + vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS); assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); - service.cancelVibrate(service); + service.cancelVibrate(/* usageFilter= */ -1, service); + assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + } + + @Test + public void cancelVibrate_withFilter_onlyCancelsVibrationWithFilteredUsage() throws Exception { + mockVibrators(1); + VibratorManagerService service = createSystemReadyService(); + + vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS); + assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + // Vibration is not cancelled with a different usage. + service.cancelVibrate(VibrationAttributes.USAGE_RINGTONE, service); + assertFalse(waitUntil(s -> !s.isVibrating(1), service, /* timeout= */ 50)); + + // Vibration is not cancelled with a different usage class used as filter. + service.cancelVibrate( + VibrationAttributes.USAGE_CLASS_FEEDBACK | ~VibrationAttributes.USAGE_CLASS_MASK, + service); + assertFalse(waitUntil(s -> !s.isVibrating(1), service, /* timeout= */ 50)); + + // Vibration is cancelled with usage class as filter. + service.cancelVibrate( + VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK, + service); assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index ff881748cfea..00eb0f284719 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; @@ -444,11 +445,14 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } private void verifyStopVibrate() { - verify(mVibrator, times(1)).cancel(); + int alarmClassUsageFilter = + VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK; + verify(mVibrator, times(1)).cancel(eq(alarmClassUsageFilter)); } - private void verifyNeverStopVibrate() throws RemoteException { + private void verifyNeverStopVibrate() { verify(mVibrator, never()).cancel(); + verify(mVibrator, never()).cancel(anyInt()); } private void verifyNeverLights() { diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java index 7cd2c23162fb..e0f3f03e9cb7 100644 --- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java +++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java @@ -140,7 +140,7 @@ public class VibratorManagerServicePermissionTest { @Test public void testCancelVibrateFails() throws RemoteException { expectSecurityException("VIBRATE"); - mVibratorService.cancelVibrate(new Binder()); + mVibratorService.cancelVibrate(/* usageFilter= */ -1, new Binder()); } private void expectSecurityException(String expectedPermission) { |