diff options
author | 2022-07-20 10:58:43 +0000 | |
---|---|---|
committer | 2022-08-27 16:38:54 +0000 | |
commit | a0ae5c195186971e0b581451a3da856645a1fd6d (patch) | |
tree | c93d5528dce1ced360326f207d710a2adb5e5a05 | |
parent | cd9be246fdd343318e5d906238abedb17e0cdc41 (diff) |
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.
Test: VibrationSettingsTest, VibratorManagerServiceTest
bug: 189474679
Change-Id: Ie10dbfe65bb3ab9d62ed5c7b5ed7095a51bccf24
10 files changed, 342 insertions, 32 deletions
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl index a0d6ce1ba108..fb9752f5626e 100644 --- a/core/java/android/os/IVibratorManagerService.aidl +++ b/core/java/android/os/IVibratorManagerService.aidl @@ -30,7 +30,7 @@ interface IVibratorManagerService { 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 c690df2e3d31..eb2a712c8575 100644 --- a/core/java/android/os/SystemVibratorManager.java +++ b/core/java/android/os/SystemVibratorManager.java @@ -137,7 +137,8 @@ public class SystemVibratorManager extends VibratorManager { 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 25a1f68a0afe..c211a5edf078 100644 --- a/core/proto/android/server/vibrator/vibratormanagerservice.proto +++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto @@ -127,6 +127,7 @@ message VibrationProto { 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 a375d0aceb54..83caa0e0b366 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 @@ final class Vibration { 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 @@ final class Vibration { 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 @@ final class Vibration { 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 @@ final class Vibration { /** 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 @@ final class Vibration { 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 @@ final class Vibration { mScale = scale; mAttrs = attrs; mUid = uid; + mDisplayId = displayId; mOpPkg = opPkg; mReason = reason; mStatus = status; @@ -349,6 +354,8 @@ final class Vibration { .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 8e6a290c095f..d7bfd1deca90 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.Slog; 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 class VibrationSettings { final UidObserver mUidObserver; @VisibleForTesting final SettingsBroadcastReceiver mSettingChangeReceiver; + final VirtualDeviceListener mVirtualDeviceListener; @GuardedBy("mLock") private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); @@ -193,6 +196,7 @@ final class VibrationSettings { mSettingObserver = new SettingsContentObserver(handler); mUidObserver = new UidObserver(); mSettingChangeReceiver = new SettingsBroadcastReceiver(); + mVirtualDeviceListener = new VirtualDeviceListener(); mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class) .getSystemUiServiceComponent().getPackageName(); @@ -220,6 +224,8 @@ final class VibrationSettings { public void onSystemReady() { PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); + VirtualDeviceManagerInternal vdm = LocalServices.getService( + VirtualDeviceManagerInternal.class); AudioManager am = mContext.getSystemService(AudioManager.class); int ringerMode = am.getRingerModeInternal(); @@ -257,6 +263,9 @@ final class VibrationSettings { } }); + vdm.registerVirtualDisplayListener(mVirtualDeviceListener); + vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener); + registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER); registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER); @@ -364,13 +373,17 @@ final class VibrationSettings { * 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 +754,71 @@ final class VibrationSettings { 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: + * 1) the displayId is valid, and it's owned by a virtual device + * 2) the displayId is invalid, and the calling app (uid) is running on a virtual device. + */ + public boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) { + synchronized (mLock) { + switch (displayId) { + case Display.DEFAULT_DISPLAY: + // The default display is the primary physical display on the phone. + return false; + case 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); + default: + // 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 d1cde602b391..01595724866f 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.text.TextUtils; 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { */ @VisibleForTesting @Nullable - 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { + 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { */ @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // 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 { // Waits for the client vibration to finish, but the VibrationThread may still 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 4ef653032299..99a12c2c4ff3 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.test.TestLooper; 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 @@ import java.util.Set; 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 @@ public class VibrationSettingsTest { @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,22 @@ public class VibrationSettingsTest { }).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); + LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class); + LocalServices.addService(VirtualDeviceManagerInternal.class, + mVirtualDeviceManagerInternalMock); setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM); mAudioManager = mContextSpy.getSystemService(AudioManager.class); @@ -447,6 +470,65 @@ public class VibrationSettingsTest { } @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 +561,7 @@ public class VibrationSettingsTest { @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, }; @@ -648,9 +730,14 @@ public class VibrationSettingsTest { 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,14 +747,27 @@ public class VibrationSettingsTest { 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) .build())); } + private String errorMessageForUsage(int usage) { return "Error for usage " + VibrationAttributes.usageToString(usage); } 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 efc240d3f172..a15e4b0c74a0 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 @@ public class VibrationThreadTest { 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(); @@ -1625,7 +1626,8 @@ public class VibrationThreadTest { } 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 1a8df719c223..8b512e9943a4 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -78,7 +78,9 @@ import android.os.vibrator.VibrationConfig; 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; @@ -88,6 +90,7 @@ import com.android.internal.util.FrameworkStatsLog; 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; @@ -118,6 +121,7 @@ public class VibratorManagerServiceTest { 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() @@ -156,6 +160,8 @@ public class VibratorManagerServiceTest { private IBatteryStats mBatteryStatsMock; @Mock private VibratorFrameworkStatsLogger mVibratorFrameworkStatsLoggerMock; + @Mock + private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock; private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); @@ -165,6 +171,9 @@ public class VibratorManagerServiceTest { 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 { @@ -189,6 +198,14 @@ public class VibratorManagerServiceTest { 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); @@ -205,6 +222,7 @@ public class VibratorManagerServiceTest { addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock); addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock); + addLocalServiceMock(VirtualDeviceManagerInternal.class, mVirtualDeviceManagerInternalMock); mTestLooper.startAutoDispatch(); } @@ -1144,6 +1162,64 @@ public class VibratorManagerServiceTest { } @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 vibrate_prebakedAndComposedVibrationsWithFallbacks_playsFallbackOnlyForPredefined() throws Exception { mockVibrators(1); @@ -1260,6 +1336,24 @@ public class VibratorManagerServiceTest { } @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); @@ -1843,7 +1937,8 @@ public class VibratorManagerServiceTest { 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(); } @@ -1856,7 +1951,12 @@ public class VibratorManagerServiceTest { 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 e0f3f03e9cb7..421ceb797c15 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 @@ import org.junit.runners.JUnit4; 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 @@ public class VibratorManagerServicePermissionTest { @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 @@ public class VibratorManagerServicePermissionTest { 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 @@ public class VibratorManagerServicePermissionTest { 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 @@ public class VibratorManagerServicePermissionTest { 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()); } |