Merge "Add flag to refresh of user vibration settings before vibrating" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-22 14:42:43 +00:00
committed by Android (Google) Code Review
7 changed files with 174 additions and 40 deletions

View File

@@ -166,12 +166,28 @@ public final class VibrationAttributes implements Parcelable {
*/
public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF = 0x2;
/**
* Flag requesting vibration effect to be played with fresh user settings values.
*
* <p>This flag is not protected by any permission, but vibrations that use it require an extra
* query of user vibration intensity settings, ringer mode and other controls that affect the
* vibration effect playback, which can increase the latency for the overall request.
*
* <p>This is intended to be used on scenarios where the user settings might have changed
* recently, and needs to be applied to this vibration, like settings controllers that preview
* newly set intensities to the user.
*
* @hide
*/
public static final int FLAG_INVALIDATE_SETTINGS_CACHE = 0x3;
/**
* All flags supported by vibrator service, update it when adding new flag.
* @hide
*/
public static final int FLAG_ALL_SUPPORTED =
FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
| FLAG_INVALIDATE_SETTINGS_CACHE;
/** Creates a new {@link VibrationAttributes} instance with given usage. */
public static @NonNull VibrationAttributes createForUsage(@Usage int usage) {
@@ -446,8 +462,10 @@ public final class VibrationAttributes implements Parcelable {
}
/**
* Set flags
* @param flags combination of flags to be set.
* Sets only the flags specified in the bitmask, leaving the other supported flag values
* unchanged in the builder.
*
* @param flags Combination of flags to be set.
* @param mask Bit range that should be changed.
* @return the same Builder instance.
*/
@@ -456,5 +474,18 @@ public final class VibrationAttributes implements Parcelable {
mFlags = (mFlags & ~mask) | (flags & mask);
return this;
}
/**
* Set all supported flags with given combination of flags, overriding any previous values
* set to this builder.
*
* @param flags combination of flags to be set.
* @return the same Builder instance.
*
* @hide
*/
public @NonNull Builder setFlags(@Flag int flags) {
return setFlags(flags, FLAG_ALL_SUPPORTED);
}
}
}

View File

@@ -5446,8 +5446,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
VibrationAttributes attrs = getVibrationAttributes(effectId);
if (always) {
attrs = new VibrationAttributes.Builder(attrs)
.setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.build();
}
mVibrator.vibrate(uid, packageName, effect, reason, attrs);

View File

@@ -121,6 +121,11 @@ final class VibrationSettings {
USAGE_PHYSICAL_EMULATION,
USAGE_HARDWARE_FEEDBACK));
private static final IntentFilter USER_SWITCHED_INTENT_FILTER =
new IntentFilter(Intent.ACTION_USER_SWITCHED);
private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
/** Listener for changes on vibration settings. */
interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
@@ -130,11 +135,11 @@ final class VibrationSettings {
private final Object mLock = new Object();
private final Context mContext;
private final String mSystemUiPackage;
private final SettingsObserver mSettingObserver;
private final SettingsContentObserver mSettingObserver;
@VisibleForTesting
final UidObserver mUidObserver;
@VisibleForTesting
final UserObserver mUserReceiver;
final SettingsBroadcastReceiver mSettingChangeReceiver;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -154,6 +159,8 @@ final class VibrationSettings {
private boolean mBatterySaverMode;
@GuardedBy("mLock")
private boolean mVibrateOn;
@GuardedBy("mLock")
private int mRingerMode;
VibrationSettings(Context context, Handler handler) {
this(context, handler, new VibrationConfig(context.getResources()));
@@ -163,9 +170,9 @@ final class VibrationSettings {
VibrationSettings(Context context, Handler handler, VibrationConfig config) {
mContext = context;
mVibrationConfig = config;
mSettingObserver = new SettingsObserver(handler);
mSettingObserver = new SettingsContentObserver(handler);
mUidObserver = new UidObserver();
mUserReceiver = new UserObserver();
mSettingChangeReceiver = new SettingsBroadcastReceiver();
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -188,12 +195,13 @@ final class VibrationSettings {
VibrationEffect.get(VibrationEffect.EFFECT_TICK, false));
// Update with current values from settings.
updateSettings();
update();
}
public void onSystemReady() {
synchronized (mLock) {
mAudioManager = mContext.getSystemService(AudioManager.class);
mRingerMode = mAudioManager.getRingerModeInternal();
}
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
@@ -224,8 +232,8 @@ final class VibrationSettings {
}
});
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mUserReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
// Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
@@ -248,7 +256,7 @@ final class VibrationSettings {
Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
// Update with newly loaded services.
updateSettings();
update();
}
/**
@@ -396,16 +404,17 @@ final class VibrationSettings {
// Only ringtone and notification vibrations are disabled when phone is on silent mode.
return true;
}
// If audio manager was not loaded yet then assume most restrictive mode.
int ringerMode = (mAudioManager == null)
? AudioManager.RINGER_MODE_SILENT
: mAudioManager.getRingerModeInternal();
return ringerMode != AudioManager.RINGER_MODE_SILENT;
return mRingerMode != AudioManager.RINGER_MODE_SILENT;
}
/** Updates all vibration settings and triggers registered listeners. */
@VisibleForTesting
void updateSettings() {
/** Update all cached settings and triggers registered listeners. */
void update() {
updateSettings();
updateRingerMode();
notifyListeners();
}
private void updateSettings() {
synchronized (mLock) {
mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
@@ -435,7 +444,6 @@ final class VibrationSettings {
loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1),
getDefaultIntensity(USAGE_RINGTONE));
mCurrentVibrationIntensities.clear();
mCurrentVibrationIntensities.put(USAGE_ALARM, alarmIntensity);
mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity);
@@ -469,7 +477,16 @@ final class VibrationSettings {
// A11y is not disabled by any haptic feedback setting.
mCurrentVibrationIntensities.put(USAGE_ACCESSIBILITY, positiveHapticFeedbackIntensity);
}
notifyListeners();
}
private void updateRingerMode() {
synchronized (mLock) {
// If audio manager was not loaded yet then assume most restrictive mode.
// This will be loaded again as soon as the audio manager is loaded in onSystemReady.
mRingerMode = (mAudioManager == null)
? AudioManager.RINGER_MODE_SILENT
: mAudioManager.getRingerModeInternal();
}
}
@Override
@@ -586,6 +603,11 @@ final class VibrationSettings {
UserHandle.USER_ALL);
}
private void registerSettingsChangeReceiver(IntentFilter intentFilter) {
mContext.registerReceiver(mSettingChangeReceiver, intentFilter,
Context.RECEIVER_NOT_EXPORTED);
}
@Nullable
private VibrationEffect createEffectFromResource(int resId) {
long[] timings = getLongIntArray(mContext.getResources(), resId);
@@ -616,24 +638,33 @@ final class VibrationSettings {
}
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
private final class SettingsContentObserver extends ContentObserver {
SettingsContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
updateSettings();
notifyListeners();
}
}
/** Implementation of {@link BroadcastReceiver} to update settings on current user change. */
/**
* Implementation of {@link BroadcastReceiver} to update settings on current user or ringer
* mode change.
*/
@VisibleForTesting
final class UserObserver extends BroadcastReceiver {
final class SettingsBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
updateSettings();
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// Reload all settings, as they are user-based.
update();
} else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
updateRingerMode();
notifyListeners();
}
}
}

View File

@@ -401,6 +401,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
uid, opPkg, reason);
fillVibrationFallbacks(vib, effect);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
// Force update of user settings before checking if this vibration effect should
// be ignored or scaled.
mVibrationSettings.update();
}
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
@@ -1506,12 +1512,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return IExternalVibratorService.SCALE_MUTE;
}
VibrationAttributes attrs = fixupVibrationAttributes(vib.getVibrationAttributes(),
/* effect= */ null);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
// Force update of user settings before checking if this vibration effect should
// be ignored or scaled.
mVibrationSettings.update();
}
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
int scale;
synchronized (mLock) {
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
vib.getUid(), vib.getPackage(), attrs);
if (ignoreStatus != null) {
ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
@@ -1549,7 +1563,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
vib.linkToDeath(mCurrentExternalVibration);
mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
vib.getVibrationAttributes().getUsage());
attrs.getUsage());
scale = mCurrentExternalVibration.scale;
}
@@ -1908,7 +1922,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
final int flags =
commonOptions.force ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY : 0;
return new VibrationAttributes.Builder()
.setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
.setFlags(flags)
// Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects.
.setUsage(VibrationAttributes.USAGE_TOUCH)
.build();

View File

@@ -277,6 +277,6 @@ public class VibrationScalerTest {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
mVibrationSettings.update();
}
}

View File

@@ -475,6 +475,24 @@ public class VibrationSettingsTest {
}
}
@Test
public void shouldIgnoreVibration_updateTriggeredAfterInternalRingerModeChanged() {
// Vibrating settings on are overruled by ringer mode.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertVibrationNotIgnoredForUsage(USAGE_RINGTONE);
// Testing the broadcast flow manually.
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
assertVibrationIgnoredForUsage(USAGE_RINGTONE, Vibration.Status.IGNORED_FOR_RINGER_MODE);
}
@Test
public void shouldVibrateInputDevices_returnsSettingsValue() {
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
@@ -577,7 +595,7 @@ public class VibrationSettingsTest {
Settings.System.putIntForUser(mContextSpy.getContentResolver(),
Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
UserHandle.USER_CURRENT);
mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(Intent.ACTION_USER_SWITCHED));
assertEquals(VIBRATION_INTENSITY_LOW,
mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
@@ -587,7 +605,7 @@ public class VibrationSettingsTest {
public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() {
setDefaultIntensity(USAGE_HARDWARE_FEEDBACK, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
mVibrationSettings.updateSettings();
mVibrationSettings.update();
assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
// If haptic feedback is off, fallback to default value.
assertEquals(VIBRATION_INTENSITY_MEDIUM,
@@ -596,7 +614,7 @@ public class VibrationSettingsTest {
mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
mVibrationSettings.updateSettings();
mVibrationSettings.update();
assertEquals(VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
// If haptic feedback is on, fallback to that value.
@@ -633,7 +651,7 @@ public class VibrationSettingsTest {
mVibrationSettings.shouldIgnoreVibration(UID,
new VibrationAttributes.Builder()
.setUsage(usage)
.setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
.setFlags(flags)
.build()));
}
@@ -654,18 +672,19 @@ public class VibrationSettingsTest {
Settings.System.putStringForUser(
mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
mVibrationSettings.update();
}
private void setUserSetting(String settingName, int value) {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
mVibrationSettings.update();
}
private void setRingerMode(int ringerMode) {
mAudioManager.setRingerModeInternal(ringerMode);
assertEquals(ringerMode, mAudioManager.getRingerModeInternal());
mVibrationSettings.update();
}
}

View File

@@ -672,6 +672,42 @@ public class VibratorManagerServiceTest {
eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
}
@Test
public void vibrate_withVibrationAttributesEnforceFreshSettings_refreshesVibrationSettings()
throws Exception {
mockVibrators(0);
mVibratorProviders.get(0).setSupportedEffects(VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_TICK);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
VibratorManagerService service = createSystemReadyService();
VibrationAttributes enforceFreshAttrs = new VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_NOTIFICATION)
.setFlags(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)
.build();
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), NOTIFICATION_ATTRS);
// VibrationThread will start this vibration async, so wait before vibrating a second time.
assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 0,
service, TEST_TIMEOUT_MILLIS));
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), enforceFreshAttrs);
// VibrationThread will start this vibration async, so wait before checking.
assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 1,
service, TEST_TIMEOUT_MILLIS));
assertEquals(
Arrays.asList(
expectedPrebaked(VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_STRENGTH_STRONG),
expectedPrebaked(VibrationEffect.EFFECT_TICK,
VibrationEffect.EFFECT_STRENGTH_LIGHT)),
mVibratorProviders.get(0).getAllEffectSegments());
}
@Test
public void vibrate_withAttributesUnknownUsage_usesEffectToIdentifyTouchUsage() {
VibratorManagerService service = createSystemReadyService();
@@ -1280,7 +1316,11 @@ public class VibratorManagerServiceTest {
}
private VibrationEffectSegment expectedPrebaked(int effectId) {
return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
return expectedPrebaked(effectId, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
private VibrationEffectSegment expectedPrebaked(int effectId, int effectStrength) {
return new PrebakedSegment(effectId, false, effectStrength);
}
private void mockCapabilities(long... capabilities) {