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