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