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