1 /*
2  * Copyright (C) 2018 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.wm;
18 
19 import static android.Manifest.permission.CONTROL_KEYGUARD;
20 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
21 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
22 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
23 import static android.Manifest.permission.STATUS_BAR_SERVICE;
24 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
25 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
26 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
27 import static android.app.WindowConfiguration.activityTypeToString;
28 import static android.content.pm.PackageManager.PERMISSION_DENIED;
29 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
30 import static android.view.Display.DEFAULT_DISPLAY;
31 import static android.view.Display.INVALID_DISPLAY;
32 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
33 
34 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
35 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
36 
37 import android.annotation.Nullable;
38 import android.app.ActivityOptions;
39 import android.app.AppGlobals;
40 import android.app.PendingIntent;
41 import android.content.Intent;
42 import android.content.pm.ActivityInfo;
43 import android.content.pm.PackageManager;
44 import android.os.Binder;
45 import android.os.Bundle;
46 import android.os.Process;
47 import android.os.RemoteException;
48 import android.os.UserHandle;
49 import android.util.Slog;
50 import android.view.RemoteAnimationAdapter;
51 import android.window.RemoteTransition;
52 import android.window.WindowContainerToken;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 
56 /**
57  * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving
58  * the inner options. Also supports having two set of options: Once from the original caller, and
59  * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
60  */
61 public class SafeActivityOptions {
62 
63     private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM;
64 
65     private final int mOriginalCallingPid;
66     private final int mOriginalCallingUid;
67     private int mRealCallingPid;
68     private int mRealCallingUid;
69     private final @Nullable ActivityOptions mOriginalOptions;
70     private @Nullable ActivityOptions mCallerOptions;
71 
72     /**
73      * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/
74      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
75      * this object.
76      *
77      * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
78      */
fromBundle(Bundle bOptions)79     public static SafeActivityOptions fromBundle(Bundle bOptions) {
80         return bOptions != null
81                 ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions))
82                 : null;
83     }
84 
85     /**
86      * Constructs a new instance from a bundle and provided pid/uid.
87      *
88      * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
89      */
fromBundle(Bundle bOptions, int callingPid, int callingUid)90     static SafeActivityOptions fromBundle(Bundle bOptions, int callingPid, int callingUid) {
91         return bOptions != null
92                 ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions),
93                         callingPid, callingUid)
94                 : null;
95     }
96 
97     /**
98      * Constructs a new instance and records {@link Binder#getCallingPid}/
99      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
100      * this object.
101      *
102      * @param options The options to wrap.
103      */
SafeActivityOptions(@ullable ActivityOptions options)104     public SafeActivityOptions(@Nullable ActivityOptions options) {
105         mOriginalCallingPid = Binder.getCallingPid();
106         mOriginalCallingUid = Binder.getCallingUid();
107         mOriginalOptions = options;
108     }
109 
110     /**
111      * Constructs a new instance.
112      *
113      * @param options The options to wrap.
114      */
SafeActivityOptions(@ullable ActivityOptions options, int callingPid, int callingUid)115     private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
116         mOriginalCallingPid = callingPid;
117         mOriginalCallingUid = callingUid;
118         mOriginalOptions = options;
119     }
120 
121     /**
122      * To ensure that two activities, one using this object, and the other using the
123      * SafeActivityOptions returned from this function, are launched into the same display/root task
124      * through ActivityStartController#startActivities, all display-related information, i.e.
125      * displayAreaToken, launchDisplayId, callerDisplayId and the launch root task are cloned.
126      */
selectiveCloneLaunchOptions()127     @Nullable SafeActivityOptions selectiveCloneLaunchOptions() {
128         final ActivityOptions options = cloneLaunchingOptions(mOriginalOptions);
129         final ActivityOptions callerOptions = cloneLaunchingOptions(mCallerOptions);
130         if (options == null && callerOptions == null) {
131             return null;
132         }
133 
134         final SafeActivityOptions safeOptions = new SafeActivityOptions(options,
135                 mOriginalCallingPid, mOriginalCallingUid);
136         safeOptions.mCallerOptions = callerOptions;
137         safeOptions.mRealCallingPid = mRealCallingPid;
138         safeOptions.mRealCallingUid = mRealCallingUid;
139         return safeOptions;
140     }
141 
cloneLaunchingOptions(ActivityOptions options)142     private ActivityOptions cloneLaunchingOptions(ActivityOptions options) {
143         return options == null ? null : ActivityOptions.makeBasic()
144                 .setLaunchTaskDisplayArea(options.getLaunchTaskDisplayArea())
145                 .setLaunchDisplayId(options.getLaunchDisplayId())
146                 .setCallerDisplayId(options.getCallerDisplayId())
147                 .setLaunchRootTask(options.getLaunchRootTask())
148                 .setPendingIntentBackgroundActivityStartMode(
149                         options.getPendingIntentBackgroundActivityStartMode())
150                 .setPendingIntentCreatorBackgroundActivityStartMode(
151                         options.getPendingIntentCreatorBackgroundActivityStartMode())
152                 .setRemoteTransition(options.getRemoteTransition());
153     }
154 
155     /**
156      * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
157      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
158      * method.
159      */
setCallerOptions(@ullable ActivityOptions options)160     public void setCallerOptions(@Nullable ActivityOptions options) {
161         mRealCallingPid = Binder.getCallingPid();
162         mRealCallingUid = Binder.getCallingUid();
163         mCallerOptions = options;
164     }
165 
166     /**
167      * Performs permission check and retrieves the options.
168      *
169      * @param r The record of the being started activity.
170      */
getOptions(ActivityRecord r)171     ActivityOptions getOptions(ActivityRecord r) throws SecurityException {
172         return getOptions(r.intent, r.info, r.app, r.mTaskSupervisor);
173     }
174 
175     /**
176      * Performs permission check and retrieves the options when options are not being used to launch
177      * a specific activity (i.e. a task is moved to front).
178      */
getOptions(ActivityTaskSupervisor supervisor)179     ActivityOptions getOptions(ActivityTaskSupervisor supervisor) throws SecurityException {
180         return getOptions(null, null, null, supervisor);
181     }
182 
183     /**
184      * Performs permission check and retrieves the options.
185      *
186      * @param intent The intent that is being launched.
187      * @param aInfo The info of the activity being launched.
188      * @param callerApp The record of the caller.
189      */
getOptions(@ullable Intent intent, @Nullable ActivityInfo aInfo, @Nullable WindowProcessController callerApp, ActivityTaskSupervisor supervisor)190     ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
191             @Nullable WindowProcessController callerApp,
192             ActivityTaskSupervisor supervisor) throws SecurityException {
193         if (mOriginalOptions != null) {
194             checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
195                     mOriginalCallingPid, mOriginalCallingUid);
196             setCallingPidUidForRemoteAnimationAdapter(mOriginalOptions, mOriginalCallingPid,
197                     mOriginalCallingUid);
198         }
199         if (mCallerOptions != null) {
200             checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
201                     mRealCallingPid, mRealCallingUid);
202             setCallingPidUidForRemoteAnimationAdapter(mCallerOptions, mRealCallingPid,
203                     mRealCallingUid);
204         }
205         return mergeActivityOptions(mOriginalOptions, mCallerOptions);
206     }
207 
setCallingPidUidForRemoteAnimationAdapter(ActivityOptions options, int callingPid, int callingUid)208     private void setCallingPidUidForRemoteAnimationAdapter(ActivityOptions options,
209             int callingPid, int callingUid) {
210         final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
211         if (adapter == null) {
212             return;
213         }
214         if (callingPid == WindowManagerService.MY_PID) {
215             Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
216             return;
217         }
218         adapter.setCallingPidUid(callingPid, callingUid);
219     }
220 
221     /**
222      * Gets the original options passed in. It should only be used for logging. DO NOT use it as a
223      * condition in the logic of activity launch.
224      */
getOriginalOptions()225     ActivityOptions getOriginalOptions() {
226         return mOriginalOptions;
227     }
228 
229     /**
230      * @see ActivityOptions#popAppVerificationBundle
231      */
popAppVerificationBundle()232     Bundle popAppVerificationBundle() {
233         return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
234     }
235 
abort()236     private void abort() {
237         if (mOriginalOptions != null) {
238             ActivityOptions.abort(mOriginalOptions);
239         }
240         if (mCallerOptions != null) {
241             ActivityOptions.abort(mCallerOptions);
242         }
243     }
244 
abort(@ullable SafeActivityOptions options)245     static void abort(@Nullable SafeActivityOptions options) {
246         if (options != null) {
247             options.abort();
248         }
249     }
250 
251     /**
252      * Merges two activity options into one, with {@code options2} taking precedence in case of a
253      * conflict.
254      */
255     @VisibleForTesting
mergeActivityOptions(@ullable ActivityOptions options1, @Nullable ActivityOptions options2)256     @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
257             @Nullable ActivityOptions options2) {
258         if (options1 == null) {
259             return options2;
260         }
261         if (options2 == null) {
262             return options1;
263         }
264         final Bundle b1 = options1.toBundle();
265         final Bundle b2 = options2.toBundle();
266         b1.putAll(b2);
267         return ActivityOptions.fromBundle(b1);
268     }
269 
checkPermissions(@ullable Intent intent, @Nullable ActivityInfo aInfo, @Nullable WindowProcessController callerApp, ActivityTaskSupervisor supervisor, ActivityOptions options, int callingPid, int callingUid)270     private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
271             @Nullable WindowProcessController callerApp, ActivityTaskSupervisor supervisor,
272             ActivityOptions options, int callingPid, int callingUid) {
273         // If a launch task id is specified, then ensure that the caller is the recents
274         // component or has the START_TASKS_FROM_RECENTS permission
275         if ((options.getLaunchTaskId() != INVALID_TASK_ID || options.getDisableStartingWindow())
276                 && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
277             final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
278                     START_TASKS_FROM_RECENTS, callingPid, callingUid);
279             if (startInTaskPerm == PERMISSION_DENIED) {
280                 final String msg = "Permission Denial: starting " + getIntentString(intent)
281                         + " from " + callerApp + " (pid=" + callingPid
282                         + ", uid=" + callingUid + ") with launchTaskId="
283                         + options.getLaunchTaskId();
284                 Slog.w(TAG, msg);
285                 throw new SecurityException(msg);
286             }
287         }
288         if (options.getTransientLaunch() && !supervisor.mRecentTasks.isCallerRecents(callingUid)
289                 && ActivityTaskManagerService.checkPermission(
290                         MANAGE_ACTIVITY_TASKS, callingPid, callingUid) == PERMISSION_DENIED) {
291             final String msg = "Permission Denial: starting transient launch from " + callerApp
292                     + ", pid=" + callingPid + ", uid=" + callingUid;
293             Slog.w(TAG, msg);
294             throw new SecurityException(msg);
295         }
296         // Check if the caller is allowed to launch on the specified display area.
297         final TaskDisplayArea taskDisplayArea = getLaunchTaskDisplayArea(options, supervisor);
298         if (aInfo != null && taskDisplayArea != null
299                 && !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid,
300                 taskDisplayArea, aInfo)) {
301             final String msg = "Permission Denial: starting " + getIntentString(intent)
302                     + " from " + callerApp + " (pid=" + callingPid
303                     + ", uid=" + callingUid + ") with launchTaskDisplayArea=" + taskDisplayArea;
304             Slog.w(TAG, msg);
305             throw new SecurityException(msg);
306         }
307         // Check if the caller is allowed to launch on the specified display.
308         final int launchDisplayId = options.getLaunchDisplayId();
309         if (aInfo != null && launchDisplayId != INVALID_DISPLAY
310                 && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
311                         launchDisplayId, aInfo)) {
312             final String msg = "Permission Denial: starting " + getIntentString(intent)
313                     + " from " + callerApp + " (pid=" + callingPid
314                     + ", uid=" + callingUid + ") with launchDisplayId="
315                     + launchDisplayId;
316             Slog.w(TAG, msg);
317             throw new SecurityException(msg);
318         }
319         // Check if someone tries to launch an unallowlisted activity into LockTask mode.
320         final boolean lockTaskMode = options.getLockTaskMode();
321         if (aInfo != null && lockTaskMode
322                 && !supervisor.mService.getLockTaskController().isPackageAllowlisted(
323                         UserHandle.getUserId(callingUid), aInfo.packageName)) {
324             final String msg = "Permission Denial: starting " + getIntentString(intent)
325                     + " from " + callerApp + " (pid=" + callingPid
326                     + ", uid=" + callingUid + ") with lockTaskMode=true";
327             Slog.w(TAG, msg);
328             throw new SecurityException(msg);
329         }
330 
331         // Check if the caller is allowed to override any app transition animation.
332         final boolean overrideTaskTransition = options.getOverrideTaskTransition();
333         if (aInfo != null && overrideTaskTransition) {
334             final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission(
335                     START_TASKS_FROM_RECENTS, callingPid, callingUid);
336             // Allow if calling uid is from assistant, or start task from recents
337             if (startTasksFromRecentsPerm != PERMISSION_GRANTED
338                     && !isAssistant(supervisor.mService, callingUid)) {
339                 final String msg = "Permission Denial: starting " + getIntentString(intent)
340                         + " from " + callerApp + " (pid=" + callingPid
341                         + ", uid=" + callingUid + ") with overrideTaskTransition=true";
342                 Slog.w(TAG, msg);
343                 throw new SecurityException(msg);
344             }
345         }
346 
347         // Check if the caller is allowed to dismiss keyguard.
348         final boolean dismissKeyguardIfInsecure = options.getDismissKeyguardIfInsecure();
349         if (aInfo != null && dismissKeyguardIfInsecure) {
350             final int controlKeyguardPerm = ActivityTaskManagerService.checkPermission(
351                     CONTROL_KEYGUARD, callingPid, callingUid);
352             if (controlKeyguardPerm != PERMISSION_GRANTED) {
353                 final String msg = "Permission Denial: starting " + getIntentString(intent)
354                         + " from " + callerApp + " (pid=" + callingPid
355                         + ", uid=" + callingUid + ") with dismissKeyguardIfInsecure=true";
356                 Slog.w(TAG, msg);
357                 throw new SecurityException(msg);
358             }
359         }
360 
361         // Check permission for remote animations
362         final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
363         if (adapter != null && supervisor.mService.checkPermission(
364                 CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
365                         != PERMISSION_GRANTED) {
366             final String msg = "Permission Denial: starting " + getIntentString(intent)
367                     + " from " + callerApp + " (pid=" + callingPid
368                     + ", uid=" + callingUid + ") with remoteAnimationAdapter";
369             Slog.w(TAG, msg);
370             throw new SecurityException(msg);
371         }
372 
373         // Check permission for remote transitions
374         final RemoteTransition transition = options.getRemoteTransition();
375         if (transition != null && supervisor.mService.checkPermission(
376                 CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
377                 != PERMISSION_GRANTED) {
378             final String msg = "Permission Denial: starting " + getIntentString(intent)
379                     + " from " + callerApp + " (pid=" + callingPid
380                     + ", uid=" + callingUid + ") with remoteTransition";
381             Slog.w(TAG, msg);
382             throw new SecurityException(msg);
383         }
384 
385         // If launched from bubble is specified, then ensure that the caller is system or sysui.
386         if (options.getLaunchedFromBubble() && !isSystemOrSystemUI(callingPid, callingUid)) {
387             final String msg = "Permission Denial: starting " + getIntentString(intent)
388                     + " from " + callerApp + " (pid=" + callingPid
389                     + ", uid=" + callingUid + ") with launchedFromBubble=true";
390             Slog.w(TAG, msg);
391             throw new SecurityException(msg);
392         }
393 
394         final int activityType = options.getLaunchActivityType();
395         if (activityType != ACTIVITY_TYPE_UNDEFINED
396                 && !isSystemOrSystemUI(callingPid, callingUid)) {
397             // Granted if it is assistant type and the calling uid is assistant.
398             boolean activityTypeGranted = false;
399             if (activityType == ACTIVITY_TYPE_ASSISTANT
400                     && isAssistant(supervisor.mService, callingUid)) {
401                 activityTypeGranted = true;
402             }
403 
404             if (!activityTypeGranted) {
405                 final String msg = "Permission Denial: starting " + getIntentString(intent)
406                         + " from " + callerApp + " (pid=" + callingPid
407                         + ", uid=" + callingUid + ") with launchActivityType="
408                         + activityTypeToString(options.getLaunchActivityType());
409                 Slog.w(TAG, msg);
410                 throw new SecurityException(msg);
411             }
412         }
413     }
414 
415     @VisibleForTesting
getLaunchTaskDisplayArea(ActivityOptions options, ActivityTaskSupervisor supervisor)416     TaskDisplayArea getLaunchTaskDisplayArea(ActivityOptions options,
417             ActivityTaskSupervisor supervisor) {
418         final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
419         TaskDisplayArea taskDisplayArea = daToken != null
420                 ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
421         if (taskDisplayArea != null) {
422             return taskDisplayArea;
423         }
424 
425         // If we do not have a task display area token, check if the launch task display area
426         // feature id is specified.
427         final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
428         if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
429             final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
430                     ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
431             final DisplayContent dc = supervisor.mRootWindowContainer
432                     .getDisplayContent(launchDisplayId);
433             if (dc != null) {
434                 taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
435                         tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
436             }
437         }
438         return taskDisplayArea;
439     }
440 
isAssistant(ActivityTaskManagerService atmService, int callingUid)441     private boolean isAssistant(ActivityTaskManagerService atmService, int callingUid) {
442         if (atmService.mActiveVoiceInteractionServiceComponent == null) {
443             return false;
444         }
445 
446         final String assistantPackage =
447                 atmService.mActiveVoiceInteractionServiceComponent.getPackageName();
448         try {
449             final int uid = AppGlobals.getPackageManager().getPackageUid(assistantPackage,
450                     PackageManager.MATCH_DIRECT_BOOT_AUTO,
451                     UserHandle.getUserId(callingUid));
452             if (uid == callingUid) {
453                 return true;
454             }
455         } catch (RemoteException e) {
456             // Should not happen
457         }
458         return false;
459     }
460 
isSystemOrSystemUI(int callingPid, int callingUid)461     private boolean isSystemOrSystemUI(int callingPid, int callingUid) {
462         if (callingUid == Process.SYSTEM_UID) {
463             return true;
464         }
465 
466         final int statusBarPerm = ActivityTaskManagerService.checkPermission(
467                 STATUS_BAR_SERVICE, callingPid, callingUid);
468         return statusBarPerm == PERMISSION_GRANTED;
469     }
470 
getIntentString(Intent intent)471     private String getIntentString(Intent intent) {
472         return intent != null ? intent.toString() : "(no intent)";
473     }
474 }
475