summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2022-03-01 12:46:31 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-03-01 12:46:31 +0000
commitff122ff053ba3400fd820bd6b562a8a19e381ec1 (patch)
treea67e8a55c9333dd1c429cbf119b9422ed4a04873
parent1b512780460f2f6ccac2785cfb82eaa4ee0b4ee7 (diff)
parent8d2117d1078b7a1b745a43478135b41ca7ef0010 (diff)
Merge "Enable system vibrations on screen off for other feedbacks" into tm-dev
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java39
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java25
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()),