我目前正在开发一款适用于平板电脑的应用,可以在商店中展示广告。我需要为设置应用程序添加密码保护。为此,我需要检测设置应用程序是在后台服务中启动的。对于Android版本低于API 21的设备,可以使用getRunningTasks()完成此操作。不幸的是,此方法现已弃用。我尝试用this answer来实现我的目标,但事情并没有按照我的需要运作。我试图听V/WindowManager( 740): Adding window Window{1f4535ef u0 com.android.settings/com.android.settings.Settings} at 9 of 16 (before Window{e14eed2 u0 Starting com.android.settings})
,但我无法将其视为输出。我使用上面引用的答案中的代码。而不是烘烤它我在logcat中打印它。
你能告诉我如何在API 21以上的Android版本上实现我的目标吗?我知道这是可能的,因为像Smart AppLock这样的应用程序可以实现。提前谢谢!
答案 0 :(得分:7)
除非您被授予系统级权限android.permission.PACKAGE_USAGE_STATS
,否则目前无法在Android 5.1.1+上获取前台应用。请注意,几个主要供应商已删除了允许从其设备访问UsageStats API的系统活动。这意味着这些设备上的应用程序永远无法获得必要的权限。这可能会随系统更新而改变。
您提到Smart Lock (App Protector)可以检测Android M上的当前前台活动。首次打开Smart Lock(或类似应用)时,系统会提示您授予PACKAGE_USAGE_STATS
权限。因此,我们可以得出结论,他们依靠UsageStatsManager
来获取前台应用程序。
我尝试在没有系统级权限的情况下找到解决方法(hack)来获取Android M上的前台应用。请在我的答案中阅读并试用我的脚本:Android 5.1.1 and above - getRunningAppProcesses() returns my application package only
<强> logcat的强>
除非您拥有root权限,否则读取logcat以获取前台应用程序将无效。从Jelly Bean开始读取logcat只返回来自应用程序的日志消息。
有用的链接:
How to check if "android.permission.PACKAGE_USAGE_STATS" permission is given?
https://code.google.com/p/android-developer-preview/issues/detail?id=2347
在挖掘Android源代码后,我编写了以下代码,以便在没有任何权限的情况下在Android M上获取前台应用。我只在运行最新开发者预览版的Nexus 9上测试过它。请将代码复制并粘贴到项目中的类中,并通过调用静态方法getForegroundApp()
对其进行测试:
/** first app user */
public static final int AID_APP = 10000;
/** offset for uid ranges for each user */
public static final int AID_USER = 100000;
public static String getForegroundApp() {
File[] files = new File("/proc").listFiles();
int lowestOomScore = Integer.MAX_VALUE;
String foregroundProcess = null;
for (File file : files) {
if (!file.isDirectory()) {
continue;
}
int pid;
try {
pid = Integer.parseInt(file.getName());
} catch (NumberFormatException e) {
continue;
}
try {
String cgroup = read(String.format("/proc/%d/cgroup", pid));
String[] lines = cgroup.split("\n");
if (lines.length != 2) {
continue;
}
String cpuSubsystem = lines[0];
String cpuaccctSubsystem = lines[1];
if (!cpuaccctSubsystem.endsWith(Integer.toString(pid))) {
// not an application process
continue;
}
if (cpuSubsystem.endsWith("bg_non_interactive")) {
// background policy
continue;
}
String cmdline = read(String.format("/proc/%d/cmdline", pid));
if (cmdline.contains("com.android.systemui")) {
continue;
}
int uid = Integer.parseInt(
cpuaccctSubsystem.split(":")[2].split("/")[1].replace("uid_", ""));
if (uid >= 1000 && uid <= 1038) {
// system process
continue;
}
int appId = uid - AID_APP;
int userId = 0;
// loop until we get the correct user id.
// 100000 is the offset for each user.
while (appId > AID_USER) {
appId -= AID_USER;
userId++;
}
if (appId < 0) {
continue;
}
// u{user_id}_a{app_id} is used on API 17+ for multiple user account support.
// String uidName = String.format("u%d_a%d", userId, appId);
File oomScoreAdj = new File(String.format("/proc/%d/oom_score_adj", pid));
if (oomScoreAdj.canRead()) {
int oomAdj = Integer.parseInt(read(oomScoreAdj.getAbsolutePath()));
if (oomAdj != 0) {
continue;
}
}
int oomscore = Integer.parseInt(read(String.format("/proc/%d/oom_score", pid)));
if (oomscore < lowestOomScore) {
lowestOomScore = oomscore;
foregroundProcess = cmdline;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return foregroundProcess;
}
private static String read(String path) throws IOException {
StringBuilder output = new StringBuilder();
BufferedReader reader = new BufferedReader(new FileReader(path));
output.append(reader.readLine());
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
output.append('\n').append(line);
}
reader.close();
return output.toString();
}