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