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.app.ActivityTaskManager.INVALID_TASK_ID;
22 import static android.content.pm.PackageManager.PERMISSION_DENIED;
23 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
24 import static android.view.Display.INVALID_DISPLAY;
25 
26 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
27 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
28 
29 import android.annotation.Nullable;
30 import android.app.ActivityOptions;
31 import android.app.PendingIntent;
32 import android.content.Intent;
33 import android.content.pm.ActivityInfo;
34 import android.os.Binder;
35 import android.os.Bundle;
36 import android.os.Process;
37 import android.os.UserHandle;
38 import android.util.Slog;
39 import android.view.RemoteAnimationAdapter;
40 import android.window.WindowContainerToken;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 
44 /**
45  * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving
46  * the inner options. Also supports having two set of options: Once from the original caller, and
47  * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
48  */
49 public class SafeActivityOptions {
50 
51     private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM;
52 
53     private final int mOriginalCallingPid;
54     private final int mOriginalCallingUid;
55     private int mRealCallingPid;
56     private int mRealCallingUid;
57     private final @Nullable ActivityOptions mOriginalOptions;
58     private @Nullable ActivityOptions mCallerOptions;
59 
60     /**
61      * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/
62      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
63      * this object.
64      *
65      * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
66      */
fromBundle(Bundle bOptions)67     public static SafeActivityOptions fromBundle(Bundle bOptions) {
68         return bOptions != null
69                 ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions))
70                 : null;
71     }
72 
73     /**
74      * Constructs a new instance and records {@link Binder#getCallingPid}/
75      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
76      * this object.
77      *
78      * @param options The options to wrap.
79      */
SafeActivityOptions(@ullable ActivityOptions options)80     public SafeActivityOptions(@Nullable ActivityOptions options) {
81         mOriginalCallingPid = Binder.getCallingPid();
82         mOriginalCallingUid = Binder.getCallingUid();
83         mOriginalOptions = options;
84     }
85 
86     /**
87      * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
88      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
89      * method.
90      */
setCallerOptions(@ullable ActivityOptions options)91     public void setCallerOptions(@Nullable ActivityOptions options) {
92         mRealCallingPid = Binder.getCallingPid();
93         mRealCallingUid = Binder.getCallingUid();
94         mCallerOptions = options;
95     }
96 
97     /**
98      * Performs permission check and retrieves the options.
99      *
100      * @param r The record of the being started activity.
101      */
getOptions(ActivityRecord r)102     ActivityOptions getOptions(ActivityRecord r) throws SecurityException {
103         return getOptions(r.intent, r.info, r.app, r.mStackSupervisor);
104     }
105 
106     /**
107      * Performs permission check and retrieves the options when options are not being used to launch
108      * a specific activity (i.e. a task is moved to front).
109      */
getOptions(ActivityStackSupervisor supervisor)110     ActivityOptions getOptions(ActivityStackSupervisor supervisor) throws SecurityException {
111         return getOptions(null, null, null, supervisor);
112     }
113 
114     /**
115      * Performs permission check and retrieves the options.
116      *
117      * @param intent The intent that is being launched.
118      * @param aInfo The info of the activity being launched.
119      * @param callerApp The record of the caller.
120      */
getOptions(@ullable Intent intent, @Nullable ActivityInfo aInfo, @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor)121     ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
122             @Nullable WindowProcessController callerApp,
123             ActivityStackSupervisor supervisor) throws SecurityException {
124         if (mOriginalOptions != null) {
125             checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
126                     mOriginalCallingPid, mOriginalCallingUid);
127             setCallingPidUidForRemoteAnimationAdapter(mOriginalOptions, mOriginalCallingPid,
128                     mOriginalCallingUid);
129         }
130         if (mCallerOptions != null) {
131             checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
132                     mRealCallingPid, mRealCallingUid);
133             setCallingPidUidForRemoteAnimationAdapter(mCallerOptions, mRealCallingPid,
134                     mRealCallingUid);
135         }
136         return mergeActivityOptions(mOriginalOptions, mCallerOptions);
137     }
138 
setCallingPidUidForRemoteAnimationAdapter(ActivityOptions options, int callingPid, int callingUid)139     private void setCallingPidUidForRemoteAnimationAdapter(ActivityOptions options,
140             int callingPid, int callingUid) {
141         final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
142         if (adapter == null) {
143             return;
144         }
145         if (callingPid == Process.myPid()) {
146             Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
147             return;
148         }
149         adapter.setCallingPidUid(callingPid, callingUid);
150     }
151 
152     /**
153      * @see ActivityOptions#popAppVerificationBundle
154      */
popAppVerificationBundle()155     Bundle popAppVerificationBundle() {
156         return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
157     }
158 
abort()159     private void abort() {
160         if (mOriginalOptions != null) {
161             ActivityOptions.abort(mOriginalOptions);
162         }
163         if (mCallerOptions != null) {
164             ActivityOptions.abort(mCallerOptions);
165         }
166     }
167 
abort(@ullable SafeActivityOptions options)168     static void abort(@Nullable SafeActivityOptions options) {
169         if (options != null) {
170             options.abort();
171         }
172     }
173 
174     /**
175      * Merges two activity options into one, with {@code options2} taking precedence in case of a
176      * conflict.
177      */
178     @VisibleForTesting
mergeActivityOptions(@ullable ActivityOptions options1, @Nullable ActivityOptions options2)179     @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
180             @Nullable ActivityOptions options2) {
181         if (options1 == null) {
182             return options2;
183         }
184         if (options2 == null) {
185             return options1;
186         }
187         final Bundle b1 = options1.toBundle();
188         final Bundle b2 = options2.toBundle();
189         b1.putAll(b2);
190         return ActivityOptions.fromBundle(b1);
191     }
192 
checkPermissions(@ullable Intent intent, @Nullable ActivityInfo aInfo, @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor, ActivityOptions options, int callingPid, int callingUid)193     private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
194             @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor,
195             ActivityOptions options, int callingPid, int callingUid) {
196         // If a launch task id is specified, then ensure that the caller is the recents
197         // component or has the START_TASKS_FROM_RECENTS permission
198         if (options.getLaunchTaskId() != INVALID_TASK_ID
199                 && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
200             final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
201                     START_TASKS_FROM_RECENTS, callingPid, callingUid);
202             if (startInTaskPerm == PERMISSION_DENIED) {
203                 final String msg = "Permission Denial: starting " + getIntentString(intent)
204                         + " from " + callerApp + " (pid=" + callingPid
205                         + ", uid=" + callingUid + ") with launchTaskId="
206                         + options.getLaunchTaskId();
207                 Slog.w(TAG, msg);
208                 throw new SecurityException(msg);
209             }
210         }
211         // Check if the caller is allowed to launch on the specified display area.
212         final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
213         final TaskDisplayArea taskDisplayArea = daToken != null
214                 ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
215         if (aInfo != null && taskDisplayArea != null
216                 && !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid,
217                 taskDisplayArea, aInfo)) {
218             final String msg = "Permission Denial: starting " + getIntentString(intent)
219                     + " from " + callerApp + " (pid=" + callingPid
220                     + ", uid=" + callingUid + ") with launchTaskDisplayArea=" + taskDisplayArea;
221             Slog.w(TAG, msg);
222             throw new SecurityException(msg);
223         }
224         // Check if the caller is allowed to launch on the specified display.
225         final int launchDisplayId = options.getLaunchDisplayId();
226         if (aInfo != null && launchDisplayId != INVALID_DISPLAY
227                 && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
228                         launchDisplayId, aInfo)) {
229             final String msg = "Permission Denial: starting " + getIntentString(intent)
230                     + " from " + callerApp + " (pid=" + callingPid
231                     + ", uid=" + callingUid + ") with launchDisplayId="
232                     + launchDisplayId;
233             Slog.w(TAG, msg);
234             throw new SecurityException(msg);
235         }
236         // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
237         final boolean lockTaskMode = options.getLockTaskMode();
238         if (aInfo != null && lockTaskMode
239                 && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
240                         UserHandle.getUserId(callingUid), aInfo.packageName)) {
241             final String msg = "Permission Denial: starting " + getIntentString(intent)
242                     + " from " + callerApp + " (pid=" + callingPid
243                     + ", uid=" + callingUid + ") with lockTaskMode=true";
244             Slog.w(TAG, msg);
245             throw new SecurityException(msg);
246         }
247 
248         // Check permission for remote animations
249         final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
250         if (adapter != null && supervisor.mService.checkPermission(
251                 CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
252                         != PERMISSION_GRANTED) {
253             final String msg = "Permission Denial: starting " + getIntentString(intent)
254                     + " from " + callerApp + " (pid=" + callingPid
255                     + ", uid=" + callingUid + ") with remoteAnimationAdapter";
256             Slog.w(TAG, msg);
257             throw new SecurityException(msg);
258         }
259     }
260 
getIntentString(Intent intent)261     private String getIntentString(Intent intent) {
262         return intent != null ? intent.toString() : "(no intent)";
263     }
264 }
265