Loading services/core/java/com/android/server/vibrator/VibrationSettings.java +39 −0 Original line number Original line Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.content.res.Resources; import android.content.res.Resources; import android.database.ContentObserver; import android.database.ContentObserver; import android.media.AudioManager; import android.media.AudioManager; Loading @@ -42,6 +43,7 @@ import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationAttributes; Loading Loading @@ -106,6 +108,19 @@ final class VibrationSettings { */ */ private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY; 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. */ /** Listener for changes on vibration settings. */ interface OnVibratorSettingsChanged { interface OnVibratorSettingsChanged { /** Callback triggered when any of the vibrator settings change. */ /** Callback triggered when any of the vibrator settings change. */ Loading @@ -114,6 +129,7 @@ final class VibrationSettings { private final Object mLock = new Object(); private final Object mLock = new Object(); private final Context mContext; private final Context mContext; private final String mSystemUiPackage; private final SettingsObserver mSettingObserver; private final SettingsObserver mSettingObserver; @VisibleForTesting @VisibleForTesting final UidObserver mUidObserver; final UidObserver mUidObserver; Loading Loading @@ -151,6 +167,9 @@ final class VibrationSettings { mUidObserver = new UidObserver(); mUidObserver = new UidObserver(); mUserReceiver = new UserObserver(); mUserReceiver = new UserObserver(); mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class) .getSystemUiServiceComponent().getPackageName(); VibrationEffect clickEffect = createEffectFromResource( VibrationEffect clickEffect = createEffectFromResource( com.android.internal.R.array.config_virtualKeyVibePattern); com.android.internal.R.array.config_virtualKeyVibePattern); VibrationEffect doubleClickEffect = createEffectFromResource( VibrationEffect doubleClickEffect = createEffectFromResource( Loading Loading @@ -343,6 +362,26 @@ final class VibrationSettings { return null; return null; } } /** * 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. * Return {@code true} if the device should vibrate for current ringer mode. * * Loading services/core/java/com/android/server/vibrator/VibratorManagerService.java +10 −18 Original line number Original line Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.hardware.vibrator.IVibrator; import android.hardware.vibrator.IVibrator; import android.os.BatteryStats; import android.os.BatteryStats; import android.os.Binder; import android.os.Binder; Loading Loading @@ -62,7 +61,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemService; import libcore.util.NativeAllocationRegistry; import libcore.util.NativeAllocationRegistry; Loading Loading @@ -120,7 +118,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private final Object mLock = new Object(); private final Object mLock = new Object(); private final Context mContext; private final Context mContext; private final String mSystemUiPackage; private final PowerManager.WakeLock mWakeLock; private final PowerManager.WakeLock mWakeLock; private final IBatteryStats mBatteryStatsService; private final IBatteryStats mBatteryStatsService; private final Handler mHandler; private final Handler mHandler; Loading Loading @@ -153,17 +150,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { synchronized (mLock) { synchronized (mLock) { // When the system is entering a non-interactive state, we want // When the system is entering a non-interactive state, we want to cancel // to cancel vibrations in case a misbehaving app has abandoned // vibrations in case a misbehaving app has abandoned them. // them. However it may happen that the system is currently playing if (shouldCancelOnScreenOffLocked(mNextVibration)) { // haptic feedback as part of the transition. So we don't cancel // system vibrations. if (mNextVibration != null && !isSystemHapticFeedback(mNextVibration.getVibration())) { clearNextVibrationLocked(Vibration.Status.CANCELLED); clearNextVibrationLocked(Vibration.Status.CANCELLED); } } if (mCurrentVibration != null if (shouldCancelOnScreenOffLocked(mCurrentVibration)) { && !isSystemHapticFeedback(mCurrentVibration.getVibration())) { mCurrentVibration.cancel(); mCurrentVibration.cancel(); } } } } Loading Loading @@ -203,9 +195,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { com.android.internal.R.integer.config_previousVibrationsDumpLimit); com.android.internal.R.integer.config_previousVibrationsDumpLimit); mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit); mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit); mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class) .getSystemUiServiceComponent().getPackageName(); mBatteryStatsService = injector.getBatteryStatsService(); mBatteryStatsService = injector.getBatteryStatsService(); mAppOps = mContext.getSystemService(AppOpsManager.class); mAppOps = mContext.getSystemService(AppOpsManager.class); Loading Loading @@ -1074,11 +1063,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { == PackageManager.PERMISSION_GRANTED; == PackageManager.PERMISSION_GRANTED; } } private boolean isSystemHapticFeedback(Vibration vib) { @GuardedBy("mLock") if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) { private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationThread vibrationThread) { if (vibrationThread == null) { return false; 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") @GuardedBy("mLock") Loading services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -18,7 +18,10 @@ package com.android.server.vibrator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; 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.hardware.vibrator.IVibrator; import android.os.Handler; import android.os.Handler; import android.os.VibrationEffect; import android.os.VibrationEffect; Loading @@ -33,8 +36,14 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import com.android.server.LocalServices; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Test; 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.Arrays; import java.util.stream.IntStream; import java.util.stream.IntStream; Loading @@ -59,10 +68,19 @@ public class DeviceVibrationEffectAdapterTest { new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock private PackageManagerInternal mPackageManagerInternalMock; private DeviceVibrationEffectAdapter mAdapter; private DeviceVibrationEffectAdapter mAdapter; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { when(mPackageManagerInternalMock.getSystemUiServiceComponent()) .thenReturn(new ComponentName("", "")); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); VibrationSettings vibrationSettings = new VibrationSettings( VibrationSettings vibrationSettings = new VibrationSettings( InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper())); InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper())); mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings); mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings); Loading services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +7 −0 Original line number Original line Diff line number Diff line Loading @@ -31,8 +31,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.ContextWrapper; import android.content.ContextWrapper; import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.Handler; import android.os.IExternalVibratorService; import android.os.IExternalVibratorService; import android.os.PowerManagerInternal; import android.os.PowerManagerInternal; Loading Loading @@ -76,6 +78,7 @@ public class VibrationScalerTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock private PackageManagerInternal mPackageManagerInternalMock; @Mock private VibrationConfig mVibrationConfigMock; @Mock private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; private TestLooper mTestLooper; Loading @@ -90,7 +93,11 @@ public class VibrationScalerTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); 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.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); Loading services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +63 −11 Original line number Original line Diff line number Diff line Loading @@ -47,13 +47,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.ContextWrapper; import android.content.ContextWrapper; import android.content.Intent; import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.media.AudioManager; import android.media.AudioManager; import android.os.Handler; import android.os.Handler; import android.os.PowerManagerInternal; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.PowerSaveState; import android.os.Process; import android.os.UserHandle; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.VibrationEffect; Loading Loading @@ -87,6 +90,7 @@ import org.mockito.junit.MockitoRule; public class VibrationSettingsTest { public class VibrationSettingsTest { private static final int UID = 1; 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 NORMAL_POWER_STATE = new PowerSaveState.Builder().build(); private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() .setBatterySaverEnabled(true).build(); .setBatterySaverEnabled(true).build(); Loading @@ -104,17 +108,13 @@ public class VibrationSettingsTest { USAGE_TOUCH, USAGE_TOUCH, }; }; @Rule @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock; private VibrationSettings.OnVibratorSettingsChanged mListenerMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock @Mock private PackageManagerInternal mPackageManagerInternalMock; private PowerManagerInternal mPowerManagerInternalMock; @Mock private VibrationConfig mVibrationConfigMock; @Mock private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; private TestLooper mTestLooper; private ContextWrapper mContextSpy; private ContextWrapper mContextSpy; Loading @@ -129,14 +129,17 @@ public class VibrationSettingsTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); doAnswer(invocation -> { doAnswer(invocation -> { mRegisteredPowerModeListener = invocation.getArgument(0); mRegisteredPowerModeListener = invocation.getArgument(0); return null; return null; }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any()); }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any()); when(mPackageManagerInternalMock.getSystemUiServiceComponent()) .thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, "")); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM); setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM); mAudioManager = mContextSpy.getSystemService(AudioManager.class); mAudioManager = mContextSpy.getSystemService(AudioManager.class); Loading Loading @@ -471,6 +474,55 @@ public class VibrationSettingsTest { assertFalse(mVibrationSettings.shouldVibrateInputDevices()); assertFalse(mVibrationSettings.shouldVibrateInputDevices()); } } @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 @Test public void getDefaultIntensity_returnsIntensityFromVibratorConfig() { public void getDefaultIntensity_returnsIntensityFromVibratorConfig() { setDefaultIntensity(VIBRATION_INTENSITY_HIGH); setDefaultIntensity(VIBRATION_INTENSITY_HIGH); Loading Loading
services/core/java/com/android/server/vibrator/VibrationSettings.java +39 −0 Original line number Original line Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.content.res.Resources; import android.content.res.Resources; import android.database.ContentObserver; import android.database.ContentObserver; import android.media.AudioManager; import android.media.AudioManager; Loading @@ -42,6 +43,7 @@ import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationAttributes; Loading Loading @@ -106,6 +108,19 @@ final class VibrationSettings { */ */ private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY; 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. */ /** Listener for changes on vibration settings. */ interface OnVibratorSettingsChanged { interface OnVibratorSettingsChanged { /** Callback triggered when any of the vibrator settings change. */ /** Callback triggered when any of the vibrator settings change. */ Loading @@ -114,6 +129,7 @@ final class VibrationSettings { private final Object mLock = new Object(); private final Object mLock = new Object(); private final Context mContext; private final Context mContext; private final String mSystemUiPackage; private final SettingsObserver mSettingObserver; private final SettingsObserver mSettingObserver; @VisibleForTesting @VisibleForTesting final UidObserver mUidObserver; final UidObserver mUidObserver; Loading Loading @@ -151,6 +167,9 @@ final class VibrationSettings { mUidObserver = new UidObserver(); mUidObserver = new UidObserver(); mUserReceiver = new UserObserver(); mUserReceiver = new UserObserver(); mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class) .getSystemUiServiceComponent().getPackageName(); VibrationEffect clickEffect = createEffectFromResource( VibrationEffect clickEffect = createEffectFromResource( com.android.internal.R.array.config_virtualKeyVibePattern); com.android.internal.R.array.config_virtualKeyVibePattern); VibrationEffect doubleClickEffect = createEffectFromResource( VibrationEffect doubleClickEffect = createEffectFromResource( Loading Loading @@ -343,6 +362,26 @@ final class VibrationSettings { return null; return null; } } /** * 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. * Return {@code true} if the device should vibrate for current ringer mode. * * Loading
services/core/java/com/android/server/vibrator/VibratorManagerService.java +10 −18 Original line number Original line Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.hardware.vibrator.IVibrator; import android.hardware.vibrator.IVibrator; import android.os.BatteryStats; import android.os.BatteryStats; import android.os.Binder; import android.os.Binder; Loading Loading @@ -62,7 +61,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemService; import libcore.util.NativeAllocationRegistry; import libcore.util.NativeAllocationRegistry; Loading Loading @@ -120,7 +118,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private final Object mLock = new Object(); private final Object mLock = new Object(); private final Context mContext; private final Context mContext; private final String mSystemUiPackage; private final PowerManager.WakeLock mWakeLock; private final PowerManager.WakeLock mWakeLock; private final IBatteryStats mBatteryStatsService; private final IBatteryStats mBatteryStatsService; private final Handler mHandler; private final Handler mHandler; Loading Loading @@ -153,17 +150,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { synchronized (mLock) { synchronized (mLock) { // When the system is entering a non-interactive state, we want // When the system is entering a non-interactive state, we want to cancel // to cancel vibrations in case a misbehaving app has abandoned // vibrations in case a misbehaving app has abandoned them. // them. However it may happen that the system is currently playing if (shouldCancelOnScreenOffLocked(mNextVibration)) { // haptic feedback as part of the transition. So we don't cancel // system vibrations. if (mNextVibration != null && !isSystemHapticFeedback(mNextVibration.getVibration())) { clearNextVibrationLocked(Vibration.Status.CANCELLED); clearNextVibrationLocked(Vibration.Status.CANCELLED); } } if (mCurrentVibration != null if (shouldCancelOnScreenOffLocked(mCurrentVibration)) { && !isSystemHapticFeedback(mCurrentVibration.getVibration())) { mCurrentVibration.cancel(); mCurrentVibration.cancel(); } } } } Loading Loading @@ -203,9 +195,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { com.android.internal.R.integer.config_previousVibrationsDumpLimit); com.android.internal.R.integer.config_previousVibrationsDumpLimit); mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit); mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit); mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class) .getSystemUiServiceComponent().getPackageName(); mBatteryStatsService = injector.getBatteryStatsService(); mBatteryStatsService = injector.getBatteryStatsService(); mAppOps = mContext.getSystemService(AppOpsManager.class); mAppOps = mContext.getSystemService(AppOpsManager.class); Loading Loading @@ -1074,11 +1063,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { == PackageManager.PERMISSION_GRANTED; == PackageManager.PERMISSION_GRANTED; } } private boolean isSystemHapticFeedback(Vibration vib) { @GuardedBy("mLock") if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) { private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationThread vibrationThread) { if (vibrationThread == null) { return false; 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") @GuardedBy("mLock") Loading
services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -18,7 +18,10 @@ package com.android.server.vibrator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; 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.hardware.vibrator.IVibrator; import android.os.Handler; import android.os.Handler; import android.os.VibrationEffect; import android.os.VibrationEffect; Loading @@ -33,8 +36,14 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import com.android.server.LocalServices; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Test; 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.Arrays; import java.util.stream.IntStream; import java.util.stream.IntStream; Loading @@ -59,10 +68,19 @@ public class DeviceVibrationEffectAdapterTest { new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock private PackageManagerInternal mPackageManagerInternalMock; private DeviceVibrationEffectAdapter mAdapter; private DeviceVibrationEffectAdapter mAdapter; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { when(mPackageManagerInternalMock.getSystemUiServiceComponent()) .thenReturn(new ComponentName("", "")); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); VibrationSettings vibrationSettings = new VibrationSettings( VibrationSettings vibrationSettings = new VibrationSettings( InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper())); InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper())); mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings); mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings); Loading
services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +7 −0 Original line number Original line Diff line number Diff line Loading @@ -31,8 +31,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.ContextWrapper; import android.content.ContextWrapper; import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.Handler; import android.os.IExternalVibratorService; import android.os.IExternalVibratorService; import android.os.PowerManagerInternal; import android.os.PowerManagerInternal; Loading Loading @@ -76,6 +78,7 @@ public class VibrationScalerTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock private PackageManagerInternal mPackageManagerInternalMock; @Mock private VibrationConfig mVibrationConfigMock; @Mock private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; private TestLooper mTestLooper; Loading @@ -90,7 +93,11 @@ public class VibrationScalerTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); 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.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); Loading
services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +63 −11 Original line number Original line Diff line number Diff line Loading @@ -47,13 +47,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.ContextWrapper; import android.content.ContextWrapper; import android.content.Intent; import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.media.AudioManager; import android.media.AudioManager; import android.os.Handler; import android.os.Handler; import android.os.PowerManagerInternal; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.PowerSaveState; import android.os.Process; import android.os.UserHandle; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.VibrationEffect; Loading Loading @@ -87,6 +90,7 @@ import org.mockito.junit.MockitoRule; public class VibrationSettingsTest { public class VibrationSettingsTest { private static final int UID = 1; 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 NORMAL_POWER_STATE = new PowerSaveState.Builder().build(); private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() .setBatterySaverEnabled(true).build(); .setBatterySaverEnabled(true).build(); Loading @@ -104,17 +108,13 @@ public class VibrationSettingsTest { USAGE_TOUCH, USAGE_TOUCH, }; }; @Rule @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock; private VibrationSettings.OnVibratorSettingsChanged mListenerMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock @Mock private PackageManagerInternal mPackageManagerInternalMock; private PowerManagerInternal mPowerManagerInternalMock; @Mock private VibrationConfig mVibrationConfigMock; @Mock private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; private TestLooper mTestLooper; private ContextWrapper mContextSpy; private ContextWrapper mContextSpy; Loading @@ -129,14 +129,17 @@ public class VibrationSettingsTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); doAnswer(invocation -> { doAnswer(invocation -> { mRegisteredPowerModeListener = invocation.getArgument(0); mRegisteredPowerModeListener = invocation.getArgument(0); return null; return null; }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any()); }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any()); when(mPackageManagerInternalMock.getSystemUiServiceComponent()) .thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, "")); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock); setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM); setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM); mAudioManager = mContextSpy.getSystemService(AudioManager.class); mAudioManager = mContextSpy.getSystemService(AudioManager.class); Loading Loading @@ -471,6 +474,55 @@ public class VibrationSettingsTest { assertFalse(mVibrationSettings.shouldVibrateInputDevices()); assertFalse(mVibrationSettings.shouldVibrateInputDevices()); } } @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 @Test public void getDefaultIntensity_returnsIntensityFromVibratorConfig() { public void getDefaultIntensity_returnsIntensityFromVibratorConfig() { setDefaultIntensity(VIBRATION_INTENSITY_HIGH); setDefaultIntensity(VIBRATION_INTENSITY_HIGH); Loading