blob: 0fb69e777ecccde05bbe60d5813d48a9b320ccb8 [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
17package com.android.server.am;
18
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;
24import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
25import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
26import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
27
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 */
47class SafeActivityOptions {
48
49 private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM;
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 */
65 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 */
78 SafeActivityOptions(@Nullable ActivityOptions options) {
79 mOriginalCallingPid = Binder.getCallingPid();
80 mOriginalCallingUid = Binder.getCallingUid();
81 mOriginalOptions = options;
Jorim Jaggi12d38a82018-04-26 18:51:16 +020082 if (mOriginalCallingPid == Process.myPid()) {
83 Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
84 }
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010085 }
86
87 /**
88 * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
89 * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
90 * method.
91 */
92 void setCallerOptions(@Nullable ActivityOptions options) {
93 mRealCallingPid = Binder.getCallingPid();
94 mRealCallingUid = Binder.getCallingUid();
95 mCallerOptions = options;
Jorim Jaggi12d38a82018-04-26 18:51:16 +020096 if (mRealCallingPid == Process.myPid()) {
97 Slog.wtf(TAG, "setCallerOptions called after clearing calling id");
98 }
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +010099 }
100
101 /**
102 * Performs permission check and retrieves the options.
103 *
104 * @param r The record of the being started activity.
105 */
106 ActivityOptions getOptions(ActivityRecord r) throws SecurityException {
107 return getOptions(r.intent, r.info, r.app, r.mStackSupervisor);
108 }
109
110 /**
111 * Performs permission check and retrieves the options when options are not being used to launch
112 * a specific activity (i.e. a task is moved to front).
113 */
114 ActivityOptions getOptions(ActivityStackSupervisor supervisor) throws SecurityException {
115 return getOptions(null, null, null, supervisor);
116 }
117
118 /**
119 * Performs permission check and retrieves the options.
120 *
121 * @param intent The intent that is being launched.
122 * @param aInfo The info of the activity being launched.
123 * @param callerApp The record of the caller.
124 */
125 ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
126 @Nullable ProcessRecord callerApp,
127 ActivityStackSupervisor supervisor) throws SecurityException {
128 if (mOriginalOptions != null) {
129 checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
130 mOriginalCallingPid, mOriginalCallingUid);
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100131 if (mOriginalOptions.getRemoteAnimationAdapter() != null) {
132 mOriginalOptions.getRemoteAnimationAdapter().setCallingPid(mOriginalCallingPid);
133 }
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100134 }
135 if (mCallerOptions != null) {
136 checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
137 mRealCallingPid, mRealCallingUid);
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100138 if (mCallerOptions.getRemoteAnimationAdapter() != null) {
139 mCallerOptions.getRemoteAnimationAdapter().setCallingPid(mRealCallingPid);
140 }
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100141 }
142 return mergeActivityOptions(mOriginalOptions, mCallerOptions);
143 }
144
145 /**
146 * @see ActivityOptions#popAppVerificationBundle
147 */
148 Bundle popAppVerificationBundle() {
149 return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
150 }
151
152 private void abort() {
153 if (mOriginalOptions != null) {
154 ActivityOptions.abort(mOriginalOptions);
155 }
156 if (mCallerOptions != null) {
157 ActivityOptions.abort(mCallerOptions);
158 }
159 }
160
161 static void abort(@Nullable SafeActivityOptions options) {
162 if (options != null) {
163 options.abort();
164 }
165 }
166
167 /**
168 * Merges two activity options into one, with {@code options2} taking precedence in case of a
169 * conflict.
170 */
171 @VisibleForTesting
172 @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
173 @Nullable ActivityOptions options2) {
174 if (options1 == null) {
175 return options2;
176 }
177 if (options2 == null) {
178 return options1;
179 }
180 final Bundle b1 = options1.toBundle();
181 final Bundle b2 = options2.toBundle();
182 b1.putAll(b2);
183 return ActivityOptions.fromBundle(b1);
184 }
185
186 private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
187 @Nullable ProcessRecord callerApp, ActivityStackSupervisor supervisor,
188 ActivityOptions options, int callingPid, int callingUid) {
189 // If a launch task id is specified, then ensure that the caller is the recents
190 // component or has the START_TASKS_FROM_RECENTS permission
191 if (options.getLaunchTaskId() != INVALID_TASK_ID
192 && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
193 final int startInTaskPerm = supervisor.mService.checkPermission(
194 START_TASKS_FROM_RECENTS, callingPid, callingUid);
195 if (startInTaskPerm == PERMISSION_DENIED) {
196 final String msg = "Permission Denial: starting " + getIntentString(intent)
197 + " from " + callerApp + " (pid=" + callingPid
198 + ", uid=" + callingUid + ") with launchTaskId="
199 + options.getLaunchTaskId();
200 Slog.w(TAG, msg);
201 throw new SecurityException(msg);
202 }
203 }
204 // Check if someone tries to launch an activity on a private display with a different
205 // owner.
206 final int launchDisplayId = options.getLaunchDisplayId();
207 if (aInfo != null && launchDisplayId != INVALID_DISPLAY
208 && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
209 launchDisplayId, aInfo)) {
210 final String msg = "Permission Denial: starting " + getIntentString(intent)
211 + " from " + callerApp + " (pid=" + callingPid
212 + ", uid=" + callingUid + ") with launchDisplayId="
213 + launchDisplayId;
214 Slog.w(TAG, msg);
215 throw new SecurityException(msg);
216 }
217 // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
218 final boolean lockTaskMode = options.getLockTaskMode();
219 if (aInfo != null && lockTaskMode
Bryce Lee2b8e0372018-04-05 17:01:37 -0700220 && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100221 UserHandle.getUserId(callingUid), aInfo.packageName)) {
222 final String msg = "Permission Denial: starting " + getIntentString(intent)
223 + " from " + callerApp + " (pid=" + callingPid
224 + ", uid=" + callingUid + ") with lockTaskMode=true";
225 Slog.w(TAG, msg);
226 throw new SecurityException(msg);
227 }
228
229 // Check permission for remote animations
230 final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
231 if (adapter != null && supervisor.mService.checkPermission(
232 CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
233 != PERMISSION_GRANTED) {
234 final String msg = "Permission Denial: starting " + getIntentString(intent)
235 + " from " + callerApp + " (pid=" + callingPid
236 + ", uid=" + callingUid + ") with remoteAnimationAdapter";
237 Slog.w(TAG, msg);
238 throw new SecurityException(msg);
239 }
240 }
241
242 private String getIntentString(Intent intent) {
243 return intent != null ? intent.toString() : "(no intent)";
244 }
245}