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