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