diff options
| author | 2022-03-01 12:46:31 +0000 | |
|---|---|---|
| committer | 2022-03-01 12:46:31 +0000 | |
| commit | ff122ff053ba3400fd820bd6b562a8a19e381ec1 (patch) | |
| tree | a67e8a55c9333dd1c429cbf119b9422ed4a04873 | |
| parent | 1b512780460f2f6ccac2785cfb82eaa4ee0b4ee7 (diff) | |
| parent | 8d2117d1078b7a1b745a43478135b41ca7ef0010 (diff) | |
Merge "Enable system vibrations on screen off for other feedbacks" into tm-dev
6 files changed, 152 insertions, 39 deletions
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index aad59c67b60c..b05b44bcb1d2 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -34,6 +34,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManagerInternal; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; @@ -42,6 +43,7 @@ import android.os.Handler; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.VibrationAttributes; @@ -106,6 +108,19 @@ final class VibrationSettings { */ private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY; + /** + * Set of usages allowed for vibrations from system packages when the screen goes off. + * + * <p>Some examples are touch and hardware feedback, and physical emulation. When the system is + * playing one of these usages during the screen off event then the vibration will not be + * cancelled by the service. + */ + private static final Set<Integer> SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST = new HashSet<>( + Arrays.asList( + USAGE_TOUCH, + USAGE_PHYSICAL_EMULATION, + USAGE_HARDWARE_FEEDBACK)); + /** Listener for changes on vibration settings. */ interface OnVibratorSettingsChanged { /** Callback triggered when any of the vibrator settings change. */ @@ -114,6 +129,7 @@ final class VibrationSettings { private final Object mLock = new Object(); private final Context mContext; + private final String mSystemUiPackage; private final SettingsObserver mSettingObserver; @VisibleForTesting final UidObserver mUidObserver; @@ -151,6 +167,9 @@ final class VibrationSettings { mUidObserver = new UidObserver(); mUserReceiver = new UserObserver(); + mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class) + .getSystemUiServiceComponent().getPackageName(); + VibrationEffect clickEffect = createEffectFromResource( com.android.internal.R.array.config_virtualKeyVibePattern); VibrationEffect doubleClickEffect = createEffectFromResource( @@ -344,6 +363,26 @@ final class VibrationSettings { } /** + * Check if given vibration should be cancelled by the service when the screen goes off. + * + * <p>When the system is entering a non-interactive state, we want to cancel vibrations in case + * a misbehaving app has abandoned them. However, it may happen that the system is currently + * playing haptic feedback as part of the transition. So we don't cancel system vibrations of + * usages like touch and hardware feedback, and physical emulation. + * + * @return true if the vibration should be cancelled when the screen goes off, false otherwise. + */ + public boolean shouldCancelVibrationOnScreenOff(int uid, String opPkg, + @VibrationAttributes.Usage int usage) { + if (!SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST.contains(usage)) { + // Usages not allowed even for system vibrations should always be cancelled. + return true; + } + // Only allow vibrations from System packages to continue vibrating when the screen goes off + return uid != Process.SYSTEM_UID && uid != 0 && !mSystemUiPackage.equals(opPkg); + } + + /** * Return {@code true} if the device should vibrate for current ringer mode. * * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 01f9d0b94879..94d0a7b22cdc 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -28,7 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.hardware.vibrator.IVibrator; import android.os.BatteryStats; import android.os.Binder; @@ -62,7 +61,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; -import com.android.server.LocalServices; import com.android.server.SystemService; import libcore.util.NativeAllocationRegistry; @@ -120,7 +118,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private final Object mLock = new Object(); private final Context mContext; - private final String mSystemUiPackage; private final PowerManager.WakeLock mWakeLock; private final IBatteryStats mBatteryStatsService; private final Handler mHandler; @@ -153,17 +150,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { synchronized (mLock) { - // When the system is entering a non-interactive state, we want - // to cancel vibrations in case a misbehaving app has abandoned - // them. However it may happen that the system is currently playing - // haptic feedback as part of the transition. So we don't cancel - // system vibrations. - if (mNextVibration != null - && !isSystemHapticFeedback(mNextVibration.getVibration())) { + // When the system is entering a non-interactive state, we want to cancel + // vibrations in case a misbehaving app has abandoned them. + if (shouldCancelOnScreenOffLocked(mNextVibration)) { clearNextVibrationLocked(Vibration.Status.CANCELLED); } - if (mCurrentVibration != null - && !isSystemHapticFeedback(mCurrentVibration.getVibration())) { + if (shouldCancelOnScreenOffLocked(mCurrentVibration)) { mCurrentVibration.cancel(); } } @@ -203,9 +195,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { com.android.internal.R.integer.config_previousVibrationsDumpLimit); mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit); - mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class) - .getSystemUiServiceComponent().getPackageName(); - mBatteryStatsService = injector.getBatteryStatsService(); mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -1074,11 +1063,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { == PackageManager.PERMISSION_GRANTED; } - private boolean isSystemHapticFeedback(Vibration vib) { - if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) { + @GuardedBy("mLock") + private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationThread vibrationThread) { + if (vibrationThread == null) { return false; } - return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg); + Vibration vib = vibrationThread.getVibration(); + return mVibrationSettings.shouldCancelVibrationOnScreenOff( + vib.uid, vib.opPkg, vib.attrs.getUsage()); } @GuardedBy("mLock") diff --git a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java index 5e9e16a0baf8..1a146f636f6e 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java @@ -18,7 +18,10 @@ package com.android.server.vibrator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import android.content.ComponentName; +import android.content.pm.PackageManagerInternal; import android.hardware.vibrator.IVibrator; import android.os.Handler; import android.os.VibrationEffect; @@ -33,8 +36,14 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; +import com.android.server.LocalServices; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.util.Arrays; import java.util.stream.IntStream; @@ -59,10 +68,19 @@ public class DeviceVibrationEffectAdapterTest { new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private PackageManagerInternal mPackageManagerInternalMock; + private DeviceVibrationEffectAdapter mAdapter; @Before public void setUp() throws Exception { + when(mPackageManagerInternalMock.getSystemUiServiceComponent()) + .thenReturn(new ComponentName("", "")); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); + VibrationSettings vibrationSettings = new VibrationSettings( InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper())); mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java index 81677101c139..0301e94a6acc 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java @@ -31,8 +31,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContextWrapper; +import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.IExternalVibratorService; import android.os.PowerManagerInternal; @@ -76,6 +78,7 @@ public class VibrationScalerTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock private PowerManagerInternal mPowerManagerInternalMock; + @Mock private PackageManagerInternal mPackageManagerInternalMock; @Mock private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; @@ -90,7 +93,11 @@ public class VibrationScalerTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); + when(mPackageManagerInternalMock.getSystemUiServiceComponent()) + .thenReturn(new ComponentName("", "")); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index b7cfbaf4887f..3cda2a554e48 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -47,13 +47,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContextWrapper; import android.content.Intent; +import android.content.pm.PackageManagerInternal; import android.media.AudioManager; import android.os.Handler; import android.os.PowerManagerInternal; import android.os.PowerSaveState; +import android.os.Process; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; @@ -87,6 +90,7 @@ import org.mockito.junit.MockitoRule; public class VibrationSettingsTest { private static final int UID = 1; + private static final String SYSUI_PACKAGE_NAME = "sysui"; private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build(); private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() .setBatterySaverEnabled(true).build(); @@ -104,17 +108,13 @@ public class VibrationSettingsTest { USAGE_TOUCH, }; - @Rule - public MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Rule - public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); - @Mock - private VibrationSettings.OnVibratorSettingsChanged mListenerMock; - @Mock - private PowerManagerInternal mPowerManagerInternalMock; - @Mock - private VibrationConfig mVibrationConfigMock; + @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock; + @Mock private PowerManagerInternal mPowerManagerInternalMock; + @Mock private PackageManagerInternal mPackageManagerInternalMock; + @Mock private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; private ContextWrapper mContextSpy; @@ -129,14 +129,17 @@ public class VibrationSettingsTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); - doAnswer(invocation -> { mRegisteredPowerModeListener = invocation.getArgument(0); return null; }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any()); + when(mPackageManagerInternalMock.getSystemUiServiceComponent()) + .thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, "")); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM); mAudioManager = mContextSpy.getSystemService(AudioManager.class); @@ -472,6 +475,55 @@ public class VibrationSettingsTest { } @Test + public void shouldCancelVibrationOnScreenOff_withNonSystemPackageAndUid_returnsAlwaysTrue() { + for (int usage : ALL_USAGES) { + assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(UID, "some.app", usage)); + } + } + + @Test + public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForTouchAndHardware() { + for (int usage : ALL_USAGES) { + if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK + || usage == USAGE_PHYSICAL_EMULATION) { + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + /* uid= */ 0, "", usage)); + } else { + assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff( + /* uid= */ 0, "", usage)); + } + } + } + + @Test + public void shouldCancelVibrationOnScreenOff_withSystemUid_returnsFalseForTouchAndHardware() { + for (int usage : ALL_USAGES) { + if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK + || usage == USAGE_PHYSICAL_EMULATION) { + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + Process.SYSTEM_UID, "", usage)); + } else { + assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff( + Process.SYSTEM_UID, "", usage)); + } + } + } + + @Test + public void shouldCancelVibrationOnScreenOff_withSysUi_returnsFalseForTouchAndHardware() { + for (int usage : ALL_USAGES) { + if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK + || usage == USAGE_PHYSICAL_EMULATION) { + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + UID, SYSUI_PACKAGE_NAME, usage)); + } else { + assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff( + UID, SYSUI_PACKAGE_NAME, usage)); + } + } + } + + @Test public void getDefaultIntensity_returnsIntensityFromVibratorConfig() { setDefaultIntensity(VIBRATION_INTENSITY_HIGH); setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 3d24a814c7cd..0590d7df5ee3 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -35,7 +35,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.hardware.vibrator.Braking; import android.hardware.vibrator.IVibrator; import android.hardware.vibrator.IVibratorManager; @@ -61,6 +63,8 @@ import android.util.SparseArray; import androidx.test.InstrumentationRegistry; +import com.android.server.LocalServices; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -94,17 +98,13 @@ public class VibrationThreadTest { private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build(); private static final int TEST_RAMP_STEP_DURATION = 5; - @Rule - public MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Mock - private VibrationThread.VibratorManagerHooks mManagerHooks; - @Mock - private VibratorController.OnVibrationCompleteListener mControllerCallbacks; - @Mock - private IBinder mVibrationToken; - @Mock - private VibrationConfig mVibrationConfigMock; + @Mock private PackageManagerInternal mPackageManagerInternalMock; + @Mock private VibrationThread.VibratorManagerHooks mManagerHooks; + @Mock private VibratorController.OnVibrationCompleteListener mControllerCallbacks; + @Mock private IBinder mVibrationToken; + @Mock private VibrationConfig mVibrationConfigMock; private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); private VibrationSettings mVibrationSettings; @@ -123,6 +123,11 @@ public class VibrationThreadTest { when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt())) .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); when(mVibrationConfigMock.getRampStepDurationMs()).thenReturn(TEST_RAMP_STEP_DURATION); + when(mPackageManagerInternalMock.getSystemUiServiceComponent()) + .thenReturn(new ComponentName("", "")); + + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); Context context = InstrumentationRegistry.getContext(); mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()), |