美文网首页
Android9.0系统源码_获取当前运行在最顶层的Activi

Android9.0系统源码_获取当前运行在最顶层的Activi

作者: AFinalStone | 来源:发表于2022-12-05 19:30 被阅读0次

    前言

    在 Android 开发中,由于某些需求常常需要获取当前顶层的 Activity 信息。比如 App 中获取顶层 Activity 界面信息来判断某一个 app 是否在前台运行、统计某一个 app 的使用时长、更有恶意程序通过监听界面伪造 app 进行盗号以及欺诈、自动化开发中通过顶层 Activity 进行页面元素定位点击(比如基于辅助功自动化、uiautomator 自动化)等等操作。 在逆向工程中,获取当前运行 app 运行顶层 activity 也比较常用。通过顶层 Activity 可以快速定位界面中的功能在哪一个页面。

    一、获取当前运行的顶层 Activity的几种方式

    1、调用ActivityManager的getRunningTasks方法

    1)在AndroidManifest文件中添加权限:

    <uses-permission  android:name = "android.permission.GET_TASKS"/>
    

    2)获取顶层 activity 参考代码:

        private String getTopActivityByActivityManager() {
            ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningTaskInfo> listTask = activityManager.getRunningTasks(0);
            String activityName = "";
            if (listTask != null && !listTask.isEmpty()) {
                ActivityManager.RunningTaskInfo runningTaskInfo = listTask.get(1);
                activityName = runningTaskInfo.topActivity.getClassName();
            }
           return activityName;
        }
    

    2、调用UsageStatsManager的queryEvents方法:

    1)在AndroidManifest文件中添加权限:

    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
    

    2)需要启动授权页面,让用户授权app获取应用使用情况统计权限。:

    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    context.startActivity(intent);
    

    3)获取顶层 activity的参考代码:

        public String getTopActivityByUsageStatsManager() {
            long endTime = System.currentTimeMillis();
            long beginTime = endTime - 10000;
    
            UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
            String activityInfo = "";
            UsageEvents.Event event = new UsageEvents.Event();
            UsageEvents usageEvents = usageStatsManager.queryEvents(beginTime, endTime);
            while (usageEvents.hasNextEvent()) {
                usageEvents.getNextEvent(event);
                if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
                    activityInfo = event.getPackageName() + "/" + event.getClassName();
                }
            }
            return activityInfo;
        }
    

    3、使用adb命令
    1)输入dumpsys指令

    adb shell "dumpsys activity | grep "ResumedActivity:"
    

    2)得到的结果如下所示:

    ResumedActivity: ActivityRecord{17ea57d u10 com.example.appcenter/.activity.MainActivity t1000085}
    

    二、ActivityManager的getRunningTasks方法源码分析

    1、ActivityManager的getRunningTasks方法如下所示:

    frameworks/base/core/java/android/app/ActivityManager.java

    @SystemService(Context.ACTIVITY_SERVICE)
    public class ActivityManager {
        ...代码省略...
        public List<RunningTaskInfo> getRunningTasks(int maxNum)
                throws SecurityException {
            try {
                return getService().getTasks(maxNum);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        ...代码省略...
    }
    

    getRunningTasks方法内部直接调用了getService获取一个实例对象并调用该对象的getTasks方法。

    2、ActivityManager的getService方法如下所示:

    @TestApi
    @SystemService(Context.ACTIVITY_TASK_SERVICE)
    public class ActivityTaskManager {
        ...代码省略...
        public static IActivityManager getService() {
            return IActivityManagerSingleton.get();
        }
    
        private static final Singleton<IActivityManager> IActivityManagerSingleton =
                new Singleton<IActivityManager>() {
                    @Override
                    protected IActivityManager create() {
                        final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                        final IActivityManager am = IActivityManager.Stub.asInterface(b);
                        return am;
                    }
                };
        ...代码省略...
    }
    

    getService方法会返回一个IActivityTaskManager类型的单例对象。

    4、在Android9系统源码中并不存在IActivityManager.java这样一个文件,只能找到 IActivityManager.aidl文件:

    frameworks/base/core/java/android/app/IActivityManager.aidl

    interface IActivityManager {
        ...代码省略...
       List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
        ...代码省略...
    }
    

    我们知道源码编译的时候会将aidl文件转化为 java 文件,IActivityManager的getTasks方法的调用最终是通过binder来实现跨进程通信的。而IActivityManager.aidl中getTasks方法的具体实现类,其实是ActivityManagerService。

    5、ActivityManagerService的getTasks方法如下所示:

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    public class ActivityManagerService extends IActivityManager.Stub
            implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        ...代码省略...
        final ActivityStackSupervisor mStackSupervisor;//Activity任务栈管理者
        ...代码省略...
        @Override
        public List<RunningTaskInfo> getTasks(int maxNum) {
            return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
        }
    
        @Override
        public List<RunningTaskInfo> getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType,
                                                      @WindowingMode int ignoreWindowingMode) {
            final int callingUid = Binder.getCallingUid();
            ArrayList<RunningTaskInfo> list = new ArrayList<>();
    
            synchronized (this) {
                if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
    
                final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
                        callingUid);
                mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
                        ignoreWindowingMode, callingUid, allowed);
            }
    
            return list;
        }
        ...代码省略...
    }
    

    ActivityManagerService的getTasks方法内部会再次调用getFilteredTasks方法,getFilteredTasks方法最终会调用ActivityStackSupervisor的getRunningTasks方法。

    6、ActivityStackSupervisor的getRunningTasks方法如下所示:

    frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

    public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
            RecentTasks.Callbacks {
        ...代码省略...
        private RunningTasks mRunningTasks;
        ...代码省略...
        void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
                @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
                int callingUid, boolean allowed) {
            mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
                    mActivityDisplays, callingUid, allowed);
        }
        ...代码省略...
    }
    

    getRunningTasks内部再次调用了RunningTasks的getTasks方法。
    7、RunningTasks这个类的代码并不多,如下所示:

    class RunningTasks {
    
        private static final Comparator<TaskRecord> LAST_ACTIVE_TIME_COMPARATOR =
                (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
        private final TaskRecord.TaskActivitiesReport mTmpReport = new TaskRecord.TaskActivitiesReport();
        private final TreeSet<TaskRecord> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
        private final ArrayList<TaskRecord> mTmpStackTasks = new ArrayList<>();
    
        void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
                @WindowingMode int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
                int callingUid, boolean allowed) {
            if (maxNum <= 0) {
                return;
            }
            mTmpSortedSet.clear();//清空
            mTmpStackTasks.clear();//清空
            final int numDisplays = activityDisplays.size();//获取显示屏的数量
            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                final ActivityDisplay display = activityDisplays.valueAt(displayNdx);//默认只有一个显示屏,所以displayNdx等于0
                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                    final ActivityStack stack = display.getChildAt(stackNdx);//获取屏幕所对应的Activity栈管理者
                    stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
                            callingUid, allowed);//获取正在运行的任务栈,按照最后活跃的时间序列将其存储在mTmpStackTasks中
                    for (int i = mTmpStackTasks.size() - 1; i >= 0; i--) {
                        mTmpSortedSet.addAll(mTmpStackTasks);
                    }
                }
            }
            final Iterator<TaskRecord> iter = mTmpSortedSet.iterator();
            while (iter.hasNext()) {
                if (maxNum == 0) {
                    break;
                }
    
                final TaskRecord task = iter.next();
                list.add(createRunningTaskInfo(task));
                maxNum--;
            }
        }
    
        //将任务栈TaskRecord 转化为RunningTaskInfo对象
        private RunningTaskInfo createRunningTaskInfo(TaskRecord task) {
            task.getNumRunningActivities(mTmpReport);
            final RunningTaskInfo ci = new RunningTaskInfo();
            ci.id = task.taskId;
            ci.stackId = task.getStackId();
            ci.baseActivity = mTmpReport.base.intent.getComponent();
            ci.topActivity = mTmpReport.top.intent.getComponent();
            ci.lastActiveTime = task.lastActiveTime;
            ci.description = task.lastDescription;
            ci.numActivities = mTmpReport.numActivities;
            ci.numRunning = mTmpReport.numRunning;
            ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
            ci.resizeMode = task.mResizeMode;
            ci.configuration.setTo(task.getConfiguration());
            return ci;
        }
    }
    

    getTasks方法首先将mTmpSortedSet和mTmpStackTasks清空,然后获取当前显示屏的数量,默认只有一个显示屏,所以displayNdx等于0且循环会执行一次,紧接着获取默认屏幕所对应的Activity栈管理者ActivityStack,调用ActivityStack的getRunningTasks方法获取正在运行的任务栈,将其存储在mTmpStackTasks中;随后会将mTmpStackTasks整个添加到mTmpSortedSet中,mTmpSortedSet会按照最后活跃时间来对条目进行排序。然后获取mTmpSortedSet的迭代器,依次将TaskRecord转化为RunningTaskInfo实例对象并存储到getTasks方法参数list中,这样上层就能拿到当前最上层的Activity信息了。

    image.png

    相关文章

      网友评论

          本文标题:Android9.0系统源码_获取当前运行在最顶层的Activi

          本文链接:https://www.haomeiwen.com/subject/vagvfdtx.html