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