本文最后更新于:2024年5月31日 下午
Android在AMS中拦截某个具体Activity的启动 最近在开发的过程中遇到这样一个问题,Android13项目带有GMS应用和服务的情况下,如果在系统中操作Location(位置信息),com.google.android.gms这个应用会弹出一个关于Location相关的提示框,因此非常影响我当下的业务需求和使用,所以我们研究如何屏蔽这个来自GMS的应用发出的弹窗。
通过adb命令可以查询当前Activity的包名和具体Calss名
1 adb shell dumpsys activity |findstr "mFocus"
方案一(推荐):在ActivityTaskManagerService.startActivityAsUser方法中去作拦截 1 system/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
AMS(ActivityManagerService)先校验一下Activity的正确性,如果正确的话,会暂存一下Activity的信息。然后,AMS会通知Launcher程序pause Activity(在AMS所在进程执行)
下面是AMS启动Activity的调用流程():
ActivityManagerService.startActivity
ActivityTaskManagerService.startActivity
ActivityTaskManagerService.startActivityAsUser(我是在这一步进行Activity的拦截的,因为这个方法中的参数比较明显,基本可以直接调用)
…………
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private final String GOOGLE_PACKAGE = "com.google.android.gms" ;private final String GOOGLE_LOCATION_ACTIVITY = "com.google.android.gms.location.settings.LocationOffWarningActivity" ;private int startActivityAsUser (IApplicationThread caller, String callingPackage, @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { assertPackageMatchesCallingUid(callingPackage); enforceNotIsolatedCaller("startActivityAsUser" ); try { ActivityInfo aInfo = mTaskSupervisor.resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId, Binder.getCallingUid()); Log.d(TAG, "callingPackage = " + callingPackage); if (GOOGLE_PACKAGE.equals(callingPackage) && GOOGLE_LOCATION_ACTIVITY.equals(aInfo.taskAffinity))) { Log.d(TAG, "Block Google location pop-ups" ); return ActivityManager.START_CANCELED; } }catch (Exception e){ Log.d(TAG, "e.getMessage = " + e.getMessage()); } userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser" ); }
方案二:在Dialog.show()方法中直接对这个包名所创建的Dialog做限制 在上述第一个方案之前,我一开始遇到这个问题,脑海里直接想的是在Dialog.show()方法中里面去做拦截(因为弹窗本身就是一个Dialog)。我确实也尝试了,下面是我修改的代码。 但是后来我发现这样修改后,我思考了一下,会存在两个问题,例如:
1.在Dialog中只能通过包名去拦截,不能直接拦截某一个指定的弹窗(或者说是Activity)。拦截一个和拦截这个包名中的全部来源,在本质上还是有区别的。
2.虽然这种方式可以达到拦截Dialog弹窗的目的,但是依旧还是会走Activity的创建流程。既然弹窗都屏蔽掉了,那这样会有什么影响呢? 因为我项目中的业务是在某个应用的使用过程中不想要出现这个提示框,那么我就要获取到当前顶层运行的应用和Activity,实际获取到的还是这个弹窗的包名和Activity,因为虽然屏蔽掉了Dialog,但是它依然走的是Activity的启动流程,所以这种方式并不周全。
1 system/frameworks/base/core/java/android/app/Dialog.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private final String GOOGLE_PACKAGE = "com.google.android.gms" ;public void show () { onStart(); mDecor = mWindow.getDecorView(); Log.d(TAG, "mContext.getPackageName() = " + mContext.getPackageName()); if (GOOGLE_PACKAGE_NAME.equals(mContext.getPackageName())) { Log.d(TAG, "Disable Google location pop-ups" ); return ; } if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); mActionBar = new WindowDecorActionBar (this ); }
那天,我听到了种子破土的声音,又细微又坚定。