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