1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.companion.virtual;
18 
19 import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
20 import static android.view.Display.DEFAULT_DISPLAY;
21 import static android.view.Display.INVALID_DISPLAY;
22 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
23 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.UserIdInt;
28 import android.app.WindowConfiguration;
29 import android.app.compat.CompatChanges;
30 import android.companion.virtual.VirtualDeviceManager.ActivityListener;
31 import android.companion.virtual.flags.Flags;
32 import android.compat.annotation.ChangeId;
33 import android.compat.annotation.EnabledSince;
34 import android.content.AttributionSource;
35 import android.content.ComponentName;
36 import android.content.Intent;
37 import android.content.pm.ActivityInfo;
38 import android.os.Build;
39 import android.os.Handler;
40 import android.os.Looper;
41 import android.os.UserHandle;
42 import android.util.ArraySet;
43 import android.util.Slog;
44 import android.view.Display;
45 import android.window.DisplayWindowPolicyController;
46 
47 import com.android.internal.annotations.GuardedBy;
48 import com.android.internal.annotations.VisibleForTesting;
49 import com.android.internal.app.BlockedAppStreamingActivity;
50 import com.android.modules.expresslog.Counter;
51 
52 import java.util.Set;
53 import java.util.concurrent.CountDownLatch;
54 import java.util.concurrent.TimeUnit;
55 
56 /**
57  * A controller to control the policies of the windows that can be displayed on the virtual display.
58  */
59 public class GenericWindowPolicyController extends DisplayWindowPolicyController {
60 
61     private static final String TAG = "GenericWindowPolicyController";
62 
63     /** Interface to listen running applications change on virtual display. */
64     public interface RunningAppsChangedListener {
65         /**
66          * Notifies the running applications change.
67          */
onRunningAppsChanged(ArraySet<Integer> runningUids)68         void onRunningAppsChanged(ArraySet<Integer> runningUids);
69     }
70 
71     /**
72      * For communicating when activities are blocked from running on the display by this policy
73      * controller.
74      */
75     public interface ActivityBlockedCallback {
76         /** Called when an activity is blocked.*/
onActivityBlocked(int displayId, ActivityInfo activityInfo)77         void onActivityBlocked(int displayId, ActivityInfo activityInfo);
78     }
79     private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
80             new ComponentName("android", BlockedAppStreamingActivity.class.getName());
81 
82     /**
83      * For communicating when a secure window shows on the virtual display.
84      */
85     public interface SecureWindowCallback {
86         /** Called when a secure window shows on the virtual display. */
onSecureWindowShown(int displayId, int uid)87         void onSecureWindowShown(int displayId, int uid);
88     }
89 
90     /**
91      * For communicating when activities are blocked from entering PIP on the display by this
92      * policy controller.
93      */
94     public interface PipBlockedCallback {
95         /** Called when an activity is blocked from entering PIP. */
onEnteringPipBlocked(int uid)96         void onEnteringPipBlocked(int uid);
97     }
98 
99     /** Interface to listen for interception of intents. */
100     public interface IntentListenerCallback {
101         /** Returns true when an intent should be intercepted */
shouldInterceptIntent(Intent intent)102         boolean shouldInterceptIntent(Intent intent);
103     }
104 
105     /**
106      * If required, allow the secure activity to display on remote device since
107      * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
108      */
109     @ChangeId
110     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
111     public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
112     @NonNull
113     private final AttributionSource mAttributionSource;
114     @NonNull
115     private final ArraySet<UserHandle> mAllowedUsers;
116     @GuardedBy("mGenericWindowPolicyControllerLock")
117     private boolean mActivityLaunchAllowedByDefault;
118     @NonNull
119     @GuardedBy("mGenericWindowPolicyControllerLock")
120     private final Set<ComponentName> mActivityPolicyExemptions;
121     private final boolean mCrossTaskNavigationAllowedByDefault;
122     @NonNull
123     private final ArraySet<ComponentName> mCrossTaskNavigationExemptions;
124     @Nullable
125     private final ComponentName mPermissionDialogComponent;
126     private final Object mGenericWindowPolicyControllerLock = new Object();
127     @Nullable private final ActivityBlockedCallback mActivityBlockedCallback;
128 
129     // Do not access mDisplayId and mIsMirrorDisplay directly, instead use waitAndGetDisplayId()
130     // and waitAndGetIsMirrorDisplay()
131     private int mDisplayId = Display.INVALID_DISPLAY;
132     private boolean mIsMirrorDisplay = false;
133     private final CountDownLatch mDisplayIdSetLatch = new CountDownLatch(1);
134 
135     @NonNull
136     @GuardedBy("mGenericWindowPolicyControllerLock")
137     private final ArraySet<Integer> mRunningUids = new ArraySet<>();
138     @Nullable private final ActivityListener mActivityListener;
139     @Nullable private final PipBlockedCallback mPipBlockedCallback;
140     @Nullable private final IntentListenerCallback mIntentListenerCallback;
141     private final Handler mHandler = new Handler(Looper.getMainLooper());
142     @NonNull
143     @GuardedBy("mGenericWindowPolicyControllerLock")
144     private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
145             new ArraySet<>();
146     @Nullable private final SecureWindowCallback mSecureWindowCallback;
147     @NonNull private final Set<String> mDisplayCategories;
148 
149     @GuardedBy("mGenericWindowPolicyControllerLock")
150     private boolean mShowTasksInHostDeviceRecents;
151     @Nullable private final ComponentName mCustomHomeComponent;
152 
153     /**
154      * Creates a window policy controller that is generic to the different use cases of virtual
155      * device.
156      *
157      * @param windowFlags The window flags that this controller is interested in.
158      * @param systemWindowFlags The system window flags that this controller is interested in.
159      * @param attributionSource The AttributionSource of the VirtualDevice owner application.
160      * @param allowedUsers The set of users that are allowed to stream in this display.
161      * @param activityLaunchAllowedByDefault Whether activities are default allowed to be launched
162      *   or blocked.
163      * @param activityPolicyExemptions The set of activities explicitly exempt from the default
164      *   activity policy.
165      * @param crossTaskNavigationAllowedByDefault Whether cross task navigations are allowed by
166      *   default or not.
167      * @param crossTaskNavigationExemptions The set of components explicitly exempt from the default
168      *   navigation policy.
169      * @param activityListener Activity listener to listen for activity changes.
170      * @param activityBlockedCallback Callback that is called when an activity is blocked from
171      *   launching.
172      * @param secureWindowCallback Callback that is called when a secure window shows on the
173      *   virtual display.
174      * @param intentListenerCallback Callback that is called to intercept intents when matching
175      *   passed in filters.
176      * @param showTasksInHostDeviceRecents whether to show activities in recents on the host device.
177      * @param customHomeComponent The component acting as a home activity on the virtual display. If
178      *   {@code null}, then the system-default secondary home activity will be used. This is only
179      *   applicable to displays that support home activities, i.e. they're created with the relevant
180      *   virtual display flag.
181      */
GenericWindowPolicyController( int windowFlags, int systemWindowFlags, AttributionSource attributionSource, @NonNull ArraySet<UserHandle> allowedUsers, boolean activityLaunchAllowedByDefault, @NonNull Set<ComponentName> activityPolicyExemptions, boolean crossTaskNavigationAllowedByDefault, @NonNull Set<ComponentName> crossTaskNavigationExemptions, @Nullable ComponentName permissionDialogComponent, @Nullable ActivityListener activityListener, @Nullable PipBlockedCallback pipBlockedCallback, @Nullable ActivityBlockedCallback activityBlockedCallback, @Nullable SecureWindowCallback secureWindowCallback, @Nullable IntentListenerCallback intentListenerCallback, @NonNull Set<String> displayCategories, boolean showTasksInHostDeviceRecents, @Nullable ComponentName customHomeComponent)182     public GenericWindowPolicyController(
183             int windowFlags,
184             int systemWindowFlags,
185             AttributionSource attributionSource,
186             @NonNull ArraySet<UserHandle> allowedUsers,
187             boolean activityLaunchAllowedByDefault,
188             @NonNull Set<ComponentName> activityPolicyExemptions,
189             boolean crossTaskNavigationAllowedByDefault,
190             @NonNull Set<ComponentName> crossTaskNavigationExemptions,
191             @Nullable ComponentName permissionDialogComponent,
192             @Nullable ActivityListener activityListener,
193             @Nullable PipBlockedCallback pipBlockedCallback,
194             @Nullable ActivityBlockedCallback activityBlockedCallback,
195             @Nullable SecureWindowCallback secureWindowCallback,
196             @Nullable IntentListenerCallback intentListenerCallback,
197             @NonNull Set<String> displayCategories,
198             boolean showTasksInHostDeviceRecents,
199             @Nullable ComponentName customHomeComponent) {
200         super();
201         mAttributionSource = attributionSource;
202         mAllowedUsers = allowedUsers;
203         mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault;
204         mActivityPolicyExemptions = activityPolicyExemptions;
205         mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault;
206         mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions);
207         mPermissionDialogComponent = permissionDialogComponent;
208         mActivityBlockedCallback = activityBlockedCallback;
209         setInterestedWindowFlags(windowFlags, systemWindowFlags);
210         mActivityListener = activityListener;
211         mPipBlockedCallback = pipBlockedCallback;
212         mSecureWindowCallback = secureWindowCallback;
213         mIntentListenerCallback = intentListenerCallback;
214         mDisplayCategories = displayCategories;
215         mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents;
216         mCustomHomeComponent = customHomeComponent;
217     }
218 
219     /**
220      * Expected to be called once this object is associated with a newly created display.
221      */
setDisplayId(int displayId, boolean isMirrorDisplay)222     void setDisplayId(int displayId, boolean isMirrorDisplay) {
223         mDisplayId = displayId;
224         mIsMirrorDisplay = isMirrorDisplay;
225         mDisplayIdSetLatch.countDown();
226     }
227 
waitAndGetDisplayId()228     private int waitAndGetDisplayId() {
229         try {
230             if (!mDisplayIdSetLatch.await(10, TimeUnit.SECONDS)) {
231                 Slog.e(TAG, "Timed out while waiting for GWPC displayId to be set.");
232                 return INVALID_DISPLAY;
233             }
234         } catch (InterruptedException e) {
235             Slog.e(TAG, "Interrupted while waiting for GWPC displayId to be set.");
236             return INVALID_DISPLAY;
237         }
238         return mDisplayId;
239     }
240 
waitAndGetIsMirrorDisplay()241     private boolean waitAndGetIsMirrorDisplay() {
242         try {
243             if (!mDisplayIdSetLatch.await(10, TimeUnit.SECONDS)) {
244                 Slog.e(TAG, "Timed out while waiting for GWPC isMirrorDisplay to be set.");
245                 return false;
246             }
247         } catch (InterruptedException e) {
248             Slog.e(TAG, "Interrupted while waiting for GWPC isMirrorDisplay to be set.");
249             return false;
250         }
251         return mIsMirrorDisplay;
252     }
253 
254     /**
255      * Set whether to show activities in recents on the host device.
256      */
setShowInHostDeviceRecents(boolean showInHostDeviceRecents)257     public void setShowInHostDeviceRecents(boolean showInHostDeviceRecents) {
258         synchronized (mGenericWindowPolicyControllerLock) {
259             mShowTasksInHostDeviceRecents = showInHostDeviceRecents;
260         }
261     }
262 
setActivityLaunchDefaultAllowed(boolean activityLaunchDefaultAllowed)263     void setActivityLaunchDefaultAllowed(boolean activityLaunchDefaultAllowed) {
264         synchronized (mGenericWindowPolicyControllerLock) {
265             if (mActivityLaunchAllowedByDefault != activityLaunchDefaultAllowed) {
266                 mActivityPolicyExemptions.clear();
267             }
268             mActivityLaunchAllowedByDefault = activityLaunchDefaultAllowed;
269         }
270     }
271 
addActivityPolicyExemption(@onNull ComponentName componentName)272     void addActivityPolicyExemption(@NonNull ComponentName componentName) {
273         synchronized (mGenericWindowPolicyControllerLock) {
274             mActivityPolicyExemptions.add(componentName);
275         }
276     }
277 
removeActivityPolicyExemption(@onNull ComponentName componentName)278     void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
279         synchronized (mGenericWindowPolicyControllerLock) {
280             mActivityPolicyExemptions.remove(componentName);
281         }
282     }
283 
284     /** Register a listener for running applications changes. */
registerRunningAppsChangedListener(@onNull RunningAppsChangedListener listener)285     public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
286         synchronized (mGenericWindowPolicyControllerLock) {
287             mRunningAppsChangedListeners.add(listener);
288         }
289     }
290 
291     /** Unregister a listener for running applications changes. */
unregisterRunningAppsChangedListener(@onNull RunningAppsChangedListener listener)292     public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
293         synchronized (mGenericWindowPolicyControllerLock) {
294             mRunningAppsChangedListeners.remove(listener);
295         }
296     }
297 
298     @Override
canActivityBeLaunched(@onNull ActivityInfo activityInfo, @Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, boolean isNewTask)299     public boolean canActivityBeLaunched(@NonNull ActivityInfo activityInfo,
300             @Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
301             int launchingFromDisplayId, boolean isNewTask) {
302         if (Flags.interceptIntentsBeforeApplyingPolicy()) {
303             if (mIntentListenerCallback != null && intent != null
304                     && mIntentListenerCallback.shouldInterceptIntent(intent)) {
305                 logActivityLaunchBlocked("Virtual device intercepting intent");
306                 return false;
307             }
308             if (!canContainActivity(activityInfo, windowingMode, launchingFromDisplayId,
309                     isNewTask)) {
310                 notifyActivityBlocked(activityInfo);
311                 return false;
312             }
313         } else {
314             if (!canContainActivity(activityInfo, windowingMode, launchingFromDisplayId,
315                     isNewTask)) {
316                 notifyActivityBlocked(activityInfo);
317                 return false;
318             }
319             if (mIntentListenerCallback != null && intent != null
320                     && mIntentListenerCallback.shouldInterceptIntent(intent)) {
321                 logActivityLaunchBlocked("Virtual device intercepting intent");
322                 return false;
323             }
324         }
325         return true;
326     }
327 
328     @Override
canContainActivity(@onNull ActivityInfo activityInfo, @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, boolean isNewTask)329     public boolean canContainActivity(@NonNull ActivityInfo activityInfo,
330             @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
331             boolean isNewTask) {
332         // Mirror displays cannot contain activities.
333         if (waitAndGetIsMirrorDisplay()) {
334             logActivityLaunchBlocked("Mirror virtual displays cannot contain activities.");
335             return false;
336         }
337         if (!isWindowingModeSupported(windowingMode)) {
338             logActivityLaunchBlocked(
339                     "Virtual device doesn't support windowing mode " + windowingMode);
340             return false;
341         }
342         if ((activityInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
343             logActivityLaunchBlocked(
344                     "Activity requires android:canDisplayOnRemoteDevices=true");
345             return false;
346         }
347         final UserHandle activityUser =
348                 UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid);
349         final ComponentName activityComponent = activityInfo.getComponentName();
350         if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent) && activityUser.isSystem()) {
351             // The error dialog alerting users that streaming is blocked is always allowed.
352             return true;
353         }
354         if (!activityUser.isSystem() && !mAllowedUsers.contains(activityUser)) {
355             logActivityLaunchBlocked("Activity launch disallowed from user " + activityUser);
356             return false;
357         }
358         if (!activityMatchesDisplayCategory(activityInfo)) {
359             logActivityLaunchBlocked("The activity's required display category '"
360                     + activityInfo.requiredDisplayCategory
361                     + "' not found on virtual display with the following categories: "
362                     + mDisplayCategories);
363             return false;
364         }
365         synchronized (mGenericWindowPolicyControllerLock) {
366             if (!isAllowedByPolicy(mActivityLaunchAllowedByDefault, mActivityPolicyExemptions,
367                     activityComponent)) {
368                 logActivityLaunchBlocked("Activity launch disallowed by policy: "
369                         + activityComponent);
370                 return false;
371             }
372         }
373         if (isNewTask && launchingFromDisplayId != DEFAULT_DISPLAY
374                 && !isAllowedByPolicy(mCrossTaskNavigationAllowedByDefault,
375                         mCrossTaskNavigationExemptions, activityComponent)) {
376             logActivityLaunchBlocked("Cross task navigation disallowed by policy: "
377                     + activityComponent);
378             return false;
379         }
380 
381         // mPermissionDialogComponent being null means we don't want to block permission Dialogs
382         // based on FLAG_STREAM_PERMISSIONS
383         if (mPermissionDialogComponent != null
384                 && mPermissionDialogComponent.equals(activityComponent)) {
385             logActivityLaunchBlocked("Permission dialog not allowed on virtual device");
386             return false;
387         }
388 
389         return true;
390     }
391 
logActivityLaunchBlocked(String reason)392     private void logActivityLaunchBlocked(String reason) {
393         Slog.d(TAG, "Virtual device activity launch disallowed on display "
394                 + waitAndGetDisplayId() + ", reason: " + reason);
395     }
396 
397     @Override
398     @SuppressWarnings("AndroidFrameworkRequiresPermission")
keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags, int systemWindowFlags)399     public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
400             int systemWindowFlags) {
401         int displayId = waitAndGetDisplayId();
402         // The callback is fired only when windowFlags are changed. To let VirtualDevice owner
403         // aware that the virtual display has a secure window on top.
404         if ((windowFlags & FLAG_SECURE) != 0 && mSecureWindowCallback != null
405                 && displayId != INVALID_DISPLAY) {
406             // Post callback on the main thread, so it doesn't block activity launching.
407             mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(displayId,
408                     activityInfo.applicationInfo.uid));
409         }
410 
411         if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
412                 activityInfo.packageName,
413                 UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid))) {
414             // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
415             if ((windowFlags & FLAG_SECURE) != 0
416                     || (systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
417                 notifyActivityBlocked(activityInfo);
418                 return false;
419             }
420         }
421 
422         return true;
423     }
424 
425     @Override
onTopActivityChanged(ComponentName topActivity, int uid, @UserIdInt int userId)426     public void onTopActivityChanged(ComponentName topActivity, int uid, @UserIdInt int userId) {
427         int displayId = waitAndGetDisplayId();
428         // Don't send onTopActivityChanged() callback when topActivity is null because it's defined
429         // as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when
430         // there is no activity running on virtual display.
431         if (mActivityListener != null && topActivity != null && displayId != INVALID_DISPLAY) {
432             // Post callback on the main thread so it doesn't block activity launching
433             mHandler.post(() ->
434                     mActivityListener.onTopActivityChanged(displayId, topActivity, userId));
435         }
436     }
437 
438     @Override
onRunningAppsChanged(ArraySet<Integer> runningUids)439     public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
440         synchronized (mGenericWindowPolicyControllerLock) {
441             mRunningUids.clear();
442             mRunningUids.addAll(runningUids);
443             int displayId = waitAndGetDisplayId();
444             if (mActivityListener != null && mRunningUids.isEmpty()
445                     && displayId != INVALID_DISPLAY) {
446                 // Post callback on the main thread so it doesn't block activity launching
447                 mHandler.post(() -> mActivityListener.onDisplayEmpty(displayId));
448             }
449             if (!mRunningAppsChangedListeners.isEmpty()) {
450                 final ArraySet<RunningAppsChangedListener> listeners =
451                         new ArraySet<>(mRunningAppsChangedListeners);
452                 mHandler.post(() -> {
453                     for (RunningAppsChangedListener listener : listeners) {
454                         listener.onRunningAppsChanged(runningUids);
455                     }
456                 });
457             }
458         }
459     }
460 
461     @Override
canShowTasksInHostDeviceRecents()462     public boolean canShowTasksInHostDeviceRecents() {
463         synchronized (mGenericWindowPolicyControllerLock) {
464             return mShowTasksInHostDeviceRecents;
465         }
466     }
467 
468     @Override
isEnteringPipAllowed(int uid)469     public boolean isEnteringPipAllowed(int uid) {
470         if (super.isEnteringPipAllowed(uid)) {
471             return true;
472         }
473         if (mPipBlockedCallback != null) {
474             mHandler.post(() -> mPipBlockedCallback.onEnteringPipBlocked(uid));
475         }
476         return false;
477     }
478 
479     @Override
getCustomHomeComponent()480     public @Nullable ComponentName getCustomHomeComponent() {
481         return mCustomHomeComponent;
482     }
483 
484     /**
485      * Returns true if an app with the given UID has an activity running on the virtual display for
486      * this controller.
487      */
containsUid(int uid)488     boolean containsUid(int uid) {
489         synchronized (mGenericWindowPolicyControllerLock) {
490             return mRunningUids.contains(uid);
491         }
492     }
493 
activityMatchesDisplayCategory(ActivityInfo activityInfo)494     private boolean activityMatchesDisplayCategory(ActivityInfo activityInfo) {
495         if (mDisplayCategories.isEmpty()) {
496             return activityInfo.requiredDisplayCategory == null;
497         }
498         return activityInfo.requiredDisplayCategory != null
499                     && mDisplayCategories.contains(activityInfo.requiredDisplayCategory);
500     }
501 
notifyActivityBlocked(ActivityInfo activityInfo)502     private void notifyActivityBlocked(ActivityInfo activityInfo) {
503         int displayId = waitAndGetDisplayId();
504         // Don't trigger activity blocked callback for mirror displays, because we can't show
505         // any activity or presentation on it anyway.
506         if (!waitAndGetIsMirrorDisplay() && mActivityBlockedCallback != null
507                 && displayId != INVALID_DISPLAY) {
508             mActivityBlockedCallback.onActivityBlocked(displayId, activityInfo);
509         }
510         if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
511             Counter.logIncrementWithUid(
512                     "virtual_devices.value_activity_blocked_count",
513                     mAttributionSource.getUid());
514         }
515 
516     }
517 
isAllowedByPolicy(boolean allowedByDefault, Set<ComponentName> exemptions, ComponentName component)518     private static boolean isAllowedByPolicy(boolean allowedByDefault,
519             Set<ComponentName> exemptions, ComponentName component) {
520         // Either allowed and the exemptions do not contain the component,
521         // or disallowed and the exemptions contain the component.
522         return allowedByDefault != exemptions.contains(component);
523     }
524 
525     @VisibleForTesting
getRunningAppsChangedListenersSizeForTesting()526     int getRunningAppsChangedListenersSizeForTesting() {
527         synchronized (mGenericWindowPolicyControllerLock) {
528             return mRunningAppsChangedListeners.size();
529         }
530     }
531 }
532