diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d3d049382fd0c..b7da6ae31ce34 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6321,6 +6321,8 @@ ul.
Can’t access the phone’s camera from your %1$s
Can’t access the tablet’s camera from your %1$s
+
+ This can’t be accessed while streaming. Try on your phone instead.
System default
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01c8e968194b0..0654fff661bdf 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4803,6 +4803,7 @@
+
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 43e2b881927c6..593a63c2f0c90 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -76,6 +76,14 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+ /**
+ * For communicating when a secure window shows on the virtual display.
+ */
+ public interface SecureWindowCallback {
+ /** Called when a secure window shows on the virtual display. */
+ void onSecureWindowShown(int displayId, int uid);
+ }
+
/**
* If required, allow the secure activity to display on remote device since
* {@link android.os.Build.VERSION_CODES#TIRAMISU}.
@@ -108,6 +116,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
new ArraySet<>();
@Nullable
private final @AssociationRequest.DeviceProfile String mDeviceProfile;
+ @Nullable private final SecureWindowCallback mSecureWindowCallback;
/**
* Creates a window policy controller that is generic to the different use cases of virtual
@@ -131,6 +140,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
* @param activityListener Activity listener to listen for activity changes.
* @param activityBlockedCallback Callback that is called when an activity is blocked from
* launching.
+ * @param secureWindowCallback Callback that is called when a secure window shows on the
+ * virtual display.
* @param deviceProfile The {@link AssociationRequest.DeviceProfile} of this virtual device.
*/
public GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
@@ -142,6 +153,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@ActivityPolicy int defaultActivityPolicy,
@NonNull ActivityListener activityListener,
@NonNull ActivityBlockedCallback activityBlockedCallback,
+ @NonNull SecureWindowCallback secureWindowCallback,
@AssociationRequest.DeviceProfile String deviceProfile) {
super();
mAllowedUsers = allowedUsers;
@@ -154,6 +166,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
mDeviceProfile = deviceProfile;
+ mSecureWindowCallback = secureWindowCallback;
}
/**
@@ -234,6 +247,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@Override
public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
int systemWindowFlags) {
+ // 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) {
+ mSecureWindowCallback.onSecureWindowShown(mDisplayId, activityInfo.applicationInfo.uid);
+ }
+
if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) {
mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
return false;
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 5f337ab9bae30..cca3212703f06 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -52,6 +52,7 @@ import android.hardware.input.VirtualMouseScrollEvent;
import android.hardware.input.VirtualTouchEvent;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -542,6 +543,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mParams.getDefaultActivityPolicy(),
createListenerAdapter(),
this::onActivityBlocked,
+ this::onSecureWindowShown,
mAssociationInfo.getDeviceProfile());
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
return gwpc;
@@ -591,6 +593,21 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mContext.getUser());
}
+ private void onSecureWindowShown(int displayId, int uid) {
+ if (!mVirtualDisplayIds.contains(displayId)) {
+ return;
+ }
+
+ // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
+ // if the secure window is shown on a non-secure virtual display.
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ Display display = displayManager.getDisplay(displayId);
+ if ((display.getFlags() & FLAG_SECURE) == 0) {
+ showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,
+ Toast.LENGTH_LONG, mContext.getMainLooper());
+ }
+ }
+
private ArraySet getAllowedUserHandles() {
ArraySet result = new ArraySet<>();
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
@@ -650,14 +667,16 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
/**
* Shows a toast on virtual displays owned by this device which have a given uid running.
*/
- void showToastWhereUidIsRunning(int uid, @StringRes int resId, @Toast.Duration int duration) {
- showToastWhereUidIsRunning(uid, mContext.getString(resId), duration);
+ void showToastWhereUidIsRunning(int uid, @StringRes int resId, @Toast.Duration int duration,
+ Looper looper) {
+ showToastWhereUidIsRunning(uid, mContext.getString(resId), duration, looper);
}
/**
* Shows a toast on virtual displays owned by this device which have a given uid running.
*/
- void showToastWhereUidIsRunning(int uid, String text, @Toast.Duration int duration) {
+ void showToastWhereUidIsRunning(int uid, String text, @Toast.Duration int duration,
+ Looper looper) {
synchronized (mVirtualDeviceLock) {
DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
final int size = mWindowPolicyControllers.size();
@@ -666,7 +685,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
int displayId = mWindowPolicyControllers.keyAt(i);
Display display = displayManager.getDisplay(displayId);
if (display != null && display.isValid()) {
- Toast.makeText(mContext.createDisplayContext(display), text,
+ Toast.makeText(mContext.createDisplayContext(display), looper, text,
duration).show();
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index b25518875cf02..cdddc1dea82bd 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -203,7 +203,7 @@ public class VirtualDeviceManagerService extends SystemService {
getContext().getString(
com.android.internal.R.string.vdm_camera_access_denied,
deviceName),
- Toast.LENGTH_LONG);
+ Toast.LENGTH_LONG, Looper.myLooper());
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index e2c3a94fc187d..4c939f0779402 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -83,6 +83,7 @@ public class VirtualAudioControllerTest {
VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED,
/* activityListener= */ null,
/* activityBlockedCallback= */ null,
+ /* secureWindowCallback= */ null,
/* deviceProfile= */ DEVICE_PROFILE_APP_STREAMING);
}