Register VibrationSettings as a listener to:
1) virtual displays creation and removal.
2) App Uids running on virtual displays.
These will be used in combination to ignore vibration initiated from a virtual display.
This change also consolicates the fix to avoid Null reference when VirtualDeviceManger
is not present in the available system services. See also ag/19744341.
Test: VibrationSettingsTest, VibratorManagerServiceTest
bug: 189474679
Change-Id: Ie10dbfe65bb3ab9d62ed5c7b5ed7095a51bccf24
Merged-In: Ie10dbfe65bb3ab9d62ed5c7b5ed7095a51bccf24
(cherry picked from commit a0ae5c195186971e0b581451a3da856645a1fd6d)
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index a0d6ce1..fb9752f 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -30,7 +30,7 @@
boolean unregisterVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
in CombinedVibration vibration, in VibrationAttributes attributes);
- void vibrate(int uid, String opPkg, in CombinedVibration vibration,
+ void vibrate(int uid, int displayId, String opPkg, in CombinedVibration vibration,
in VibrationAttributes attributes, String reason, IBinder token);
void cancelVibrate(int usageFilter, IBinder token);
}
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index c690df2..eb2a712 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -137,7 +137,8 @@
return;
}
try {
- mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
+ mService.vibrate(uid, mContext.getAssociatedDisplayId(), opPkg, effect, attributes,
+ reason, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 25a1f68..c211a5e 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -127,6 +127,7 @@
IGNORED_FOR_RINGER_MODE = 23;
IGNORED_FOR_SETTINGS = 24;
IGNORED_SUPERSEDED = 25;
+ IGNORED_FROM_VIRTUAL_DEVICE = 26;
}
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index a375d0a..83caa0e 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -71,7 +71,8 @@
IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER),
IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE),
IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS),
- IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED);
+ IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED),
+ IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE);
private final int mProtoEnumValue;
@@ -87,6 +88,7 @@
public final VibrationAttributes attrs;
public final long id;
public final int uid;
+ public final int displayId;
public final String opPkg;
public final String reason;
public final IBinder token;
@@ -113,12 +115,13 @@
private final CountDownLatch mCompletionLatch = new CountDownLatch(1);
Vibration(IBinder token, int id, CombinedVibration effect,
- VibrationAttributes attrs, int uid, String opPkg, String reason) {
+ VibrationAttributes attrs, int uid, int displayId, String opPkg, String reason) {
this.token = token;
this.mEffect = effect;
this.id = id;
this.attrs = attrs;
this.uid = uid;
+ this.displayId = displayId;
this.opPkg = opPkg;
this.reason = reason;
mStatus = Vibration.Status.RUNNING;
@@ -236,7 +239,7 @@
/** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */
public Vibration.DebugInfo getDebugInfo() {
return new Vibration.DebugInfo(mStatus, mStats, mEffect, mOriginalEffect, /* scale= */ 0,
- attrs, uid, opPkg, reason);
+ attrs, uid, displayId, opPkg, reason);
}
/** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
@@ -304,13 +307,14 @@
private final float mScale;
private final VibrationAttributes mAttrs;
private final int mUid;
+ private final int mDisplayId;
private final String mOpPkg;
private final String mReason;
private final Status mStatus;
DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration effect,
@Nullable CombinedVibration originalEffect, float scale, VibrationAttributes attrs,
- int uid, String opPkg, String reason) {
+ int uid, int displayId, String opPkg, String reason) {
mCreateTime = stats.getCreateTimeDebug();
mStartTime = stats.getStartTimeDebug();
mEndTime = stats.getEndTimeDebug();
@@ -320,6 +324,7 @@
mScale = scale;
mAttrs = attrs;
mUid = uid;
+ mDisplayId = displayId;
mOpPkg = opPkg;
mReason = reason;
mStatus = status;
@@ -349,6 +354,8 @@
.append(mAttrs)
.append(", uid: ")
.append(mUid)
+ .append(", displayId: ")
+ .append(mDisplayId)
.append(", opPkg: ")
.append(mOpPkg)
.append(", reason: ")
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 8e6a290..6012993 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -56,10 +56,12 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -157,6 +159,7 @@
final UidObserver mUidObserver;
@VisibleForTesting
final SettingsBroadcastReceiver mSettingChangeReceiver;
+ final VirtualDeviceListener mVirtualDeviceListener;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -193,6 +196,7 @@
mSettingObserver = new SettingsContentObserver(handler);
mUidObserver = new UidObserver();
mSettingChangeReceiver = new SettingsBroadcastReceiver();
+ mVirtualDeviceListener = new VirtualDeviceListener();
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -257,6 +261,13 @@
}
});
+ VirtualDeviceManagerInternal vdm = LocalServices.getService(
+ VirtualDeviceManagerInternal.class);
+ if (vdm != null) {
+ vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
+ vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
+ }
+
registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
@@ -364,13 +375,17 @@
* null otherwise.
*/
@Nullable
- public Vibration.Status shouldIgnoreVibration(int uid, VibrationAttributes attrs) {
+ public Vibration.Status shouldIgnoreVibration(int uid, int displayId,
+ VibrationAttributes attrs) {
final int usage = attrs.getUsage();
synchronized (mLock) {
if (!mUidObserver.isUidForeground(uid)
&& !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_BACKGROUND;
}
+ if (mVirtualDeviceListener.isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
+ return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+ }
if (mBatterySaverMode && !BATTERY_SAVER_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_FOR_POWER;
@@ -741,4 +756,73 @@
public void onUidProcAdjChanged(int uid) {
}
}
+
+ /**
+ * Implementation of Virtual Device listeners for the changes of virtual displays and of apps
+ * running on any virtual device.
+ */
+ final class VirtualDeviceListener implements
+ VirtualDeviceManagerInternal.VirtualDisplayListener,
+ VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener {
+ @GuardedBy("mLock")
+ private final Set<Integer> mVirtualDisplays = new HashSet<>();
+ @GuardedBy("mLock")
+ private final Set<Integer> mAppsOnVirtualDevice = new HashSet<>();
+
+
+ @Override
+ public void onVirtualDisplayCreated(int displayId) {
+ synchronized (mLock) {
+ mVirtualDisplays.add(displayId);
+ }
+ }
+
+ @Override
+ public void onVirtualDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mVirtualDisplays.remove(displayId);
+ }
+ }
+
+
+ @Override
+ public void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids) {
+ synchronized (mLock) {
+ mAppsOnVirtualDevice.clear();
+ mAppsOnVirtualDevice.addAll(allRunningUids);
+ }
+ }
+
+ /**
+ * @param uid: uid of the calling app.
+ * @param displayId: the id of a Display.
+ * @return Returns true if:
+ * <ul>
+ * <li> the displayId is valid, and it's owned by a virtual device.</li>
+ * <li> the displayId is invalid, and the calling app (uid) is running on a virtual
+ * device.</li>
+ * </ul>
+ */
+ public boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ // The default display is the primary physical display on the phone.
+ return false;
+ }
+
+ synchronized (mLock) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ // There is no Display object associated with the Context of calling
+ // {@link SystemVibratorManager}, checking the calling UID instead.
+ return mAppsOnVirtualDevice.contains(uid);
+ } else {
+ // Other valid display IDs representing valid logical displays will be
+ // checked
+ // against the active virtual displays set built with the registered
+ // {@link VirtualDisplayListener}.
+ return mVirtualDisplays.contains(displayId);
+ }
+ }
+ }
+
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 2f12a82..9727558 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -58,6 +58,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -378,9 +379,9 @@
}
@Override // Binder call
- public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
+ public void vibrate(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
- vibrateInternal(uid, opPkg, effect, attrs, reason, token);
+ vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
}
/**
@@ -389,8 +390,9 @@
*/
@Nullable
@VisibleForTesting
- Vibration vibrateInternal(int uid, String opPkg, @NonNull CombinedVibration effect,
- @Nullable VibrationAttributes attrs, String reason, IBinder token) {
+ Vibration vibrateInternal(int uid, int displayId, String opPkg,
+ @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
+ String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, "vibrate");
@@ -406,7 +408,7 @@
attrs = fixupVibrationAttributes(attrs, effect);
// Create Vibration.Stats as close to the received request as possible, for tracking.
Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
- uid, opPkg, reason);
+ uid, displayId, opPkg, reason);
fillVibrationFallbacks(vib, effect);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
@@ -424,7 +426,7 @@
Vibration.Status status = null;
// Check if user settings or DnD is set to ignore this vibration.
- status = shouldIgnoreVibrationLocked(vib.uid, vib.opPkg, vib.attrs);
+ status = shouldIgnoreVibrationLocked(vib.uid, vib.displayId, vib.opPkg, vib.attrs);
// Check if something has external control, assume it's more important.
if ((status == null) && (mCurrentExternalVibration != null)) {
@@ -629,7 +631,7 @@
Vibration vib = mCurrentVibration.getVibration();
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.uid, vib.opPkg, vib.attrs);
+ vib.uid, vib.displayId, vib.opPkg, vib.attrs);
if (inputDevicesChanged || (ignoreStatus != null)) {
if (DEBUG) {
@@ -659,7 +661,7 @@
continue;
}
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.uid, vib.opPkg, vib.attrs);
+ vib.uid, Display.DEFAULT_DISPLAY, vib.opPkg, vib.attrs);
if (ignoreStatus == null) {
effect = mVibrationScaler.scale(effect, vib.attrs.getUsage());
} else {
@@ -780,6 +782,12 @@
+ attrs);
}
break;
+ case IGNORED_FROM_VIRTUAL_DEVICE:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration because it came from a virtual"
+ + " device, attrs= " + attrs);
+ }
+ break;
default:
if (DEBUG) {
Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
@@ -894,9 +902,10 @@
*/
@GuardedBy("mLock")
@Nullable
- private Vibration.Status shouldIgnoreVibrationLocked(int uid, String opPkg,
+ private Vibration.Status shouldIgnoreVibrationLocked(int uid, int displayId, String opPkg,
VibrationAttributes attrs) {
- Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(uid, attrs);
+ Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(uid,
+ displayId, attrs);
if (statusFromSettings != null) {
return statusFromSettings;
}
@@ -1442,6 +1451,9 @@
return new Vibration.DebugInfo(
mStatus, stats, /* effect= */ null, /* originalEffect= */ null, scale,
externalVibration.getVibrationAttributes(), externalVibration.getUid(),
+ // TODO(b/243604888): propagating displayID from IExternalVibration instead of
+ // using INVALID_DISPLAY for all external vibrations.
+ Display.INVALID_DISPLAY,
externalVibration.getPackage(), /* reason= */ null);
}
@@ -1647,8 +1659,10 @@
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
synchronized (mLock) {
+ // TODO(b/243604888): propagating displayID from IExternalVibration instead of
+ // using INVALID_DISPLAY for all external vibrations.
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.getUid(), vib.getPackage(), attrs);
+ vib.getUid(), Display.INVALID_DISPLAY, vib.getPackage(), attrs);
if (ignoreStatus != null) {
vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
// Failed to start the vibration, end it and report metrics right away.
@@ -1840,8 +1854,8 @@
// only cancel background vibrations.
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
- Vibration vib = vibrateInternal(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combined,
- attrs, commonOptions.description, deathBinder);
+ Vibration vib = vibrateInternal(Binder.getCallingUid(), Display.DEFAULT_DISPLAY,
+ SHELL_PACKAGE_NAME, combined, attrs, commonOptions.description, deathBinder);
if (vib != null && !commonOptions.background) {
try {
vib.waitForEnd();
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 4ef6530..aafbb11 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -66,12 +66,15 @@
import android.os.vibrator.VibrationConfig;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
+import android.util.ArraySet;
+import android.view.Display;
import androidx.test.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -95,6 +98,7 @@
public class VibrationSettingsTest {
private static final int UID = 1;
+ private static final int VIRTUAL_DISPLAY_ID = 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()
@@ -117,15 +121,23 @@
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
- @Mock private PowerManagerInternal mPowerManagerInternalMock;
- @Mock private PackageManagerInternal mPackageManagerInternalMock;
- @Mock private VibrationConfig mVibrationConfigMock;
+ @Mock
+ private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternalMock;
+ @Mock
+ private VibrationConfig mVibrationConfigMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
private AudioManager mAudioManager;
private VibrationSettings mVibrationSettings;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
+ private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
+ private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
+ mRegisteredAppsOnVirtualDeviceListener;
@Before
public void setUp() throws Exception {
@@ -140,11 +152,17 @@
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));
+ doAnswer(invocation -> {
+ mRegisteredVirtualDisplayListener = invocation.getArgument(0);
+ return null;
+ }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
+ doAnswer(invocation -> {
+ mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
+ return null;
+ }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
- LocalServices.removeServiceForTest(PowerManagerInternal.class);
- LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
+ removeServicesForTest();
+ addServicesForTest();
setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
mAudioManager = mContextSpy.getSystemService(AudioManager.class);
@@ -168,6 +186,18 @@
}
@Test
+ public void create_withOnlyRequiredSystemServices() {
+ // The only core services that we depend on are PowerManager and PackageManager
+ removeServicesForTest();
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
+
+ VibrationSettings minimalVibrationSettings = new VibrationSettings(mContextSpy,
+ new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
+ minimalVibrationSettings.onSystemReady();
+ }
+
+ @Test
public void addListener_settingsChangeTriggerListener() {
mVibrationSettings.addListener(mListenerMock);
@@ -447,6 +477,65 @@
}
@Test
+ public void shouldIgnoreVibrationFromVirtualDisplays_displayNonVirtual_neverIgnored() {
+ // Vibrations from the primary display is never ignored regardless of the creation and
+ // removal of virtual displays and of the changes of apps running on virtual displays.
+ mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
+ mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
+ new ArraySet<>(Arrays.asList(UID)));
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
+ }
+
+ mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
+ }
+
+ mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
+ }
+ }
+
+ @Test
+ public void shouldIgnoreVibrationFromVirtualDisplays_displayVirtual() {
+ // Ignore the vibration when the coming display id represents a virtual display.
+ mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
+
+ for (int usage : ALL_USAGES) {
+ assertVibrationIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID,
+ Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
+ }
+
+ // Stop ignoring when the virtual display is removed.
+ mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID);
+ }
+ }
+
+
+ @Test
+ public void shouldIgnoreVibrationFromVirtualDisplays_appsOnVirtualDisplay() {
+ // Ignore when the passed-in display id is invalid and the calling uid is on a virtual
+ // display.
+ mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
+ new ArraySet<>(Arrays.asList(UID)));
+ for (int usage : ALL_USAGES) {
+ assertVibrationIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY,
+ Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
+ }
+
+ // Stop ignoring when the app is no longer on virtual display.
+ mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY);
+ }
+
+ }
+
+ @Test
public void shouldVibrateInputDevices_returnsSettingsValue() {
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
assertTrue(mVibrationSettings.shouldVibrateInputDevices());
@@ -479,7 +568,7 @@
@Test
public void shouldCancelVibrationOnScreenOff_withSleepReasonInAllowlist_returnsAlwaysFalse() {
long vibrateStartTime = 100;
- int[] allowedSleepReasons = new int[] {
+ int[] allowedSleepReasons = new int[]{
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
};
@@ -646,11 +735,29 @@
assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_DOUBLE_CLICK));
}
+ private void removeServicesForTest() {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ }
+
+ private void addServicesForTest() {
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
+ LocalServices.addService(VirtualDeviceManagerInternal.class,
+ mVirtualDeviceManagerInternalMock);
+ }
+
private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
Vibration.Status expectedStatus) {
+ assertVibrationIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY, expectedStatus);
+ }
+
+ private void assertVibrationIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
+ int displayId, Vibration.Status expectedStatus) {
assertEquals(errorMessageForUsage(usage),
expectedStatus,
- mVibrationSettings.shouldIgnoreVibration(UID,
+ mVibrationSettings.shouldIgnoreVibration(UID, displayId,
VibrationAttributes.createForUsage(usage)));
}
@@ -660,8 +767,20 @@
private void assertVibrationNotIgnoredForUsageAndFlags(@VibrationAttributes.Usage int usage,
@VibrationAttributes.Flag int flags) {
+ assertVibrationNotIgnoredForUsageAndFlagsAndDidsplay(usage, Display.DEFAULT_DISPLAY, flags);
+ }
+
+ private void assertVibrationNotIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
+ int displayId) {
+ assertVibrationNotIgnoredForUsageAndFlagsAndDidsplay(usage, displayId, /* flags= */ 0);
+ }
+
+ private void assertVibrationNotIgnoredForUsageAndFlagsAndDidsplay(
+ @VibrationAttributes.Usage int usage, int displayId,
+ @VibrationAttributes.Flag int flags) {
assertNull(errorMessageForUsage(usage),
mVibrationSettings.shouldIgnoreVibration(UID,
+ displayId,
new VibrationAttributes.Builder()
.setUsage(usage)
.setFlags(flags)
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 ca162ef..0551bfc 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -94,6 +94,7 @@
private static final int TEST_TIMEOUT_MILLIS = 900;
private static final int UID = Process.ROOT_UID;
+ private static final int DISPLAY_ID = 10;
private static final int VIBRATOR_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
@@ -1584,7 +1585,8 @@
}
private Vibration createVibration(long id, CombinedVibration effect) {
- return new Vibration(mVibrationToken, (int) id, effect, ATTRS, UID, PACKAGE_NAME, "reason");
+ return new Vibration(mVibrationToken, (int) id, effect, ATTRS, UID, DISPLAY_ID,
+ PACKAGE_NAME, "reason");
}
private SparseArray<VibratorController> createVibratorControllers() {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 36bec75..fe0a79c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -76,7 +76,9 @@
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.SparseBooleanArray;
+import android.view.Display;
import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
@@ -86,6 +88,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -115,6 +118,7 @@
private static final int TEST_TIMEOUT_MILLIS = 1_000;
private static final int UID = Process.ROOT_UID;
+ private static final int VIRTUAL_DISPLAY_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
@@ -153,6 +157,8 @@
private IBatteryStats mBatteryStatsMock;
@Mock
private VibratorFrameworkStatsLogger mVibratorFrameworkStatsLoggerMock;
+ @Mock
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -162,6 +168,9 @@
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
private VibrationConfig mVibrationConfig;
+ private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
+ private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
+ mRegisteredAppsOnVirtualDeviceListener;
@Before
public void setUp() throws Exception {
@@ -186,6 +195,14 @@
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
+ doAnswer(invocation -> {
+ mRegisteredVirtualDisplayListener = invocation.getArgument(0);
+ return null;
+ }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
+ doAnswer(invocation -> {
+ mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
+ return null;
+ }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
@@ -202,6 +219,7 @@
addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
+ addLocalServiceMock(VirtualDeviceManagerInternal.class, mVirtualDeviceManagerInternalMock);
mTestLooper.startAutoDispatch();
}
@@ -1167,6 +1185,64 @@
}
@Test
+ public void vibrate_withVitualDisplayChange_ignoreVibrationFromVirtualDisplay()
+ throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+ mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
+
+ vibrateWithDisplay(service,
+ VIRTUAL_DISPLAY_ID,
+ CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
+ .combine(),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // Haptic feedback ignored when it's from a virtual display.
+ assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
+
+ mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
+ vibrateWithDisplay(service,
+ VIRTUAL_DISPLAY_ID,
+ CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
+ .combine(),
+ HAPTIC_FEEDBACK_ATTRS);
+ // Haptic feedback played normally when the virtual display is removed.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ }
+
+ @Test
+ public void vibrate_withAppsOnVitualDisplayChange_ignoreVibrationFromVirtualDisplay()
+ throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+ mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
+ new ArraySet<>(Arrays.asList(UID)));
+ vibrateWithDisplay(service,
+ Display.INVALID_DISPLAY,
+ CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
+ .combine(),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // Haptic feedback ignored when it's from an app running virtual display.
+ assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
+
+ mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
+ vibrateWithDisplay(service,
+ Display.INVALID_DISPLAY,
+ CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
+ .combine(),
+ HAPTIC_FEEDBACK_ATTRS);
+ // Haptic feedback played normally when the same app no long runs on a virtual display.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ }
+
+ @Test
public void cancelVibrate_withoutUsageFilter_stopsVibrating() throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -1240,6 +1316,24 @@
}
@Test
+ public void onExternalVibration_ignoreVibrationFromVirtualDevices() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ createSystemReadyService();
+
+ IBinder binderToken = mock(IBinder.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
+ mock(IExternalVibrationController.class), binderToken);
+ int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+
+ mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
+ new ArraySet<>(Arrays.asList(UID)));
+ scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ }
+
+ @Test
public void onExternalVibration_setsExternalControl() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -1820,7 +1914,8 @@
private void vibrateAndWaitUntilFinished(VibratorManagerService service,
CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
Vibration vib =
- service.vibrateInternal(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
+ service.vibrateInternal(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME, effect, attrs,
+ "some reason", service);
if (vib != null) {
vib.waitForEnd();
}
@@ -1833,7 +1928,12 @@
private void vibrate(VibratorManagerService service, CombinedVibration effect,
VibrationAttributes attrs) {
- service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
+ vibrateWithDisplay(service, Display.DEFAULT_DISPLAY, effect, attrs);
+ }
+
+ private void vibrateWithDisplay(VibratorManagerService service, int displayId,
+ CombinedVibration effect, VibrationAttributes attrs) {
+ service.vibrate(UID, displayId, PACKAGE_NAME, effect, attrs, "some reason", service);
}
private boolean waitUntil(Predicate<VibratorManagerService> predicate,
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index e0f3f03..421ceb7 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -50,6 +50,7 @@
public class VibratorManagerServicePermissionTest {
private static final String PACKAGE_NAME = "com.android.framework.permission.tests";
+ private static final int DISPLAY_ID = 1;
private static final CombinedVibration EFFECT =
CombinedVibration.createParallel(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -106,7 +107,8 @@
@Test
public void testVibrateWithoutPermissionFails() throws RemoteException {
expectSecurityException("VIBRATE");
- mVibratorService.vibrate(Process.myUid(), PACKAGE_NAME, EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ "testVibrate",
new Binder());
}
@@ -115,7 +117,8 @@
throws RemoteException {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.myUid(), PACKAGE_NAME, EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ "testVibrate",
new Binder());
}
@@ -124,7 +127,8 @@
expectSecurityException("UPDATE_APP_OPS_STATS");
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.SYSTEM_UID, "android", EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ "testVibrate",
new Binder());
}
@@ -133,7 +137,8 @@
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE,
Manifest.permission.UPDATE_APP_OPS_STATS);
- mVibratorService.vibrate(Process.SYSTEM_UID, "android", EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ "testVibrate",
new Binder());
}