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