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