Add listeners for the changes of App UIDs running on virtual displays.

This will be used later by VibrationSettings to keep record of apps
currently on virtual displays.

Test: VirtualDeviceManagerServiceTest

bug: 189474679
Change-Id: I120f8f8a1d3d0d8c72bf3ff42a6661147e474ccb
(cherry picked from commit 0880191a03)
This commit is contained in:
Zixuan Qu
2022-07-30 11:26:57 +00:00
parent c9e49d8ed2
commit fa3d822f87
3 changed files with 159 additions and 12 deletions

View File

@@ -41,6 +41,7 @@ import android.os.Looper;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -86,6 +87,12 @@ public class VirtualDeviceManagerService extends SystemService {
@GuardedBy("mVirtualDeviceManagerLock")
private final SparseArray<VirtualDeviceImpl> mVirtualDevices = new SparseArray<>();
/**
* Mapping from CDM association IDs to app UIDs running on the corresponding virtual device.
*/
@GuardedBy("mVirtualDeviceManagerLock")
private final SparseArray<ArraySet<Integer>> mAppsOnVirtualDevices = new SparseArray<>();
/**
* Mapping from user ID to CDM associations. The associations come from
* {@link CompanionDeviceManager#getAllAssociations()}, which contains associations across all
@@ -213,6 +220,14 @@ public class VirtualDeviceManagerService extends SystemService {
return mLocalService;
}
@VisibleForTesting
void notifyRunningAppsChanged(int associationId, ArraySet<Integer> uids) {
synchronized (mVirtualDeviceManagerLock) {
mAppsOnVirtualDevices.put(associationId, uids);
}
mLocalService.onAppsOnVirtualDeviceChanged();
}
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
VirtualDeviceImpl.PendingTrampolineCallback {
@@ -252,6 +267,7 @@ public class VirtualDeviceManagerService extends SystemService {
public void onClose(int associationId) {
synchronized (mVirtualDeviceManagerLock) {
mVirtualDevices.remove(associationId);
mAppsOnVirtualDevices.remove(associationId);
if (cameraAccessController != null) {
cameraAccessController.stopObservingIfNeeded();
} else {
@@ -262,8 +278,10 @@ public class VirtualDeviceManagerService extends SystemService {
}
},
this, activityListener,
runningUids -> cameraAccessController.blockCameraAccessIfNeeded(
runningUids),
runningUids -> {
cameraAccessController.blockCameraAccessIfNeeded(runningUids);
notifyRunningAppsChanged(associationInfo.getId(), runningUids);
},
params);
if (cameraAccessController != null) {
cameraAccessController.startObservingIfNeeded();
@@ -385,8 +403,13 @@ public class VirtualDeviceManagerService extends SystemService {
private final class LocalService extends VirtualDeviceManagerInternal {
@GuardedBy("mVirtualDeviceManagerLock")
private final ArrayList<VirtualDeviceManagerInternal.VirtualDisplayListener>
private final ArrayList<VirtualDisplayListener>
mVirtualDisplayListeners = new ArrayList<>();
@GuardedBy("mVirtualDeviceManagerLock")
private final ArrayList<AppsOnVirtualDeviceListener>
mAppsOnVirtualDeviceListeners = new ArrayList<>();
@GuardedBy("mVirtualDeviceManagerLock")
private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>();
@Override
public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
@@ -422,6 +445,34 @@ public class VirtualDeviceManagerService extends SystemService {
});
}
@Override
public void onAppsOnVirtualDeviceChanged() {
ArraySet<Integer> latestRunningUids = new ArraySet<>();
final AppsOnVirtualDeviceListener[] listeners;
synchronized (mVirtualDeviceManagerLock) {
int size = mAppsOnVirtualDevices.size();
for (int i = 0; i < size; i++) {
latestRunningUids.addAll(mAppsOnVirtualDevices.valueAt(i));
}
if (!mAllUidsOnVirtualDevice.equals(latestRunningUids)) {
mAllUidsOnVirtualDevice.clear();
mAllUidsOnVirtualDevice.addAll(latestRunningUids);
listeners =
mAppsOnVirtualDeviceListeners.toArray(
new AppsOnVirtualDeviceListener[0]);
} else {
listeners = null;
}
}
if (listeners != null) {
mHandler.post(() -> {
for (AppsOnVirtualDeviceListener listener : listeners) {
listener.onAppsOnAnyVirtualDeviceChanged(latestRunningUids);
}
});
}
}
@Override
public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) {
return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags();
@@ -481,6 +532,22 @@ public class VirtualDeviceManagerService extends SystemService {
mVirtualDisplayListeners.remove(listener);
}
}
@Override
public void registerAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener) {
synchronized (mVirtualDeviceManagerLock) {
mAppsOnVirtualDeviceListeners.add(listener);
}
}
@Override
public void unregisterAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener) {
synchronized (mVirtualDeviceManagerLock) {
mAppsOnVirtualDeviceListeners.remove(listener);
}
}
}
private static final class PendingTrampolineMap {

View File

@@ -19,6 +19,8 @@ package com.android.server.companion.virtual;
import android.annotation.NonNull;
import android.companion.virtual.IVirtualDevice;
import java.util.Set;
/**
* Virtual device manager local service interface.
* Only for use within system server.
@@ -34,14 +36,35 @@ public abstract class VirtualDeviceManagerInternal {
void onVirtualDisplayRemoved(int displayId);
}
/** Interface to listen to the changes on the list of app UIDs running on any virtual device. */
public interface AppsOnVirtualDeviceListener {
/** Notifies that running apps on any virtual device has changed */
void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids);
}
/** Register a listener for the creation and destruction of virtual displays. */
public abstract void registerVirtualDisplayListener(
@NonNull VirtualDisplayListener listener);
/** Unregister a listener for the creation and destruction of virtual displays. */
/** Unregister a listener for the creation and destruction of virtual displays. */
public abstract void unregisterVirtualDisplayListener(
@NonNull VirtualDisplayListener listener);
/** Register a listener for changes of running app UIDs on any virtual device. */
public abstract void registerAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener);
/** Unregister a listener for changes of running app UIDs on any virtual device. */
public abstract void unregisterAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener);
/**
* Notifies that the set of apps running on virtual devices has changed.
* This method only notifies the listeners when the union of running UIDs on all virtual devices
* has changed.
*/
public abstract void onAppsOnVirtualDeviceChanged();
/**
* Validate the virtual device.

View File

@@ -35,6 +35,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -110,6 +111,12 @@ public class VirtualDeviceManagerServiceTest {
private static final String GOOGLE_MAPS_PACKAGE_NAME = "com.google.android.apps.maps";
private static final String DEVICE_NAME = "device name";
private static final int DISPLAY_ID = 2;
private static final int UID_1 = 0;
private static final int UID_2 = 10;
private static final int UID_3 = 10000;
private static final int UID_4 = 10001;
private static final int ASSOCIATION_ID_1 = 1;
private static final int ASSOCIATION_ID_2 = 2;
private static final int PRODUCT_ID = 10;
private static final int VENDOR_ID = 5;
private static final String UNIQUE_ID = "uniqueid";
@@ -143,6 +150,8 @@ public class VirtualDeviceManagerServiceTest {
@Mock
private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
@Mock
private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mAppsOnVirtualDeviceListener;
@Mock
IPowerManager mIPowerManagerMock;
@Mock
IThermalService mIThermalServiceMock;
@@ -242,7 +251,7 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
public void onVirtualDisplayCreatedLocked_listenersNotified() throws RemoteException {
public void onVirtualDisplayCreatedLocked_listenersNotified() {
mLocalService.registerVirtualDisplayListener(mDisplayListener);
mLocalService.onVirtualDisplayCreated(DISPLAY_ID);
@@ -252,7 +261,7 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
public void onVirtualDisplayRemovedLocked_listenersNotified() throws RemoteException {
public void onVirtualDisplayRemovedLocked_listenersNotified() {
mLocalService.registerVirtualDisplayListener(mDisplayListener);
mDeviceImpl.onVirtualDisplayCreatedLocked(
mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
@@ -263,6 +272,54 @@ public class VirtualDeviceManagerServiceTest {
verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID);
}
@Test
public void onAppsOnVirtualDeviceChanged_singleVirtualDevice_listenersNotified() {
ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uids);
TestableLooper.get(this).processAllMessages();
verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(uids);
}
@Test
public void onAppsOnVirtualDeviceChanged_multipleVirtualDevices_listenersNotified() {
ArraySet<Integer> uidsOnDevice1 = new ArraySet<>(Arrays.asList(UID_1, UID_2));
ArraySet<Integer> uidsOnDevice2 = new ArraySet<>(Arrays.asList(UID_3, UID_4));
mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
// Notifies that the running apps on the first virtual device has changed.
mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uidsOnDevice1);
TestableLooper.get(this).processAllMessages();
verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(
new ArraySet<>(Arrays.asList(UID_1, UID_2)));
// Notifies that the running apps on the second virtual device has changed.
mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_2, uidsOnDevice2);
TestableLooper.get(this).processAllMessages();
// The union of the apps running on both virtual devices are sent to the listeners.
verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(
new ArraySet<>(Arrays.asList(UID_1, UID_2, UID_3, UID_4)));
// Notifies that the running apps on the first virtual device has changed again.
uidsOnDevice1.remove(UID_2);
mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uidsOnDevice1);
mLocalService.onAppsOnVirtualDeviceChanged();
TestableLooper.get(this).processAllMessages();
// The union of the apps running on both virtual devices are sent to the listeners.
verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(
new ArraySet<>(Arrays.asList(UID_1, UID_3, UID_4)));
// Notifies that the running apps on the first virtual device has changed but with the same
// set of UIDs.
mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uidsOnDevice1);
mLocalService.onAppsOnVirtualDeviceChanged();
TestableLooper.get(this).processAllMessages();
// Listeners should not be notified.
verifyNoMoreInteractions(mAppsOnVirtualDeviceListener);
}
@Test
public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
@@ -270,7 +327,7 @@ public class VirtualDeviceManagerServiceTest {
nullable(String.class), anyInt(), eq(null));
mDeviceImpl.onVirtualDisplayCreatedLocked(
mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
nullable(String.class), eq(DISPLAY_ID), eq(null));
}
@@ -284,7 +341,7 @@ public class VirtualDeviceManagerServiceTest {
assertThrows(IllegalStateException.class,
() -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID));
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
nullable(String.class), eq(DISPLAY_ID), eq(null));
}
@@ -302,14 +359,14 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
nullable(String.class), eq(DISPLAY_ID), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
verify(mIPowerManagerMock, Mockito.times(1)).releaseWakeLock(eq(wakeLock), anyInt());
verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt());
}
@Test
@@ -318,7 +375,7 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
nullable(String.class), eq(DISPLAY_ID), eq(null));
@@ -326,7 +383,7 @@ public class VirtualDeviceManagerServiceTest {
// Close the VirtualDevice without first notifying it of the VirtualDisplay removal.
mDeviceImpl.close();
verify(mIPowerManagerMock, Mockito.times(1)).releaseWakeLock(eq(wakeLock), anyInt());
verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt());
}
@Test