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