Android - Как определить, когда начинается приложение для сторонних разработчиков

В настоящее время я разрабатываю приложение для планшетов, которое покажет рекламу в магазинах. Мне нужно добавить защиту паролем для приложения настроек. Для этого мне нужно обнаружить, что приложение настроек запущено в фоновом режиме. Для устройств с Android ниже API 21 это можно сделать с помощью getRunningTasks(). К сожалению, этот метод устарел. Я попытался достичь своей цели с помощью этого ответа, но все работает не так, как мне нужно. Я пытаюсь слушать 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}), но я не вижу его в качестве вывода. Я использую код из ответа, на который я ссылался выше. Вместо того, чтобы поджарить его, я печатаю его в логарифме.

Можете ли вы рассказать мне, как моя цель может быть достигнута для версий Android выше API 21? Я знаю, что это возможно, потому что такие приложения, как Smart AppLock, делают это. Спасибо заранее!

Ответы

Ответ 1

В настоящее время невозможно получить приложение foreground на Android 5.1.1+, если вам не предоставлено разрешение на уровне системы android.permission.PACKAGE_USAGE_STATS. Обратите внимание, что несколько крупных поставщиков удалили системную активность, которая предоставляет доступ к API UsageStats с их устройств. Это означает, что приложения на этих устройствах никогда не смогут получить необходимое разрешение. Это может измениться при обновлении системы.

Вы упомянули, что Smart Lock (App Protector) может обнаружить текущую активность переднего плана на Android M. При первом запуске Smart Lock ( или аналогичные приложения) вам предлагается предоставить разрешение PACKAGE_USAGE_STATS. Поэтому мы можем заключить, что они полагаются на UsageStatsManager, чтобы получить приложение переднего плана.

Я попытался найти обходное решение (взломать), чтобы получить приложение переднего плана на Android M без разрешения на системном уровне. Пожалуйста, прочтите и попробуйте мой script в моем ответе здесь: Android 5.1.1 и выше - getRunningAppProcesses() возвращает только мой пакет приложений


Logcat

Если у вас нет прав на root, чтение логарифма для получения приложения переднего плана не будет работать. Чтение logcat, поскольку Jelly Bean возвращает только сообщения журнала из вашего приложения.


Полезные ссылки:

Как проверить, есть ли & android.permission.PACKAGE_USAGE_STATS " разрешение указано?

Как использовать UsageStatsManager?

https://code.google.com/p/android-developer-preview/issues/detail?id=2347


Edit:

После ротации через Android-источник я написал следующий код, чтобы получить приложение foreground на 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();
}