Loading services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +26 −48 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.WindowConfiguration; import android.app.compat.CompatChanges; import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.AttributionSource; Loading Loading @@ -61,6 +60,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController private static final String TAG = "GenericWindowPolicyController"; private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = new ComponentName("android", BlockedAppStreamingActivity.class.getName()); /** Interface to listen running applications change on virtual display. */ public interface RunningAppsChangedListener { /** Loading @@ -69,29 +71,25 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController void onRunningAppsChanged(ArraySet<Integer> runningUids); } /** * For communicating when activities are blocked from running on the display by this policy * controller. */ public interface ActivityBlockedCallback { /** Interface to react to activity changes on the virtual display. */ public interface ActivityListener { /** Called when the top activity changes. */ void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity, @UserIdInt int userId); /** Called when the display becomes empty. */ void onDisplayEmpty(int displayId); /** Called when an activity is blocked.*/ void onActivityBlocked(int displayId, ActivityInfo activityInfo, IntentSender intentSender); } private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = new ComponentName("android", BlockedAppStreamingActivity.class.getName()); void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo, @Nullable IntentSender intentSender); /** * 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); } void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo); /** Interface to listen for interception of intents. */ public interface IntentListenerCallback { /** Returns true when an intent should be intercepted */ boolean shouldInterceptIntent(Intent intent); boolean shouldInterceptIntent(@NonNull Intent intent); } /** Loading @@ -118,7 +116,6 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController private final ArraySet<ComponentName> mCrossTaskNavigationExemptions; @NonNull private final Object mGenericWindowPolicyControllerLock = new Object(); @Nullable private final ActivityBlockedCallback mActivityBlockedCallback; // Do not access mDisplayId and mIsMirrorDisplay directly, instead use waitAndGetDisplayId() // and waitAndGetIsMirrorDisplay() Loading @@ -129,14 +126,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull @GuardedBy("mGenericWindowPolicyControllerLock") private final ArraySet<Integer> mRunningUids = new ArraySet<>(); @Nullable private final ActivityListener mActivityListener; @Nullable private final IntentListenerCallback mIntentListenerCallback; @NonNull private final ActivityListener mActivityListener; private final Handler mHandler = new Handler(Looper.getMainLooper()); @NonNull @GuardedBy("mGenericWindowPolicyControllerLock") private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners = new ArraySet<>(); @Nullable private final SecureWindowCallback mSecureWindowCallback; @NonNull private final Set<String> mDisplayCategories; @GuardedBy("mGenericWindowPolicyControllerLock") Loading @@ -162,12 +157,6 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * @param crossTaskNavigationExemptions The set of components explicitly exempt from the default * navigation policy. * @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 intentListenerCallback Callback that is called to intercept intents when matching * passed in filters. * @param showTasksInHostDeviceRecents whether to show activities in recents on the host device. * @param customHomeComponent The component acting as a home activity on the virtual display. If * {@code null}, then the system-default secondary home activity will be used. This is only Loading @@ -184,10 +173,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull Set<String> activityPolicyPackageExemptions, boolean crossTaskNavigationAllowedByDefault, @NonNull Set<ComponentName> crossTaskNavigationExemptions, @Nullable ActivityListener activityListener, @Nullable ActivityBlockedCallback activityBlockedCallback, @Nullable SecureWindowCallback secureWindowCallback, @Nullable IntentListenerCallback intentListenerCallback, @NonNull ActivityListener activityListener, @NonNull Set<String> displayCategories, boolean showTasksInHostDeviceRecents, @Nullable ComponentName customHomeComponent) { Loading @@ -199,11 +185,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mActivityPolicyPackageExemptions = new ArraySet<>(activityPolicyPackageExemptions); mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault; mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions); mActivityBlockedCallback = activityBlockedCallback; setInterestedWindowFlags(windowFlags, systemWindowFlags); mActivityListener = activityListener; mSecureWindowCallback = secureWindowCallback; mIntentListenerCallback = intentListenerCallback; mDisplayCategories = displayCategories; mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents; mCustomHomeComponent = customHomeComponent; Loading Loading @@ -306,8 +289,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, boolean isNewTask, boolean isResultExpected, @Nullable Supplier<IntentSender> intentSender) { if (mIntentListenerCallback != null && intent != null && mIntentListenerCallback.shouldInterceptIntent(intent)) { if (intent != null && mActivityListener.shouldInterceptIntent(intent)) { logActivityLaunchBlocked("Virtual device intercepting intent"); return false; } Loading Loading @@ -391,11 +373,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController 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 && mSecureWindowCallback != null && displayId != INVALID_DISPLAY) { if ((windowFlags & FLAG_SECURE) != 0 && displayId != INVALID_DISPLAY) { // Post callback on the main thread, so it doesn't block activity launching. mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(displayId, activityInfo.applicationInfo.uid)); mHandler.post(() -> mActivityListener.onSecureWindowShown(displayId, activityInfo)); } if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE, Loading @@ -418,7 +398,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController // Don't send onTopActivityChanged() callback when topActivity is null because it's defined // as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when // there is no activity running on virtual display. if (mActivityListener != null && topActivity != null && displayId != INVALID_DISPLAY) { if (topActivity != null && displayId != INVALID_DISPLAY) { // Post callback on the main thread so it doesn't block activity launching mHandler.post(() -> mActivityListener.onTopActivityChanged(displayId, topActivity, userId)); Loading @@ -431,8 +411,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mRunningUids.clear(); mRunningUids.addAll(runningUids); int displayId = waitAndGetDisplayId(); if (mActivityListener != null && mRunningUids.isEmpty() && displayId != INVALID_DISPLAY) { if (mRunningUids.isEmpty() && displayId != INVALID_DISPLAY) { // Post callback on the main thread so it doesn't block activity launching mHandler.post(() -> mActivityListener.onDisplayEmpty(displayId)); } Loading Loading @@ -482,9 +461,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController int displayId = waitAndGetDisplayId(); // Don't trigger activity blocked callback for mirror displays, because we can't show // any activity or presentation on it anyway. if (!waitAndGetIsMirrorDisplay() && mActivityBlockedCallback != null && displayId != INVALID_DISPLAY) { mActivityBlockedCallback.onActivityBlocked(displayId, activityInfo, if (!waitAndGetIsMirrorDisplay() && displayId != INVALID_DISPLAY) { mActivityListener.onActivityLaunchBlocked(displayId, activityInfo, intentSender == null ? null : intentSender.get()); } Counter.logIncrementWithUid( Loading services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +94 −113 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.companion.virtual.IVirtualDeviceIntentInterceptor; import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; Loading Loading @@ -182,7 +181,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @GuardedBy("mVirtualDeviceLock") private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>(); private IVirtualDeviceActivityListener mActivityListener; private ActivityListener mActivityListenerAdapter = null; private GenericWindowPolicyController.ActivityListener mActivityListenerAdapter = null; private IVirtualDeviceSoundEffectListener mSoundEffectListener; private final DisplayManagerGlobal mDisplayManager; private final DisplayManagerInternal mDisplayManagerInternal; Loading @@ -207,18 +206,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @NonNull private final Set<String> mActivityPolicyPackageExemptions = new ArraySet<>(); private ActivityListener createListenerAdapter() { return new ActivityListener() { @Override public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity) { try { mActivityListener.onTopActivityChanged(displayId, topActivity, UserHandle.USER_NULL); } catch (RemoteException e) { Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e); } } private class GwpcActivityListener implements GenericWindowPolicyController.ActivityListener { @Override public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity, Loading @@ -240,17 +228,90 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } @Override public void onActivityLaunchBlocked(int displayId, @NonNull ComponentName componentName, @NonNull UserHandle user, public void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo, @Nullable IntentSender intentSender) { Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName()); if (shouldShowBlockedActivityDialog( activityInfo.getComponentName(), intent.getComponent())) { mContext.startActivityAsUser( intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), UserHandle.SYSTEM); } if (android.companion.virtualdevice.flags.Flags.activityControlApi()) { try { mActivityListener.onActivityLaunchBlocked( displayId, componentName, user, intentSender); displayId, activityInfo.getComponentName(), UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid), intentSender); } catch (RemoteException e) { Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e); } } }; } @Override public void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo) { synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.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() & Display.FLAG_SECURE) == 0) { showToastWhereUidIsRunning(activityInfo.applicationInfo.uid, com.android.internal.R.string.vdm_secure_window, Toast.LENGTH_LONG, mContext.getMainLooper()); Counter.logIncrementWithUid( "virtual_devices.value_secure_window_blocked_count", mAttributionSource.getUid()); } } /** * 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 * activity launch to be replaced by the interception. */ @Override public boolean shouldInterceptIntent(@NonNull Intent intent) { synchronized (mVirtualDeviceLock) { boolean hasInterceptedIntent = false; for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) { IntentFilter intentFilter = interceptor.getValue(); // Explicitly match the actions because the intent filter will match any intent // without an explicit action. If the intent has no action, then require that // there are no actions specified in the filter either. boolean explicitActionMatch = intent.getAction() != null || intentFilter.countActions() == 0; if (explicitActionMatch && intentFilter.match( intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(), intent.getCategories(), TAG) >= 0) { try { // For privacy reasons, only returning the intents action and data. // Any other required field will require a review. IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey()) .onIntentIntercepted( new Intent(intent.getAction(), intent.getData())); hasInterceptedIntent = true; } catch (RemoteException e) { Slog.w(TAG, "Unable to call mActivityListener", e); } } } return hasInterceptedIntent; } } } VirtualDeviceImpl( Loading Loading @@ -1290,7 +1351,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Flags.vdmCustomHome() ? mParams.getHomeComponent() : null; if (mActivityListenerAdapter == null) { mActivityListenerAdapter = createListenerAdapter(); mActivityListenerAdapter = new GwpcActivityListener(); } final GenericWindowPolicyController gwpc = new GenericWindowPolicyController( Loading @@ -1306,9 +1367,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub ? mParams.getBlockedCrossTaskNavigations() : mParams.getAllowedCrossTaskNavigations(), mActivityListenerAdapter, this::onActivityBlocked, this::onSecureWindowShown, this::shouldInterceptIntent, displayCategories, showTasksInHostDeviceRecents, homeComponent); Loading Loading @@ -1378,28 +1436,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) private void onActivityBlocked(int displayId, ActivityInfo activityInfo, IntentSender intentSender) { Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName()); if (shouldShowBlockedActivityDialog( activityInfo.getComponentName(), intent.getComponent())) { mContext.startActivityAsUser( intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), UserHandle.SYSTEM); } if (android.companion.virtualdevice.flags.Flags.activityControlApi()) { mActivityListenerAdapter.onActivityLaunchBlocked( displayId, activityInfo.getComponentName(), UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid), intentSender); } } private boolean shouldShowBlockedActivityDialog(ComponentName blockedComponent, ComponentName blockedAppStreamingActivityComponent) { if (Objects.equals(blockedComponent, blockedAppStreamingActivityComponent)) { Loading @@ -1414,27 +1450,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY) == DEVICE_POLICY_DEFAULT; } private void onSecureWindowShown(int displayId, int uid) { synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.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() & Display.FLAG_SECURE) == 0) { showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window, Toast.LENGTH_LONG, mContext.getMainLooper()); Counter.logIncrementWithUid( "virtual_devices.value_secure_window_blocked_count", mAttributionSource.getUid()); } } private ArraySet<UserHandle> getAllowedUserHandles() { ArraySet<UserHandle> result = new ArraySet<>(); final long token = Binder.clearCallingIdentity(); Loading Loading @@ -1621,40 +1636,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } /** * 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 * activity launch to be replaced by the interception. */ private boolean shouldInterceptIntent(Intent intent) { synchronized (mVirtualDeviceLock) { boolean hasInterceptedIntent = false; for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) { IntentFilter intentFilter = interceptor.getValue(); // Explicitly match the actions because the intent filter will match any intent // without an explicit action. If the intent has no action, then require that there // are no actions specified in the filter either. boolean explicitActionMatch = intent.getAction() != null || intentFilter.countActions() == 0; if (explicitActionMatch && intentFilter.match( intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(), intent.getCategories(), TAG) >= 0) { try { // For privacy reasons, only returning the intents action and data. Any // other required field will require a review. IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey()) .onIntentIntercepted(new Intent(intent.getAction(), intent.getData())); hasInterceptedIntent = true; } catch (RemoteException e) { Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e); } } } return hasInterceptedIntent; } } interface PendingTrampolineCallback { /** * Called when the callback should start waiting for the given pending trampoline. Loading services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java +28 −60 File changed.Preview size limit exceeded, changes collapsed. Show changes services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +0 −3 Original line number Diff line number Diff line Loading @@ -87,9 +87,6 @@ public class VirtualAudioControllerTest { /* crossTaskNavigationAllowedByDefault= */ true, /* crossTaskNavigationExemptions= */ new ArraySet<>(), /* activityListener= */ null, /* activityBlockedCallback= */ null, /* secureWindowCallback= */ null, /* intentListenerCallback= */ null, /* displayCategories= */ new ArraySet<>(), /* showTasksInHostDeviceRecents= */ true, /* customHomeComponent= */ null); Loading Loading
services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +26 −48 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.WindowConfiguration; import android.app.compat.CompatChanges; import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.AttributionSource; Loading Loading @@ -61,6 +60,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController private static final String TAG = "GenericWindowPolicyController"; private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = new ComponentName("android", BlockedAppStreamingActivity.class.getName()); /** Interface to listen running applications change on virtual display. */ public interface RunningAppsChangedListener { /** Loading @@ -69,29 +71,25 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController void onRunningAppsChanged(ArraySet<Integer> runningUids); } /** * For communicating when activities are blocked from running on the display by this policy * controller. */ public interface ActivityBlockedCallback { /** Interface to react to activity changes on the virtual display. */ public interface ActivityListener { /** Called when the top activity changes. */ void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity, @UserIdInt int userId); /** Called when the display becomes empty. */ void onDisplayEmpty(int displayId); /** Called when an activity is blocked.*/ void onActivityBlocked(int displayId, ActivityInfo activityInfo, IntentSender intentSender); } private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = new ComponentName("android", BlockedAppStreamingActivity.class.getName()); void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo, @Nullable IntentSender intentSender); /** * 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); } void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo); /** Interface to listen for interception of intents. */ public interface IntentListenerCallback { /** Returns true when an intent should be intercepted */ boolean shouldInterceptIntent(Intent intent); boolean shouldInterceptIntent(@NonNull Intent intent); } /** Loading @@ -118,7 +116,6 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController private final ArraySet<ComponentName> mCrossTaskNavigationExemptions; @NonNull private final Object mGenericWindowPolicyControllerLock = new Object(); @Nullable private final ActivityBlockedCallback mActivityBlockedCallback; // Do not access mDisplayId and mIsMirrorDisplay directly, instead use waitAndGetDisplayId() // and waitAndGetIsMirrorDisplay() Loading @@ -129,14 +126,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull @GuardedBy("mGenericWindowPolicyControllerLock") private final ArraySet<Integer> mRunningUids = new ArraySet<>(); @Nullable private final ActivityListener mActivityListener; @Nullable private final IntentListenerCallback mIntentListenerCallback; @NonNull private final ActivityListener mActivityListener; private final Handler mHandler = new Handler(Looper.getMainLooper()); @NonNull @GuardedBy("mGenericWindowPolicyControllerLock") private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners = new ArraySet<>(); @Nullable private final SecureWindowCallback mSecureWindowCallback; @NonNull private final Set<String> mDisplayCategories; @GuardedBy("mGenericWindowPolicyControllerLock") Loading @@ -162,12 +157,6 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * @param crossTaskNavigationExemptions The set of components explicitly exempt from the default * navigation policy. * @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 intentListenerCallback Callback that is called to intercept intents when matching * passed in filters. * @param showTasksInHostDeviceRecents whether to show activities in recents on the host device. * @param customHomeComponent The component acting as a home activity on the virtual display. If * {@code null}, then the system-default secondary home activity will be used. This is only Loading @@ -184,10 +173,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull Set<String> activityPolicyPackageExemptions, boolean crossTaskNavigationAllowedByDefault, @NonNull Set<ComponentName> crossTaskNavigationExemptions, @Nullable ActivityListener activityListener, @Nullable ActivityBlockedCallback activityBlockedCallback, @Nullable SecureWindowCallback secureWindowCallback, @Nullable IntentListenerCallback intentListenerCallback, @NonNull ActivityListener activityListener, @NonNull Set<String> displayCategories, boolean showTasksInHostDeviceRecents, @Nullable ComponentName customHomeComponent) { Loading @@ -199,11 +185,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mActivityPolicyPackageExemptions = new ArraySet<>(activityPolicyPackageExemptions); mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault; mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions); mActivityBlockedCallback = activityBlockedCallback; setInterestedWindowFlags(windowFlags, systemWindowFlags); mActivityListener = activityListener; mSecureWindowCallback = secureWindowCallback; mIntentListenerCallback = intentListenerCallback; mDisplayCategories = displayCategories; mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents; mCustomHomeComponent = customHomeComponent; Loading Loading @@ -306,8 +289,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, boolean isNewTask, boolean isResultExpected, @Nullable Supplier<IntentSender> intentSender) { if (mIntentListenerCallback != null && intent != null && mIntentListenerCallback.shouldInterceptIntent(intent)) { if (intent != null && mActivityListener.shouldInterceptIntent(intent)) { logActivityLaunchBlocked("Virtual device intercepting intent"); return false; } Loading Loading @@ -391,11 +373,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController 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 && mSecureWindowCallback != null && displayId != INVALID_DISPLAY) { if ((windowFlags & FLAG_SECURE) != 0 && displayId != INVALID_DISPLAY) { // Post callback on the main thread, so it doesn't block activity launching. mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(displayId, activityInfo.applicationInfo.uid)); mHandler.post(() -> mActivityListener.onSecureWindowShown(displayId, activityInfo)); } if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE, Loading @@ -418,7 +398,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController // Don't send onTopActivityChanged() callback when topActivity is null because it's defined // as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when // there is no activity running on virtual display. if (mActivityListener != null && topActivity != null && displayId != INVALID_DISPLAY) { if (topActivity != null && displayId != INVALID_DISPLAY) { // Post callback on the main thread so it doesn't block activity launching mHandler.post(() -> mActivityListener.onTopActivityChanged(displayId, topActivity, userId)); Loading @@ -431,8 +411,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mRunningUids.clear(); mRunningUids.addAll(runningUids); int displayId = waitAndGetDisplayId(); if (mActivityListener != null && mRunningUids.isEmpty() && displayId != INVALID_DISPLAY) { if (mRunningUids.isEmpty() && displayId != INVALID_DISPLAY) { // Post callback on the main thread so it doesn't block activity launching mHandler.post(() -> mActivityListener.onDisplayEmpty(displayId)); } Loading Loading @@ -482,9 +461,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController int displayId = waitAndGetDisplayId(); // Don't trigger activity blocked callback for mirror displays, because we can't show // any activity or presentation on it anyway. if (!waitAndGetIsMirrorDisplay() && mActivityBlockedCallback != null && displayId != INVALID_DISPLAY) { mActivityBlockedCallback.onActivityBlocked(displayId, activityInfo, if (!waitAndGetIsMirrorDisplay() && displayId != INVALID_DISPLAY) { mActivityListener.onActivityLaunchBlocked(displayId, activityInfo, intentSender == null ? null : intentSender.get()); } Counter.logIncrementWithUid( Loading
services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +94 −113 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.companion.virtual.IVirtualDeviceIntentInterceptor; import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; Loading Loading @@ -182,7 +181,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @GuardedBy("mVirtualDeviceLock") private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>(); private IVirtualDeviceActivityListener mActivityListener; private ActivityListener mActivityListenerAdapter = null; private GenericWindowPolicyController.ActivityListener mActivityListenerAdapter = null; private IVirtualDeviceSoundEffectListener mSoundEffectListener; private final DisplayManagerGlobal mDisplayManager; private final DisplayManagerInternal mDisplayManagerInternal; Loading @@ -207,18 +206,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @NonNull private final Set<String> mActivityPolicyPackageExemptions = new ArraySet<>(); private ActivityListener createListenerAdapter() { return new ActivityListener() { @Override public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity) { try { mActivityListener.onTopActivityChanged(displayId, topActivity, UserHandle.USER_NULL); } catch (RemoteException e) { Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e); } } private class GwpcActivityListener implements GenericWindowPolicyController.ActivityListener { @Override public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity, Loading @@ -240,17 +228,90 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } @Override public void onActivityLaunchBlocked(int displayId, @NonNull ComponentName componentName, @NonNull UserHandle user, public void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo, @Nullable IntentSender intentSender) { Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName()); if (shouldShowBlockedActivityDialog( activityInfo.getComponentName(), intent.getComponent())) { mContext.startActivityAsUser( intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), UserHandle.SYSTEM); } if (android.companion.virtualdevice.flags.Flags.activityControlApi()) { try { mActivityListener.onActivityLaunchBlocked( displayId, componentName, user, intentSender); displayId, activityInfo.getComponentName(), UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid), intentSender); } catch (RemoteException e) { Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e); } } }; } @Override public void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo) { synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.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() & Display.FLAG_SECURE) == 0) { showToastWhereUidIsRunning(activityInfo.applicationInfo.uid, com.android.internal.R.string.vdm_secure_window, Toast.LENGTH_LONG, mContext.getMainLooper()); Counter.logIncrementWithUid( "virtual_devices.value_secure_window_blocked_count", mAttributionSource.getUid()); } } /** * 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 * activity launch to be replaced by the interception. */ @Override public boolean shouldInterceptIntent(@NonNull Intent intent) { synchronized (mVirtualDeviceLock) { boolean hasInterceptedIntent = false; for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) { IntentFilter intentFilter = interceptor.getValue(); // Explicitly match the actions because the intent filter will match any intent // without an explicit action. If the intent has no action, then require that // there are no actions specified in the filter either. boolean explicitActionMatch = intent.getAction() != null || intentFilter.countActions() == 0; if (explicitActionMatch && intentFilter.match( intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(), intent.getCategories(), TAG) >= 0) { try { // For privacy reasons, only returning the intents action and data. // Any other required field will require a review. IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey()) .onIntentIntercepted( new Intent(intent.getAction(), intent.getData())); hasInterceptedIntent = true; } catch (RemoteException e) { Slog.w(TAG, "Unable to call mActivityListener", e); } } } return hasInterceptedIntent; } } } VirtualDeviceImpl( Loading Loading @@ -1290,7 +1351,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Flags.vdmCustomHome() ? mParams.getHomeComponent() : null; if (mActivityListenerAdapter == null) { mActivityListenerAdapter = createListenerAdapter(); mActivityListenerAdapter = new GwpcActivityListener(); } final GenericWindowPolicyController gwpc = new GenericWindowPolicyController( Loading @@ -1306,9 +1367,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub ? mParams.getBlockedCrossTaskNavigations() : mParams.getAllowedCrossTaskNavigations(), mActivityListenerAdapter, this::onActivityBlocked, this::onSecureWindowShown, this::shouldInterceptIntent, displayCategories, showTasksInHostDeviceRecents, homeComponent); Loading Loading @@ -1378,28 +1436,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) private void onActivityBlocked(int displayId, ActivityInfo activityInfo, IntentSender intentSender) { Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName()); if (shouldShowBlockedActivityDialog( activityInfo.getComponentName(), intent.getComponent())) { mContext.startActivityAsUser( intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), UserHandle.SYSTEM); } if (android.companion.virtualdevice.flags.Flags.activityControlApi()) { mActivityListenerAdapter.onActivityLaunchBlocked( displayId, activityInfo.getComponentName(), UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid), intentSender); } } private boolean shouldShowBlockedActivityDialog(ComponentName blockedComponent, ComponentName blockedAppStreamingActivityComponent) { if (Objects.equals(blockedComponent, blockedAppStreamingActivityComponent)) { Loading @@ -1414,27 +1450,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY) == DEVICE_POLICY_DEFAULT; } private void onSecureWindowShown(int displayId, int uid) { synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.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() & Display.FLAG_SECURE) == 0) { showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window, Toast.LENGTH_LONG, mContext.getMainLooper()); Counter.logIncrementWithUid( "virtual_devices.value_secure_window_blocked_count", mAttributionSource.getUid()); } } private ArraySet<UserHandle> getAllowedUserHandles() { ArraySet<UserHandle> result = new ArraySet<>(); final long token = Binder.clearCallingIdentity(); Loading Loading @@ -1621,40 +1636,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } /** * 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 * activity launch to be replaced by the interception. */ private boolean shouldInterceptIntent(Intent intent) { synchronized (mVirtualDeviceLock) { boolean hasInterceptedIntent = false; for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) { IntentFilter intentFilter = interceptor.getValue(); // Explicitly match the actions because the intent filter will match any intent // without an explicit action. If the intent has no action, then require that there // are no actions specified in the filter either. boolean explicitActionMatch = intent.getAction() != null || intentFilter.countActions() == 0; if (explicitActionMatch && intentFilter.match( intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(), intent.getCategories(), TAG) >= 0) { try { // For privacy reasons, only returning the intents action and data. Any // other required field will require a review. IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey()) .onIntentIntercepted(new Intent(intent.getAction(), intent.getData())); hasInterceptedIntent = true; } catch (RemoteException e) { Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e); } } } return hasInterceptedIntent; } } interface PendingTrampolineCallback { /** * Called when the callback should start waiting for the given pending trampoline. Loading
services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java +28 −60 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +0 −3 Original line number Diff line number Diff line Loading @@ -87,9 +87,6 @@ public class VirtualAudioControllerTest { /* crossTaskNavigationAllowedByDefault= */ true, /* crossTaskNavigationExemptions= */ new ArraySet<>(), /* activityListener= */ null, /* activityBlockedCallback= */ null, /* secureWindowCallback= */ null, /* intentListenerCallback= */ null, /* displayCategories= */ new ArraySet<>(), /* showTasksInHostDeviceRecents= */ true, /* customHomeComponent= */ null); Loading