前言
在 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信息了。