Merge "Add VDM onSecureWindowHidden API" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e766768..7bfa878 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3543,6 +3543,7 @@
public static interface VirtualDeviceManager.ActivityListener {
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onActivityLaunchBlocked(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.content.IntentSender);
method public void onDisplayEmpty(int);
+ method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onSecureWindowHidden(int);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onSecureWindowShown(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method @Deprecated public void onTopActivityChanged(int, @NonNull android.content.ComponentName);
method public default void onTopActivityChanged(int, @NonNull android.content.ComponentName, int);
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
index 767f52a..448793d 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -63,4 +63,11 @@
* @param user The user associated with the activity.
*/
void onSecureWindowShown(int displayId, in ComponentName componentName, in UserHandle user);
+
+ /**
+ * Called when a secure surface is no longer shown on the device.
+ *
+ * @param displayId The display ID on which the secure surface was shown.
+ */
+ void onSecureWindowHidden(int displayId);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index d63a443..42c7441 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -166,6 +166,20 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void onSecureWindowHidden(int displayId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mActivityListenersLock) {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i).onSecureWindowHidden(displayId);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
private final IVirtualDeviceSoundEffectListener mSoundEffectListener =
@@ -617,6 +631,10 @@
mExecutor.execute(() ->
mActivityListener.onSecureWindowShown(displayId, componentName, user));
}
+
+ public void onSecureWindowHidden(int displayId) {
+ mExecutor.execute(() -> mActivityListener.onSecureWindowHidden(displayId));
+ }
}
/**
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 6ea7834..b3f09a9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1288,6 +1288,17 @@
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
default void onSecureWindowShown(int displayId, @NonNull ComponentName componentName,
@NonNull UserHandle user) {}
+
+ /**
+ * Called when a window with a secure surface is no longer shown on the device.
+ *
+ * @param displayId The display ID on which the window was shown before.
+ *
+ * @see Display#FLAG_SECURE
+ * @see WindowManager.LayoutParams#FLAG_SECURE
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+ default void onSecureWindowHidden(int displayId) {}
}
/**
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 4b9065b..6069e34 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -88,6 +88,9 @@
/** Called when a secure window shows on the virtual display. */
void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo);
+ /** Called when a secure window is no longer shown on the virtual display. */
+ void onSecureWindowHidden(int displayId);
+
/** Returns true when an intent should be intercepted */
boolean shouldInterceptIntent(@NonNull Intent intent);
}
@@ -123,6 +126,9 @@
private boolean mIsMirrorDisplay = false;
private final CountDownLatch mDisplayIdSetLatch = new CountDownLatch(1);
+ // Used for detecting changes in the window flags.
+ private int mCurrentWindowFlags = 0;
+
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
private final ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -371,12 +377,19 @@
public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
int systemWindowFlags) {
int displayId = waitAndGetDisplayId();
- // The callback is fired only when windowFlags are changed. To let VirtualDevice owner
- // aware that the virtual display has a secure window on top.
- if ((windowFlags & FLAG_SECURE) != 0 && displayId != INVALID_DISPLAY) {
+ if (displayId != INVALID_DISPLAY) {
+ // The callback is fired only when windowFlags are changed. To let VirtualDevice owner
+ // aware that the virtual display has a secure window on top.
// Post callback on the main thread, so it doesn't block activity launching.
- mHandler.post(() -> mActivityListener.onSecureWindowShown(displayId, activityInfo));
+ if ((windowFlags & FLAG_SECURE) != 0 && (mCurrentWindowFlags & FLAG_SECURE) == 0) {
+ mHandler.post(
+ () -> mActivityListener.onSecureWindowShown(displayId, activityInfo));
+ }
+ if ((windowFlags & FLAG_SECURE) == 0 && (mCurrentWindowFlags & FLAG_SECURE) != 0) {
+ mHandler.post(() -> mActivityListener.onSecureWindowHidden(displayId));
+ }
}
+ mCurrentWindowFlags = windowFlags;
if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
activityInfo.packageName,
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 8b5b93e..a1d621d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -312,6 +312,17 @@
}
}
+ @Override
+ public void onSecureWindowHidden(int displayId) {
+ if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+ try {
+ mActivityListener.onSecureWindowHidden(displayId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+ }
+ }
+
/**
* Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true
* if the intent matches any filter notifying the DisplayPolicyController to abort the
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 1a593dd..42b7f4b 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -755,6 +755,7 @@
verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never()).onSecureWindowHidden(eq(DISPLAY_ID));
verify(mActivityListener, never())
.onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@@ -776,6 +777,10 @@
.onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
+
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS)).onSecureWindowHidden(eq(DISPLAY_ID));
}
@Test
@@ -794,6 +799,7 @@
verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never()).onSecureWindowHidden(eq(DISPLAY_ID));
verify(mActivityListener, never())
.onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}