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