blob: a7b53688dbdcef674930203e43679e3d32e8d6cf [file] [log] [blame]
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +01001/*
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
Wale Ogunwale59507092018-10-29 09:00:30 -070017package com.android.server.wm;
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010018
19import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
20import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
21import static android.content.pm.PackageManager.PERMISSION_DENIED;
22import static android.content.pm.PackageManager.PERMISSION_GRANTED;
23import static android.view.Display.INVALID_DISPLAY;
Wale Ogunwale98875612018-10-12 07:53:02 -070024import static android.app.ActivityTaskManager.INVALID_TASK_ID;
Wale Ogunwale59507092018-10-29 09:00:30 -070025import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
26import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010027
28import android.annotation.Nullable;
29import android.app.ActivityOptions;
30import android.app.PendingIntent;
31import android.content.Intent;
32import android.content.pm.ActivityInfo;
33import android.os.Binder;
34import android.os.Bundle;
Jorim Jaggi12d38a82018-04-26 18:51:16 +020035import android.os.Process;
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010036import android.os.UserHandle;
37import android.util.Slog;
38import android.view.RemoteAnimationAdapter;
39
40import 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 */
Wale Ogunwaleee6eca12018-09-19 20:37:53 -070047public class SafeActivityOptions {
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010048
Wale Ogunwale98875612018-10-12 07:53:02 -070049 private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM;
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010050
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 */
Wale Ogunwale59507092018-10-29 09:00:30 -070065 public static SafeActivityOptions fromBundle(Bundle bOptions) {
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010066 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 */
Wale Ogunwale59507092018-10-29 09:00:30 -070078 public SafeActivityOptions(@Nullable ActivityOptions options) {
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010079 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 */
Wale Ogunwale59507092018-10-29 09:00:30 -070089 public void setCallerOptions(@Nullable ActivityOptions options) {
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010090 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 */
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 */
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 */
119 ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700120 @Nullable WindowProcessController callerApp,
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100121 ActivityStackSupervisor supervisor) throws SecurityException {
122 if (mOriginalOptions != null) {
123 checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
124 mOriginalCallingPid, mOriginalCallingUid);
Jorim Jaggi589c5ba2019-07-30 16:50:13 +0200125 setCallingPidUidForRemoteAnimationAdapter(mOriginalOptions, mOriginalCallingPid,
126 mOriginalCallingUid);
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100127 }
128 if (mCallerOptions != null) {
129 checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
130 mRealCallingPid, mRealCallingUid);
Jorim Jaggi589c5ba2019-07-30 16:50:13 +0200131 setCallingPidUidForRemoteAnimationAdapter(mCallerOptions, mRealCallingPid,
132 mRealCallingUid);
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100133 }
134 return mergeActivityOptions(mOriginalOptions, mCallerOptions);
135 }
136
Jorim Jaggi589c5ba2019-07-30 16:50:13 +0200137 private void setCallingPidUidForRemoteAnimationAdapter(ActivityOptions options,
138 int callingPid, int callingUid) {
Jorim Jaggi084c7062018-05-16 15:52:26 -0700139 final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
140 if (adapter == null) {
141 return;
142 }
143 if (callingPid == Process.myPid()) {
144 Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
145 return;
146 }
Jorim Jaggi589c5ba2019-07-30 16:50:13 +0200147 adapter.setCallingPidUid(callingPid, callingUid);
Jorim Jaggi084c7062018-05-16 15:52:26 -0700148 }
149
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100150 /**
151 * @see ActivityOptions#popAppVerificationBundle
152 */
153 Bundle popAppVerificationBundle() {
154 return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
155 }
156
157 private void abort() {
158 if (mOriginalOptions != null) {
159 ActivityOptions.abort(mOriginalOptions);
160 }
161 if (mCallerOptions != null) {
162 ActivityOptions.abort(mCallerOptions);
163 }
164 }
165
166 static void abort(@Nullable SafeActivityOptions options) {
167 if (options != null) {
168 options.abort();
169 }
170 }
171
172 /**
173 * Merges two activity options into one, with {@code options2} taking precedence in case of a
174 * conflict.
175 */
176 @VisibleForTesting
177 @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
178 @Nullable ActivityOptions options2) {
179 if (options1 == null) {
180 return options2;
181 }
182 if (options2 == null) {
183 return options1;
184 }
185 final Bundle b1 = options1.toBundle();
186 final Bundle b2 = options2.toBundle();
187 b1.putAll(b2);
188 return ActivityOptions.fromBundle(b1);
189 }
190
191 private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700192 @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor,
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100193 ActivityOptions options, int callingPid, int callingUid) {
194 // If a launch task id is specified, then ensure that the caller is the recents
195 // component or has the START_TASKS_FROM_RECENTS permission
196 if (options.getLaunchTaskId() != INVALID_TASK_ID
197 && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
Wale Ogunwalea6191b42018-05-09 07:41:32 -0700198 final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100199 START_TASKS_FROM_RECENTS, callingPid, callingUid);
200 if (startInTaskPerm == PERMISSION_DENIED) {
201 final String msg = "Permission Denial: starting " + getIntentString(intent)
202 + " from " + callerApp + " (pid=" + callingPid
203 + ", uid=" + callingUid + ") with launchTaskId="
204 + options.getLaunchTaskId();
205 Slog.w(TAG, msg);
206 throw new SecurityException(msg);
207 }
208 }
209 // Check if someone tries to launch an activity on a private display with a different
210 // owner.
211 final int launchDisplayId = options.getLaunchDisplayId();
212 if (aInfo != null && launchDisplayId != INVALID_DISPLAY
213 && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
214 launchDisplayId, aInfo)) {
215 final String msg = "Permission Denial: starting " + getIntentString(intent)
216 + " from " + callerApp + " (pid=" + callingPid
217 + ", uid=" + callingUid + ") with launchDisplayId="
218 + launchDisplayId;
219 Slog.w(TAG, msg);
220 throw new SecurityException(msg);
221 }
222 // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
223 final boolean lockTaskMode = options.getLockTaskMode();
224 if (aInfo != null && lockTaskMode
Wale Ogunwalec9e57de2018-05-08 14:28:07 -0700225 && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100226 UserHandle.getUserId(callingUid), aInfo.packageName)) {
227 final String msg = "Permission Denial: starting " + getIntentString(intent)
228 + " from " + callerApp + " (pid=" + callingPid
229 + ", uid=" + callingUid + ") with lockTaskMode=true";
230 Slog.w(TAG, msg);
231 throw new SecurityException(msg);
232 }
233
234 // Check permission for remote animations
235 final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
Wale Ogunwalef6733932018-06-27 05:14:34 -0700236 if (adapter != null && supervisor.mService.checkPermission(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100237 CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
238 != PERMISSION_GRANTED) {
239 final String msg = "Permission Denial: starting " + getIntentString(intent)
240 + " from " + callerApp + " (pid=" + callingPid
241 + ", uid=" + callingUid + ") with remoteAnimationAdapter";
242 Slog.w(TAG, msg);
243 throw new SecurityException(msg);
244 }
245 }
246
247 private String getIntentString(Intent intent) {
248 return intent != null ? intent.toString() : "(no intent)";
249 }
250}