1 /* 2 * Copyright (C) 2016 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.app.ActivityManager.INTENT_SENDER_ACTIVITY; 20 import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; 21 import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; 22 import static android.app.PendingIntent.FLAG_IMMUTABLE; 23 import static android.app.PendingIntent.FLAG_ONE_SHOT; 24 import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION; 25 import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES; 26 import static android.content.Context.KEYGUARD_SERVICE; 27 import static android.content.Intent.EXTRA_INTENT; 28 import static android.content.Intent.EXTRA_PACKAGE_NAME; 29 import static android.content.Intent.EXTRA_TASK_ID; 30 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 31 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 32 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; 33 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; 34 35 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 36 37 import android.annotation.Nullable; 38 import android.app.ActivityOptions; 39 import android.app.KeyguardManager; 40 import android.app.TaskInfo; 41 import android.app.admin.DevicePolicyManagerInternal; 42 import android.content.Context; 43 import android.content.IIntentSender; 44 import android.content.Intent; 45 import android.content.IntentSender; 46 import android.content.pm.ActivityInfo; 47 import android.content.pm.PackageManagerInternal; 48 import android.content.pm.ResolveInfo; 49 import android.content.pm.SuspendDialogInfo; 50 import android.content.pm.UserInfo; 51 import android.os.Bundle; 52 import android.os.IBinder; 53 import android.os.RemoteException; 54 import android.os.UserHandle; 55 import android.os.UserManager; 56 import android.util.SparseArray; 57 58 import com.android.internal.annotations.VisibleForTesting; 59 import com.android.internal.app.BlockedAppActivity; 60 import com.android.internal.app.HarmfulAppWarningActivity; 61 import com.android.internal.app.SuspendedAppActivity; 62 import com.android.internal.app.UnlaunchableAppActivity; 63 import com.android.server.LocalServices; 64 import com.android.server.am.ActivityManagerService; 65 import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult; 66 67 /** 68 * A class that contains activity intercepting logic for {@link ActivityStarter#execute()} 69 * It's initialized via setStates and interception occurs via the intercept method. 70 * 71 * Note that this class is instantiated when {@link ActivityManagerService} gets created so there 72 * is no guarantee that other system services are already present. 73 */ 74 class ActivityStartInterceptor { 75 76 private final ActivityTaskManagerService mService; 77 private final ActivityTaskSupervisor mSupervisor; 78 private final RootWindowContainer mRootWindowContainer; 79 private final Context mServiceContext; 80 81 // UserManager cannot be final as it's not ready when this class is instantiated during boot 82 private UserManager mUserManager; 83 84 /* 85 * Per-intent states loaded from ActivityStarter than shouldn't be changed by any 86 * interception routines. 87 */ 88 private int mRealCallingPid; 89 private int mRealCallingUid; 90 private int mUserId; 91 private int mStartFlags; 92 private String mCallingPackage; 93 private @Nullable String mCallingFeatureId; 94 95 /* 96 * Per-intent states that were load from ActivityStarter and are subject to modifications 97 * by the interception routines. After calling {@link #intercept} the caller should assign 98 * these values back to {@link ActivityStarter#startActivityLocked}'s local variables if 99 * {@link #intercept} returns true. 100 */ 101 Intent mIntent; 102 int mCallingPid; 103 int mCallingUid; 104 ResolveInfo mRInfo; 105 ActivityInfo mAInfo; 106 String mResolvedType; 107 Task mInTask; 108 TaskFragment mInTaskFragment; 109 ActivityOptions mActivityOptions; 110 ActivityStartInterceptor( ActivityTaskManagerService service, ActivityTaskSupervisor supervisor)111 ActivityStartInterceptor( 112 ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) { 113 this(service, supervisor, service.mRootWindowContainer, service.mContext); 114 } 115 116 @VisibleForTesting ActivityStartInterceptor(ActivityTaskManagerService service, ActivityTaskSupervisor supervisor, RootWindowContainer root, Context context)117 ActivityStartInterceptor(ActivityTaskManagerService service, ActivityTaskSupervisor supervisor, 118 RootWindowContainer root, Context context) { 119 mService = service; 120 mSupervisor = supervisor; 121 mRootWindowContainer = root; 122 mServiceContext = context; 123 } 124 125 /** 126 * Effectively initialize the class before intercepting the start intent. The values set in this 127 * method should not be changed during intercept. 128 */ setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, String callingPackage, @Nullable String callingFeatureId)129 void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, 130 String callingPackage, @Nullable String callingFeatureId) { 131 mRealCallingPid = realCallingPid; 132 mRealCallingUid = realCallingUid; 133 mUserId = userId; 134 mStartFlags = startFlags; 135 mCallingPackage = callingPackage; 136 mCallingFeatureId = callingFeatureId; 137 } 138 createIntentSenderForOriginalIntent(int callingUid, int flags)139 private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) { 140 Bundle bOptions = deferCrossProfileAppsAnimationIfNecessary(); 141 final TaskFragment taskFragment = getLaunchTaskFragment(); 142 // If the original intent is going to be embedded, try to forward the embedding TaskFragment 143 // and its task id to embed back the original intent. 144 if (taskFragment != null) { 145 ActivityOptions activityOptions = bOptions != null 146 ? ActivityOptions.fromBundle(bOptions) 147 : ActivityOptions.makeBasic(); 148 activityOptions.setLaunchTaskFragmentToken(taskFragment.getFragmentToken()); 149 bOptions = activityOptions.toBundle(); 150 } 151 final IIntentSender target = mService.getIntentSenderLocked( 152 INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingFeatureId, callingUid, mUserId, 153 null /*token*/, null /*resultCode*/, 0 /*requestCode*/, 154 new Intent[] { mIntent }, new String[] { mResolvedType }, 155 flags, bOptions); 156 return new IntentSender(target); 157 } 158 159 160 /** 161 * A helper function to obtain the targeted {@link TaskFragment} during 162 * {@link #intercept(Intent, ResolveInfo, ActivityInfo, String, Task, TaskFragment, int, int, 163 * ActivityOptions)} if any. 164 */ 165 @Nullable getLaunchTaskFragment()166 private TaskFragment getLaunchTaskFragment() { 167 if (mInTaskFragment != null) { 168 return mInTaskFragment; 169 } 170 if (mActivityOptions == null) { 171 return null; 172 } 173 final IBinder taskFragToken = mActivityOptions.getLaunchTaskFragmentToken(); 174 if (taskFragToken == null) { 175 return null; 176 } 177 return TaskFragment.fromTaskFragmentToken(taskFragToken, mService); 178 } 179 180 /** 181 * Intercept the launch intent based on various signals. If an interception happened the 182 * internal variables get assigned and need to be read explicitly by the caller. 183 * 184 * @return true if an interception occurred 185 */ intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid, ActivityOptions activityOptions)186 boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, 187 Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid, 188 ActivityOptions activityOptions) { 189 mUserManager = UserManager.get(mServiceContext); 190 191 mIntent = intent; 192 mCallingPid = callingPid; 193 mCallingUid = callingUid; 194 mRInfo = rInfo; 195 mAInfo = aInfo; 196 mResolvedType = resolvedType; 197 mInTask = inTask; 198 mInTaskFragment = inTaskFragment; 199 mActivityOptions = activityOptions; 200 201 if (interceptQuietProfileIfNeeded()) { 202 // If work profile is turned off, skip the work challenge since the profile can only 203 // be unlocked when profile's user is running. 204 return true; 205 } 206 if (interceptSuspendedPackageIfNeeded()) { 207 // Skip the rest of interceptions as the package is suspended by device admin so 208 // no user action can undo this. 209 return true; 210 } 211 if (interceptLockTaskModeViolationPackageIfNeeded()) { 212 return true; 213 } 214 if (interceptHarmfulAppIfNeeded()) { 215 // If the app has a "harmful app" warning associated with it, we should ask to uninstall 216 // before issuing the work challenge. 217 return true; 218 } 219 if (interceptLockedManagedProfileIfNeeded()) { 220 return true; 221 } 222 223 final SparseArray<ActivityInterceptorCallback> callbacks = 224 mService.getActivityInterceptorCallbacks(); 225 final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo = 226 getInterceptorInfo(null /* clearOptionsAnimation */); 227 228 for (int i = 0; i < callbacks.size(); i++) { 229 final ActivityInterceptorCallback callback = callbacks.valueAt(i); 230 final ActivityInterceptResult interceptResult = callback.intercept(interceptorInfo); 231 if (interceptResult == null) { 232 continue; 233 } 234 mIntent = interceptResult.intent; 235 mActivityOptions = interceptResult.activityOptions; 236 mCallingPid = mRealCallingPid; 237 mCallingUid = mRealCallingUid; 238 mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid); 239 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, 240 null /*profilerInfo*/); 241 return true; 242 } 243 return false; 244 } 245 hasCrossProfileAnimation()246 private boolean hasCrossProfileAnimation() { 247 return mActivityOptions != null 248 && mActivityOptions.getAnimationType() == ANIM_OPEN_CROSS_PROFILE_APPS; 249 } 250 251 /** 252 * If the activity option is the {@link ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} one, 253 * defer the animation until the original intent is started. 254 * 255 * @return the activity option used to start the original intent. 256 */ deferCrossProfileAppsAnimationIfNecessary()257 private Bundle deferCrossProfileAppsAnimationIfNecessary() { 258 if (hasCrossProfileAnimation()) { 259 mActivityOptions = null; 260 return ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(); 261 } 262 return null; 263 } 264 interceptQuietProfileIfNeeded()265 private boolean interceptQuietProfileIfNeeded() { 266 // Do not intercept if the user has not turned off the profile 267 if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) { 268 return false; 269 } 270 271 IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, 272 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT); 273 274 mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target); 275 mCallingPid = mRealCallingPid; 276 mCallingUid = mRealCallingUid; 277 mResolvedType = null; 278 279 final UserInfo parent = mUserManager.getProfileParent(mUserId); 280 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid); 281 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 282 return true; 283 } 284 interceptSuspendedByAdminPackage()285 private boolean interceptSuspendedByAdminPackage() { 286 DevicePolicyManagerInternal devicePolicyManager = LocalServices 287 .getService(DevicePolicyManagerInternal.class); 288 if (devicePolicyManager == null) { 289 return false; 290 } 291 mIntent = devicePolicyManager.createShowAdminSupportIntent(mUserId, true); 292 mIntent.putExtra(EXTRA_RESTRICTION, POLICY_SUSPEND_PACKAGES); 293 294 mCallingPid = mRealCallingPid; 295 mCallingUid = mRealCallingUid; 296 mResolvedType = null; 297 298 final UserInfo parent = mUserManager.getProfileParent(mUserId); 299 if (parent != null) { 300 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, 301 mRealCallingUid); 302 } else { 303 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, 304 mRealCallingUid); 305 } 306 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 307 return true; 308 } 309 interceptSuspendedPackageIfNeeded()310 private boolean interceptSuspendedPackageIfNeeded() { 311 // Do not intercept if the package is not suspended 312 if (mAInfo == null || mAInfo.applicationInfo == null || 313 (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) { 314 return false; 315 } 316 final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked(); 317 if (pmi == null) { 318 return false; 319 } 320 final String suspendedPackage = mAInfo.applicationInfo.packageName; 321 final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId); 322 if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { 323 return interceptSuspendedByAdminPackage(); 324 } 325 final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, 326 suspendingPackage, mUserId); 327 final Bundle crossProfileOptions = hasCrossProfileAnimation() 328 ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle() 329 : null; 330 final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, 331 FLAG_IMMUTABLE); 332 mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage, 333 suspendingPackage, dialogInfo, crossProfileOptions, target, mUserId); 334 mCallingPid = mRealCallingPid; 335 mCallingUid = mRealCallingUid; 336 mResolvedType = null; 337 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); 338 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 339 return true; 340 } 341 interceptLockTaskModeViolationPackageIfNeeded()342 private boolean interceptLockTaskModeViolationPackageIfNeeded() { 343 if (mAInfo == null || mAInfo.applicationInfo == null) { 344 return false; 345 } 346 LockTaskController controller = mService.getLockTaskController(); 347 String packageName = mAInfo.applicationInfo.packageName; 348 int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions); 349 if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) { 350 return false; 351 } 352 mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName); 353 mCallingPid = mRealCallingPid; 354 mCallingUid = mRealCallingUid; 355 mResolvedType = null; 356 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); 357 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 358 return true; 359 } 360 interceptLockedManagedProfileIfNeeded()361 private boolean interceptLockedManagedProfileIfNeeded() { 362 final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId); 363 if (interceptingIntent == null) { 364 return false; 365 } 366 mIntent = interceptingIntent; 367 mCallingPid = mRealCallingPid; 368 mCallingUid = mRealCallingUid; 369 mResolvedType = null; 370 final TaskFragment taskFragment = getLaunchTaskFragment(); 371 // If we are intercepting and there was a task, convert it into an extra for the 372 // ConfirmCredentials intent and unassign it, as otherwise the task will move to 373 // front even if ConfirmCredentials is cancelled. 374 if (mInTask != null) { 375 mIntent.putExtra(EXTRA_TASK_ID, mInTask.mTaskId); 376 mInTask = null; 377 } else if (taskFragment != null) { 378 // If the original intent is started to an embedded TaskFragment, append its parent task 379 // id to extra. It is to embed back the original intent to the TaskFragment with the 380 // same task. 381 final Task parentTask = taskFragment.getTask(); 382 if (parentTask != null) { 383 mIntent.putExtra(EXTRA_TASK_ID, parentTask.mTaskId); 384 } 385 } 386 if (mActivityOptions == null) { 387 mActivityOptions = ActivityOptions.makeBasic(); 388 } 389 390 final UserInfo parent = mUserManager.getProfileParent(mUserId); 391 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid); 392 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 393 return true; 394 } 395 396 /** 397 * Creates an intent to intercept the current activity start with Confirm Credentials if needed. 398 * 399 * @return The intercepting intent if needed. 400 */ interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId)401 private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) { 402 if ((aInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0 403 || !mService.mAmInternal.shouldConfirmCredentials(userId)) { 404 return null; 405 } 406 final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, 407 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE); 408 final KeyguardManager km = (KeyguardManager) mServiceContext 409 .getSystemService(KEYGUARD_SERVICE); 410 final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId, 411 true /* disallowBiometricsIfPolicyExists */); 412 if (newIntent == null) { 413 return null; 414 } 415 newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 416 FLAG_ACTIVITY_TASK_ON_HOME); 417 newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName); 418 newIntent.putExtra(EXTRA_INTENT, target); 419 return newIntent; 420 } 421 interceptHarmfulAppIfNeeded()422 private boolean interceptHarmfulAppIfNeeded() { 423 CharSequence harmfulAppWarning; 424 try { 425 harmfulAppWarning = mService.getPackageManager() 426 .getHarmfulAppWarning(mAInfo.packageName, mUserId); 427 } catch (RemoteException | IllegalArgumentException ex) { 428 return false; 429 } 430 431 if (harmfulAppWarning == null) { 432 return false; 433 } 434 435 final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, 436 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE); 437 438 mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext, 439 mAInfo.packageName, target, harmfulAppWarning); 440 441 mCallingPid = mRealCallingPid; 442 mCallingUid = mRealCallingUid; 443 mResolvedType = null; 444 445 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); 446 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 447 return true; 448 } 449 450 /** 451 * Called when an activity is successfully launched. 452 */ onActivityLaunched(TaskInfo taskInfo, ActivityRecord r)453 void onActivityLaunched(TaskInfo taskInfo, ActivityRecord r) { 454 final SparseArray<ActivityInterceptorCallback> callbacks = 455 mService.getActivityInterceptorCallbacks(); 456 ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo( 457 r::clearOptionsAnimationForSiblings); 458 for (int i = 0; i < callbacks.size(); i++) { 459 final ActivityInterceptorCallback callback = callbacks.valueAt(i); 460 callback.onActivityLaunched(taskInfo, r.info, info); 461 } 462 } 463 getInterceptorInfo( @ullable Runnable clearOptionsAnimation)464 private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo( 465 @Nullable Runnable clearOptionsAnimation) { 466 return new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid, 467 mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent, 468 mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid, 469 mActivityOptions, clearOptionsAnimation); 470 } 471 } 472