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