Merge "Add Vibrator.cancel method with usage filters" into sc-dev

This commit is contained in:
Lais Andrade
2021-04-13 17:30:51 +00:00
committed by Android (Google) Code Review
14 changed files with 159 additions and 92 deletions

View File

@@ -175,4 +175,9 @@ final class InputDeviceVibrator extends Vibrator {
public void cancel() {
mInputManager.cancelVibrate(mDeviceId, mToken);
}
@Override
public void cancel(int usageFilter) {
cancel();
}
}

View File

@@ -134,4 +134,9 @@ public class InputDeviceVibratorManager extends VibratorManager
public void cancel() {
mInputManager.cancelVibrate(mDeviceId, mToken);
}
@Override
public void cancel(int usageFilter) {
cancel();
}
}

View File

@@ -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);
}

View File

@@ -54,4 +54,8 @@ public class NullVibrator extends Vibrator {
@Override
public void cancel() {
}
@Override
public void cancel(int usageFilter) {
}
}

View File

@@ -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.

View File

@@ -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

View File

@@ -493,6 +493,16 @@ public abstract class Vibrator {
@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);
/**
* Check whether the vibrator is vibrating.
*

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
@@ -692,6 +700,30 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return null;
}
/**
* 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;
}

View File

@@ -76,4 +76,8 @@ final class FakeVibrator extends Vibrator {
@Override
public void cancel() {
}
@Override
public void cancel(int usageFilter) {
}
}

View File

@@ -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));
}

View File

@@ -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() {

View File

@@ -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) {