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.Manifest.permission.STATUS_BAR_SERVICE; 22 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; 24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 25 import static android.app.WindowConfiguration.activityTypeToString; 26 import static android.content.pm.PackageManager.PERMISSION_DENIED; 27 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 28 import static android.view.Display.INVALID_DISPLAY; 29 30 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; 31 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; 32 33 import android.annotation.Nullable; 34 import android.app.ActivityOptions; 35 import android.app.AppGlobals; 36 import android.app.PendingIntent; 37 import android.content.Intent; 38 import android.content.pm.ActivityInfo; 39 import android.content.pm.PackageManager; 40 import android.os.Binder; 41 import android.os.Bundle; 42 import android.os.Process; 43 import android.os.RemoteException; 44 import android.os.UserHandle; 45 import android.util.Slog; 46 import android.view.RemoteAnimationAdapter; 47 import android.window.WindowContainerToken; 48 49 import com.android.internal.annotations.VisibleForTesting; 50 51 /** 52 * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving 53 * the inner options. Also supports having two set of options: Once from the original caller, and 54 * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}. 55 */ 56 public class SafeActivityOptions { 57 58 private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM; 59 60 private final int mOriginalCallingPid; 61 private final int mOriginalCallingUid; 62 private int mRealCallingPid; 63 private int mRealCallingUid; 64 private final @Nullable ActivityOptions mOriginalOptions; 65 private @Nullable ActivityOptions mCallerOptions; 66 67 /** 68 * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/ 69 * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing 70 * this object. 71 * 72 * @param bOptions The {@link ActivityOptions} as {@link Bundle}. 73 */ fromBundle(Bundle bOptions)74 public static SafeActivityOptions fromBundle(Bundle bOptions) { 75 return bOptions != null 76 ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions)) 77 : null; 78 } 79 80 /** 81 * Constructs a new instance from a bundle and provided pid/uid. 82 * 83 * @param bOptions The {@link ActivityOptions} as {@link Bundle}. 84 */ fromBundle(Bundle bOptions, int callingPid, int callingUid)85 static SafeActivityOptions fromBundle(Bundle bOptions, int callingPid, int callingUid) { 86 return bOptions != null 87 ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions), 88 callingPid, callingUid) 89 : null; 90 } 91 92 /** 93 * Constructs a new instance and records {@link Binder#getCallingPid}/ 94 * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing 95 * this object. 96 * 97 * @param options The options to wrap. 98 */ SafeActivityOptions(@ullable ActivityOptions options)99 public SafeActivityOptions(@Nullable ActivityOptions options) { 100 mOriginalCallingPid = Binder.getCallingPid(); 101 mOriginalCallingUid = Binder.getCallingUid(); 102 mOriginalOptions = options; 103 } 104 105 /** 106 * Constructs a new instance. 107 * 108 * @param options The options to wrap. 109 */ SafeActivityOptions(@ullable ActivityOptions options, int callingPid, int callingUid)110 private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) { 111 mOriginalCallingPid = callingPid; 112 mOriginalCallingUid = callingUid; 113 mOriginalOptions = options; 114 } 115 116 /** 117 * Overrides options with options from a caller and records {@link Binder#getCallingPid}/ 118 * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this 119 * method. 120 */ setCallerOptions(@ullable ActivityOptions options)121 public void setCallerOptions(@Nullable ActivityOptions options) { 122 mRealCallingPid = Binder.getCallingPid(); 123 mRealCallingUid = Binder.getCallingUid(); 124 mCallerOptions = options; 125 } 126 127 /** 128 * Performs permission check and retrieves the options. 129 * 130 * @param r The record of the being started activity. 131 */ getOptions(ActivityRecord r)132 ActivityOptions getOptions(ActivityRecord r) throws SecurityException { 133 return getOptions(r.intent, r.info, r.app, r.mTaskSupervisor); 134 } 135 136 /** 137 * Performs permission check and retrieves the options when options are not being used to launch 138 * a specific activity (i.e. a task is moved to front). 139 */ getOptions(ActivityTaskSupervisor supervisor)140 ActivityOptions getOptions(ActivityTaskSupervisor supervisor) throws SecurityException { 141 return getOptions(null, null, null, supervisor); 142 } 143 144 /** 145 * Performs permission check and retrieves the options. 146 * 147 * @param intent The intent that is being launched. 148 * @param aInfo The info of the activity being launched. 149 * @param callerApp The record of the caller. 150 */ getOptions(@ullable Intent intent, @Nullable ActivityInfo aInfo, @Nullable WindowProcessController callerApp, ActivityTaskSupervisor supervisor)151 ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo, 152 @Nullable WindowProcessController callerApp, 153 ActivityTaskSupervisor supervisor) throws SecurityException { 154 if (mOriginalOptions != null) { 155 checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions, 156 mOriginalCallingPid, mOriginalCallingUid); 157 setCallingPidUidForRemoteAnimationAdapter(mOriginalOptions, mOriginalCallingPid, 158 mOriginalCallingUid); 159 } 160 if (mCallerOptions != null) { 161 checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions, 162 mRealCallingPid, mRealCallingUid); 163 setCallingPidUidForRemoteAnimationAdapter(mCallerOptions, mRealCallingPid, 164 mRealCallingUid); 165 } 166 return mergeActivityOptions(mOriginalOptions, mCallerOptions); 167 } 168 setCallingPidUidForRemoteAnimationAdapter(ActivityOptions options, int callingPid, int callingUid)169 private void setCallingPidUidForRemoteAnimationAdapter(ActivityOptions options, 170 int callingPid, int callingUid) { 171 final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter(); 172 if (adapter == null) { 173 return; 174 } 175 if (callingPid == Process.myPid()) { 176 Slog.wtf(TAG, "Safe activity options constructed after clearing calling id"); 177 return; 178 } 179 adapter.setCallingPidUid(callingPid, callingUid); 180 } 181 182 /** 183 * Gets the original options passed in. It should only be used for logging. DO NOT use it as a 184 * condition in the logic of activity launch. 185 */ getOriginalOptions()186 ActivityOptions getOriginalOptions() { 187 return mOriginalOptions; 188 } 189 190 /** 191 * @see ActivityOptions#popAppVerificationBundle 192 */ popAppVerificationBundle()193 Bundle popAppVerificationBundle() { 194 return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null; 195 } 196 abort()197 private void abort() { 198 if (mOriginalOptions != null) { 199 ActivityOptions.abort(mOriginalOptions); 200 } 201 if (mCallerOptions != null) { 202 ActivityOptions.abort(mCallerOptions); 203 } 204 } 205 abort(@ullable SafeActivityOptions options)206 static void abort(@Nullable SafeActivityOptions options) { 207 if (options != null) { 208 options.abort(); 209 } 210 } 211 212 /** 213 * Merges two activity options into one, with {@code options2} taking precedence in case of a 214 * conflict. 215 */ 216 @VisibleForTesting mergeActivityOptions(@ullable ActivityOptions options1, @Nullable ActivityOptions options2)217 @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1, 218 @Nullable ActivityOptions options2) { 219 if (options1 == null) { 220 return options2; 221 } 222 if (options2 == null) { 223 return options1; 224 } 225 final Bundle b1 = options1.toBundle(); 226 final Bundle b2 = options2.toBundle(); 227 b1.putAll(b2); 228 return ActivityOptions.fromBundle(b1); 229 } 230 checkPermissions(@ullable Intent intent, @Nullable ActivityInfo aInfo, @Nullable WindowProcessController callerApp, ActivityTaskSupervisor supervisor, ActivityOptions options, int callingPid, int callingUid)231 private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo, 232 @Nullable WindowProcessController callerApp, ActivityTaskSupervisor supervisor, 233 ActivityOptions options, int callingPid, int callingUid) { 234 // If a launch task id is specified, then ensure that the caller is the recents 235 // component or has the START_TASKS_FROM_RECENTS permission 236 if (options.getLaunchTaskId() != INVALID_TASK_ID 237 && !supervisor.mRecentTasks.isCallerRecents(callingUid)) { 238 final int startInTaskPerm = ActivityTaskManagerService.checkPermission( 239 START_TASKS_FROM_RECENTS, callingPid, callingUid); 240 if (startInTaskPerm == PERMISSION_DENIED) { 241 final String msg = "Permission Denial: starting " + getIntentString(intent) 242 + " from " + callerApp + " (pid=" + callingPid 243 + ", uid=" + callingUid + ") with launchTaskId=" 244 + options.getLaunchTaskId(); 245 Slog.w(TAG, msg); 246 throw new SecurityException(msg); 247 } 248 } 249 // Check if the caller is allowed to launch on the specified display area. 250 final WindowContainerToken daToken = options.getLaunchTaskDisplayArea(); 251 final TaskDisplayArea taskDisplayArea = daToken != null 252 ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; 253 if (aInfo != null && taskDisplayArea != null 254 && !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid, 255 taskDisplayArea, aInfo)) { 256 final String msg = "Permission Denial: starting " + getIntentString(intent) 257 + " from " + callerApp + " (pid=" + callingPid 258 + ", uid=" + callingUid + ") with launchTaskDisplayArea=" + taskDisplayArea; 259 Slog.w(TAG, msg); 260 throw new SecurityException(msg); 261 } 262 // Check if the caller is allowed to launch on the specified display. 263 final int launchDisplayId = options.getLaunchDisplayId(); 264 if (aInfo != null && launchDisplayId != INVALID_DISPLAY 265 && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, 266 launchDisplayId, aInfo)) { 267 final String msg = "Permission Denial: starting " + getIntentString(intent) 268 + " from " + callerApp + " (pid=" + callingPid 269 + ", uid=" + callingUid + ") with launchDisplayId=" 270 + launchDisplayId; 271 Slog.w(TAG, msg); 272 throw new SecurityException(msg); 273 } 274 // Check if someone tries to launch an unallowlisted activity into LockTask mode. 275 final boolean lockTaskMode = options.getLockTaskMode(); 276 if (aInfo != null && lockTaskMode 277 && !supervisor.mService.getLockTaskController().isPackageAllowlisted( 278 UserHandle.getUserId(callingUid), aInfo.packageName)) { 279 final String msg = "Permission Denial: starting " + getIntentString(intent) 280 + " from " + callerApp + " (pid=" + callingPid 281 + ", uid=" + callingUid + ") with lockTaskMode=true"; 282 Slog.w(TAG, msg); 283 throw new SecurityException(msg); 284 } 285 286 // Check if the caller is allowed to override any app transition animation. 287 final boolean overrideTaskTransition = options.getOverrideTaskTransition(); 288 if (aInfo != null && overrideTaskTransition) { 289 final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission( 290 START_TASKS_FROM_RECENTS, callingPid, callingUid); 291 if (startTasksFromRecentsPerm != PERMISSION_GRANTED) { 292 final String msg = "Permission Denial: starting " + getIntentString(intent) 293 + " from " + callerApp + " (pid=" + callingPid 294 + ", uid=" + callingUid + ") with overrideTaskTransition=true"; 295 Slog.w(TAG, msg); 296 throw new SecurityException(msg); 297 } 298 } 299 300 // Check permission for remote animations 301 final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter(); 302 if (adapter != null && supervisor.mService.checkPermission( 303 CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid) 304 != PERMISSION_GRANTED) { 305 final String msg = "Permission Denial: starting " + getIntentString(intent) 306 + " from " + callerApp + " (pid=" + callingPid 307 + ", uid=" + callingUid + ") with remoteAnimationAdapter"; 308 Slog.w(TAG, msg); 309 throw new SecurityException(msg); 310 } 311 312 // If launched from bubble is specified, then ensure that the caller is system or sysui. 313 if (options.getLaunchedFromBubble() && !isSystemOrSystemUI(callingPid, callingUid)) { 314 final String msg = "Permission Denial: starting " + getIntentString(intent) 315 + " from " + callerApp + " (pid=" + callingPid 316 + ", uid=" + callingUid + ") with launchedFromBubble=true"; 317 Slog.w(TAG, msg); 318 throw new SecurityException(msg); 319 } 320 321 final int activityType = options.getLaunchActivityType(); 322 if (activityType != ACTIVITY_TYPE_UNDEFINED 323 && !isSystemOrSystemUI(callingPid, callingUid)) { 324 // Granted if it is assistant type and the calling uid is assistant. 325 boolean activityTypeGranted = false; 326 if (activityType == ACTIVITY_TYPE_ASSISTANT 327 && isAssistant(supervisor.mService, callingUid)) { 328 activityTypeGranted = true; 329 } 330 331 if (!activityTypeGranted) { 332 final String msg = "Permission Denial: starting " + getIntentString(intent) 333 + " from " + callerApp + " (pid=" + callingPid 334 + ", uid=" + callingUid + ") with launchActivityType=" 335 + activityTypeToString(options.getLaunchActivityType()); 336 Slog.w(TAG, msg); 337 throw new SecurityException(msg); 338 } 339 } 340 } 341 isAssistant(ActivityTaskManagerService atmService, int callingUid)342 private boolean isAssistant(ActivityTaskManagerService atmService, int callingUid) { 343 if (atmService.mActiveVoiceInteractionServiceComponent == null) { 344 return false; 345 } 346 347 final String assistantPackage = 348 atmService.mActiveVoiceInteractionServiceComponent.getPackageName(); 349 try { 350 final int uid = AppGlobals.getPackageManager().getPackageUid(assistantPackage, 351 PackageManager.MATCH_DIRECT_BOOT_AUTO, 352 UserHandle.getUserId(callingUid)); 353 if (uid == callingUid) { 354 return true; 355 } 356 } catch (RemoteException e) { 357 // Should not happen 358 } 359 return false; 360 } 361 isSystemOrSystemUI(int callingPid, int callingUid)362 private boolean isSystemOrSystemUI(int callingPid, int callingUid) { 363 if (callingUid == Process.SYSTEM_UID) { 364 return true; 365 } 366 367 final int statusBarPerm = ActivityTaskManagerService.checkPermission( 368 STATUS_BAR_SERVICE, callingPid, callingUid); 369 return statusBarPerm == PERMISSION_GRANTED; 370 } 371 getIntentString(Intent intent)372 private String getIntentString(Intent intent) { 373 return intent != null ? intent.toString() : "(no intent)"; 374 } 375 } 376