It currently is not possible to get the foreground app on Android 5.1.1+ unless you are granted the system level permission android.permission.PACKAGE_USAGE_STATS
. Note that several major vendors have removed the system activity that grants access to the UsageStats API from their devices. This means that apps on those devices can never acquire the necessary permission. This may change with a system update.
You mentioned that Smart Lock (App Protector) can detect the current foreground activity on Android M. The first time you open Smart Lock (or similar apps) you are prompted to grant the PACKAGE_USAGE_STATS
permission. So we can conclude that they are relying on UsageStatsManager
to get the foreground application.
I have tried finding a workaround (hack) to get the foreground app on Android M without a system level permission. Please read and try out my script in my answer here: Android 5.1.1 and above - getRunningAppProcesses() returns my application package only
Logcat
Unless you have root permission, reading the logcat to get the foreground application will not work. Reading the logcat since Jelly Bean only returns log messages from your application only.
Useful links:
How to check if "android.permission.PACKAGE_USAGE_STATS" permission is given?
How to use UsageStatsManager?
https://code.google.com/p/android-developer-preview/issues/detail?id=2347
Edit:
After digging through Android source I wrote the following code to get the foreground app on Android M without any permissions. I have only tested it on a Nexus 9 running the latest developer preview. Please copy and paste the code into a class in your project and test it out by calling the static method 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();
}