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