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.WindowManager.LayoutParams.FLAG_SECURE; 21 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.UserIdInt; 26 import android.app.WindowConfiguration; 27 import android.app.compat.CompatChanges; 28 import android.companion.virtual.VirtualDeviceManager.ActivityListener; 29 import android.companion.virtual.VirtualDeviceParams; 30 import android.companion.virtual.VirtualDeviceParams.ActivityPolicy; 31 import android.compat.annotation.ChangeId; 32 import android.compat.annotation.EnabledSince; 33 import android.content.ComponentName; 34 import android.content.Intent; 35 import android.content.pm.ActivityInfo; 36 import android.os.Build; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.os.UserHandle; 40 import android.util.ArraySet; 41 import android.util.Slog; 42 import android.view.Display; 43 import android.window.DisplayWindowPolicyController; 44 45 import com.android.internal.annotations.GuardedBy; 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.internal.app.BlockedAppStreamingActivity; 48 49 import java.util.List; 50 import java.util.Set; 51 52 53 /** 54 * A controller to control the policies of the windows that can be displayed on the virtual display. 55 */ 56 public class GenericWindowPolicyController extends DisplayWindowPolicyController { 57 58 private static final String TAG = "GenericWindowPolicyController"; 59 60 /** Interface to listen running applications change on virtual display. */ 61 public interface RunningAppsChangedListener { 62 /** 63 * Notifies the running applications change. 64 */ onRunningAppsChanged(ArraySet<Integer> runningUids)65 void onRunningAppsChanged(ArraySet<Integer> runningUids); 66 } 67 68 /** 69 * For communicating when activities are blocked from running on the display by this policy 70 * controller. 71 */ 72 public interface ActivityBlockedCallback { 73 /** Called when an activity is blocked.*/ onActivityBlocked(int displayId, ActivityInfo activityInfo)74 void onActivityBlocked(int displayId, ActivityInfo activityInfo); 75 } 76 private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = 77 new ComponentName("android", BlockedAppStreamingActivity.class.getName()); 78 79 /** 80 * For communicating when a secure window shows on the virtual display. 81 */ 82 public interface SecureWindowCallback { 83 /** Called when a secure window shows on the virtual display. */ onSecureWindowShown(int displayId, int uid)84 void onSecureWindowShown(int displayId, int uid); 85 } 86 87 /** 88 * For communicating when activities are blocked from entering PIP on the display by this 89 * policy controller. 90 */ 91 public interface PipBlockedCallback { 92 /** Called when an activity is blocked from entering PIP. */ onEnteringPipBlocked(int uid)93 void onEnteringPipBlocked(int uid); 94 } 95 96 /** Interface to listen for interception of intents. */ 97 public interface IntentListenerCallback { 98 /** Returns true when an intent should be intercepted */ shouldInterceptIntent(Intent intent)99 boolean shouldInterceptIntent(Intent intent); 100 } 101 102 /** 103 * If required, allow the secure activity to display on remote device since 104 * {@link android.os.Build.VERSION_CODES#TIRAMISU}. 105 */ 106 @ChangeId 107 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 108 public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L; 109 @NonNull 110 private final ArraySet<UserHandle> mAllowedUsers; 111 @Nullable 112 private final ArraySet<ComponentName> mAllowedCrossTaskNavigations; 113 @Nullable 114 private final ArraySet<ComponentName> mBlockedCrossTaskNavigations; 115 @Nullable 116 private final ArraySet<ComponentName> mAllowedActivities; 117 @Nullable 118 private final ArraySet<ComponentName> mBlockedActivities; 119 private final Object mGenericWindowPolicyControllerLock = new Object(); 120 @ActivityPolicy 121 private final int mDefaultActivityPolicy; 122 private final ActivityBlockedCallback mActivityBlockedCallback; 123 private int mDisplayId = Display.INVALID_DISPLAY; 124 125 @NonNull 126 @GuardedBy("mGenericWindowPolicyControllerLock") 127 final ArraySet<Integer> mRunningUids = new ArraySet<>(); 128 @Nullable private final ActivityListener mActivityListener; 129 @Nullable private final PipBlockedCallback mPipBlockedCallback; 130 @Nullable private final IntentListenerCallback mIntentListenerCallback; 131 private final Handler mHandler = new Handler(Looper.getMainLooper()); 132 @NonNull 133 @GuardedBy("mGenericWindowPolicyControllerLock") 134 private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners = 135 new ArraySet<>(); 136 @Nullable private final SecureWindowCallback mSecureWindowCallback; 137 @Nullable private final Set<String> mDisplayCategories; 138 139 private final boolean mShowTasksInHostDeviceRecents; 140 141 /** 142 * Creates a window policy controller that is generic to the different use cases of virtual 143 * device. 144 * 145 * @param windowFlags The window flags that this controller is interested in. 146 * @param systemWindowFlags The system window flags that this controller is interested in. 147 * @param allowedUsers The set of users that are allowed to stream in this display. 148 * @param allowedCrossTaskNavigations The set of components explicitly allowed to navigate 149 * across tasks on this device. 150 * @param blockedCrossTaskNavigations The set of components explicitly blocked from 151 * navigating across tasks on this device. 152 * @param allowedActivities The set of activities explicitly allowed to stream on this device. 153 * Used only if the {@code activityPolicy} is 154 * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_BLOCKED}. 155 * @param blockedActivities The set of activities explicitly blocked from streaming on this 156 * device. Used only if the {@code activityPolicy} is 157 * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_ALLOWED} 158 * @param defaultActivityPolicy Whether activities are default allowed to be displayed or 159 * blocked. 160 * @param activityListener Activity listener to listen for activity changes. 161 * @param activityBlockedCallback Callback that is called when an activity is blocked from 162 * launching. 163 * @param secureWindowCallback Callback that is called when a secure window shows on the 164 * virtual display. 165 * @param intentListenerCallback Callback that is called to intercept intents when matching 166 * passed in filters. 167 * @param showTasksInHostDeviceRecents whether to show activities in recents on the host device. 168 */ GenericWindowPolicyController(int windowFlags, int systemWindowFlags, @NonNull ArraySet<UserHandle> allowedUsers, @NonNull Set<ComponentName> allowedCrossTaskNavigations, @NonNull Set<ComponentName> blockedCrossTaskNavigations, @NonNull Set<ComponentName> allowedActivities, @NonNull Set<ComponentName> blockedActivities, @ActivityPolicy int defaultActivityPolicy, @NonNull ActivityListener activityListener, @NonNull PipBlockedCallback pipBlockedCallback, @NonNull ActivityBlockedCallback activityBlockedCallback, @NonNull SecureWindowCallback secureWindowCallback, @NonNull IntentListenerCallback intentListenerCallback, @NonNull Set<String> displayCategories, boolean showTasksInHostDeviceRecents)169 public GenericWindowPolicyController(int windowFlags, int systemWindowFlags, 170 @NonNull ArraySet<UserHandle> allowedUsers, 171 @NonNull Set<ComponentName> allowedCrossTaskNavigations, 172 @NonNull Set<ComponentName> blockedCrossTaskNavigations, 173 @NonNull Set<ComponentName> allowedActivities, 174 @NonNull Set<ComponentName> blockedActivities, 175 @ActivityPolicy int defaultActivityPolicy, 176 @NonNull ActivityListener activityListener, 177 @NonNull PipBlockedCallback pipBlockedCallback, 178 @NonNull ActivityBlockedCallback activityBlockedCallback, 179 @NonNull SecureWindowCallback secureWindowCallback, 180 @NonNull IntentListenerCallback intentListenerCallback, 181 @NonNull Set<String> displayCategories, 182 boolean showTasksInHostDeviceRecents) { 183 super(); 184 mAllowedUsers = allowedUsers; 185 mAllowedCrossTaskNavigations = new ArraySet<>(allowedCrossTaskNavigations); 186 mBlockedCrossTaskNavigations = new ArraySet<>(blockedCrossTaskNavigations); 187 mAllowedActivities = new ArraySet<>(allowedActivities); 188 mBlockedActivities = new ArraySet<>(blockedActivities); 189 mDefaultActivityPolicy = defaultActivityPolicy; 190 mActivityBlockedCallback = activityBlockedCallback; 191 setInterestedWindowFlags(windowFlags, systemWindowFlags); 192 mActivityListener = activityListener; 193 mPipBlockedCallback = pipBlockedCallback; 194 mSecureWindowCallback = secureWindowCallback; 195 mIntentListenerCallback = intentListenerCallback; 196 mDisplayCategories = displayCategories; 197 mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents; 198 } 199 200 /** 201 * Expected to be called once this object is associated with a newly created display. 202 */ setDisplayId(int displayId)203 public void setDisplayId(int displayId) { 204 mDisplayId = displayId; 205 } 206 207 /** Register a listener for running applications changes. */ registerRunningAppsChangedListener(@onNull RunningAppsChangedListener listener)208 public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { 209 synchronized (mGenericWindowPolicyControllerLock) { 210 mRunningAppsChangedListeners.add(listener); 211 } 212 } 213 214 /** Unregister a listener for running applications changes. */ unregisterRunningAppsChangedListener(@onNull RunningAppsChangedListener listener)215 public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { 216 synchronized (mGenericWindowPolicyControllerLock) { 217 mRunningAppsChangedListeners.remove(listener); 218 } 219 } 220 221 @Override canContainActivities(@onNull List<ActivityInfo> activities, @WindowConfiguration.WindowingMode int windowingMode)222 public boolean canContainActivities(@NonNull List<ActivityInfo> activities, 223 @WindowConfiguration.WindowingMode int windowingMode) { 224 if (!isWindowingModeSupported(windowingMode)) { 225 return false; 226 } 227 // Can't display all the activities if any of them don't want to be displayed. 228 final int activityCount = activities.size(); 229 for (int i = 0; i < activityCount; i++) { 230 final ActivityInfo aInfo = activities.get(i); 231 if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) { 232 mActivityBlockedCallback.onActivityBlocked(mDisplayId, aInfo); 233 return false; 234 } 235 } 236 return true; 237 } 238 239 @Override canActivityBeLaunched(ActivityInfo activityInfo, Intent intent, @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, boolean isNewTask)240 public boolean canActivityBeLaunched(ActivityInfo activityInfo, 241 Intent intent, @WindowConfiguration.WindowingMode int windowingMode, 242 int launchingFromDisplayId, boolean isNewTask) { 243 if (!isWindowingModeSupported(windowingMode)) { 244 return false; 245 } 246 247 final ComponentName activityComponent = activityInfo.getComponentName(); 248 if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent)) { 249 // The error dialog alerting users that streaming is blocked is always allowed. 250 return true; 251 } 252 253 if (!canContainActivity(activityInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) { 254 mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); 255 return false; 256 } 257 258 if (launchingFromDisplayId == Display.DEFAULT_DISPLAY) { 259 return true; 260 } 261 if (isNewTask && !mBlockedCrossTaskNavigations.isEmpty() 262 && mBlockedCrossTaskNavigations.contains(activityComponent)) { 263 Slog.d(TAG, "Virtual device blocking cross task navigation of " + activityComponent); 264 mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); 265 return false; 266 } 267 if (isNewTask && !mAllowedCrossTaskNavigations.isEmpty() 268 && !mAllowedCrossTaskNavigations.contains(activityComponent)) { 269 Slog.d(TAG, "Virtual device not allowing cross task navigation of " 270 + activityComponent); 271 mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); 272 return false; 273 } 274 275 if (mIntentListenerCallback != null && intent != null 276 && mIntentListenerCallback.shouldInterceptIntent(intent)) { 277 Slog.d(TAG, "Virtual device has intercepted intent"); 278 return false; 279 } 280 281 return true; 282 } 283 284 285 @Override keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags, int systemWindowFlags)286 public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags, 287 int systemWindowFlags) { 288 // The callback is fired only when windowFlags are changed. To let VirtualDevice owner 289 // aware that the virtual display has a secure window on top. 290 if ((windowFlags & FLAG_SECURE) != 0) { 291 // Post callback on the main thread, so it doesn't block activity launching. 292 mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(mDisplayId, 293 activityInfo.applicationInfo.uid)); 294 } 295 296 if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) { 297 mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); 298 return false; 299 } 300 return true; 301 } 302 303 @Override onTopActivityChanged(ComponentName topActivity, int uid, @UserIdInt int userId)304 public void onTopActivityChanged(ComponentName topActivity, int uid, @UserIdInt int userId) { 305 // Don't send onTopActivityChanged() callback when topActivity is null because it's defined 306 // as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when 307 // there is no activity running on virtual display. 308 if (mActivityListener != null && topActivity != null) { 309 // Post callback on the main thread so it doesn't block activity launching 310 mHandler.post(() -> 311 mActivityListener.onTopActivityChanged(mDisplayId, topActivity, userId)); 312 } 313 } 314 315 @Override onRunningAppsChanged(ArraySet<Integer> runningUids)316 public void onRunningAppsChanged(ArraySet<Integer> runningUids) { 317 synchronized (mGenericWindowPolicyControllerLock) { 318 mRunningUids.clear(); 319 mRunningUids.addAll(runningUids); 320 if (mActivityListener != null && mRunningUids.isEmpty()) { 321 // Post callback on the main thread so it doesn't block activity launching 322 mHandler.post(() -> mActivityListener.onDisplayEmpty(mDisplayId)); 323 } 324 if (!mRunningAppsChangedListeners.isEmpty()) { 325 final ArraySet<RunningAppsChangedListener> listeners = 326 new ArraySet<>(mRunningAppsChangedListeners); 327 mHandler.post(() -> { 328 for (RunningAppsChangedListener listener : listeners) { 329 listener.onRunningAppsChanged(runningUids); 330 } 331 }); 332 } 333 } 334 } 335 336 @Override canShowTasksInHostDeviceRecents()337 public boolean canShowTasksInHostDeviceRecents() { 338 return mShowTasksInHostDeviceRecents; 339 } 340 341 @Override isEnteringPipAllowed(int uid)342 public boolean isEnteringPipAllowed(int uid) { 343 if (super.isEnteringPipAllowed(uid)) { 344 return true; 345 } 346 mHandler.post(() -> { 347 mPipBlockedCallback.onEnteringPipBlocked(uid); 348 }); 349 return false; 350 } 351 352 /** 353 * Returns true if an app with the given UID has an activity running on the virtual display for 354 * this controller. 355 */ containsUid(int uid)356 boolean containsUid(int uid) { 357 synchronized (mGenericWindowPolicyControllerLock) { 358 return mRunningUids.contains(uid); 359 } 360 } 361 activityMatchesDisplayCategory(ActivityInfo activityInfo)362 private boolean activityMatchesDisplayCategory(ActivityInfo activityInfo) { 363 if (mDisplayCategories.isEmpty()) { 364 return activityInfo.requiredDisplayCategory == null; 365 } 366 return activityInfo.requiredDisplayCategory != null 367 && mDisplayCategories.contains(activityInfo.requiredDisplayCategory); 368 369 } 370 canContainActivity(ActivityInfo activityInfo, int windowFlags, int systemWindowFlags)371 private boolean canContainActivity(ActivityInfo activityInfo, int windowFlags, 372 int systemWindowFlags) { 373 if ((activityInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) { 374 return false; 375 } 376 ComponentName activityComponent = activityInfo.getComponentName(); 377 if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent)) { 378 // The error dialog alerting users that streaming is blocked is always allowed. Need to 379 // run before the clauses below to ensure error dialog always shows up. 380 return true; 381 } 382 if (!activityMatchesDisplayCategory(activityInfo)) { 383 Slog.d(TAG, String.format( 384 "The activity's required display category: %s is not found on virtual display" 385 + " with the following categories: %s", 386 activityInfo.requiredDisplayCategory, mDisplayCategories.toString())); 387 return false; 388 } 389 final UserHandle activityUser = 390 UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid); 391 if (!mAllowedUsers.contains(activityUser)) { 392 Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser); 393 return false; 394 } 395 if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED 396 && mBlockedActivities.contains(activityComponent)) { 397 Slog.d(TAG, "Virtual device blocking launch of " + activityComponent); 398 return false; 399 } 400 if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_BLOCKED 401 && !mAllowedActivities.contains(activityComponent)) { 402 Slog.d(TAG, activityComponent + " is not in the allowed list."); 403 return false; 404 } 405 if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE, 406 activityInfo.packageName, activityUser)) { 407 // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure. 408 if ((windowFlags & FLAG_SECURE) != 0) { 409 return false; 410 } 411 if ((systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) { 412 return false; 413 } 414 } 415 return true; 416 } 417 418 @VisibleForTesting getRunningAppsChangedListenersSizeForTesting()419 int getRunningAppsChangedListenersSizeForTesting() { 420 synchronized (mGenericWindowPolicyControllerLock) { 421 return mRunningAppsChangedListeners.size(); 422 } 423 } 424 } 425