Android 5.1.1 и выше - getRunningAppProcesses() возвращает только мой пакет приложения

Похоже, Google наконец-то закрыл все двери для получения текущего пакета приложений переднего плана.

После обновления Lollipop, которое убило getRunningTasks(int maxNum) и благодаря этому ответу я использовал этот код для получения пакета приложений переднего плана начиная с Lollipop:

final int PROCESS_STATE_TOP = 2;
RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
    field = RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) { 
}
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (RunningAppProcessInfo app : appList) {
    if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
        app.importanceReasonCode == 0 ) {
        Integer state = null;
        try {
            state = field.getInt( app );
        } catch (Exception ignored) {
        }
        if (state != null && state == PROCESS_STATE_TOP) {
            currentInfo = app;
            break;
        }
    }
}
return currentInfo;

Android 5.1.1 и выше (6.0 Marshmallow), похоже, убит getRunningAppProcesses() также. Теперь он возвращает список вашего собственного пакета приложения.


UsageStatsManager

Мы можем использовать новый UsageStatsManager API, как описано здесь, но он не работает для всех приложений. Некоторые системные приложения возвращают один и тот же пакет

com.google.android.googlequicksearchbox

AccessibilityService (декабрь 2017: запрет на использование Google)

Некоторые приложения используют AccessibilityService (как видно здесь), но у него есть некоторые недостатки.


Есть ли другой способ получить текущий запущенный пакет приложения?

7 ответов

Чтобы получить список запущенных процессов на Android 1.6 - Android 6.0, вы можете использовать эту библиотеку, которую я написал: https://github.com/jaredrummler/AndroidProcesses Библиотека читает / proc для получения информации о процессе.

Google значительно ограничил доступ к / proc в Android Nougat. Чтобы получить список запущенных процессов на Android Nougat, вам нужно будет использовать UsageStatsManager или иметь root-доступ.

Нажмите историю изменений для предыдущих альтернативных решений.

 private String printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
        long time = System.currentTimeMillis();
        List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
        if (appList != null && appList.size() > 0) {
            SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
            for (UsageStats usageStats : appList) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e("adapter", "Current App in foreground is: " + currentApp);
    return currentApp;
}

Используйте этот метод для получения переднего плана. Вам понадобится разрешение системы "android:get_usage_stats"

public static boolean needPermissionForBlocking(Context context){
    try {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
        AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
        return  (mode != AppOpsManager.MODE_ALLOWED);
    } catch (PackageManager.NameNotFoundException e) {
        return true;
    }
}

Если пользователь включит это в настройках -> Безопасность-> приложение с доступом. После этого вы получите переднее задание. Подобный процесс Чистый matser от Cheetahamobile Google Play ссылку

Посмотрите на https://github.com/ricvalerio/foregroundappchecker, это может быть то, что вам нужно. Предоставляет пример кода и избавляет от необходимости реализовывать кросс-версию детектора переднего плана.

Вот два примера:

AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();

Или регулярно проверяйте:

AppChecker appChecker = new AppChecker();
appChecker
    .when("com.other.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .when("com.my.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .other(new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .timeout(1000)
    .start(this);

Я просто представляю потенциальную оптимизацию того, что я представляю себе, - это вставленный в копию фрагмент кода для определения самого популярного приложения на Android M.

это

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
    long time = System.currentTimeMillis();
    List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
    if (appList != null && appList.size() > 0) {
        SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
        for (UsageStats usageStats : appList) {
            mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
        }
        if (mySortedMap != null && !mySortedMap.isEmpty()) {
            currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
        }
    }
}

Можно упростить до этого

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
        Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            time - 1000 * 1000, time);
    if (appStatsList != null && !appStatsList.isEmpty()) {
        currentApp = Collections.max(appStatsList, (o1, o2) ->
            Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
    }
}

Я обнаружил, что использую этот код в двухсекундном цикле, и удивился, почему я использовал комплексное решение, которое было O(n*log(n)), когда в Collections.max() было доступно более простое решение, которое является O(n).

Google ограничил эту функциональность только для системных приложений. Как уже сообщалось в заявке об ошибке, вам потребуется разрешение REAL_GET_TASKS для доступа к нему.

Теперь у приложений должно быть... разрешение.REAL_GET_TASKS, чтобы иметь возможность получать информацию о процессах для всех приложений. Только информация о процессе для вызывающего приложения будет возвращена, если приложение не имеет разрешения. Приложения с привилегиями временно смогут получать информацию о процессах для всех приложений, если у них нет нового разрешения, но они устарели... Разрешение. GET_TASKS Кроме того, только системные приложения могут получить разрешение REAL_GET_TASKS.

public class AccessibilityDetectingService extends AccessibilityService {

@Override
protected void onServiceConnected() {
    super.onServiceConnected();

    //Configure these here for compatibility with API 13 and below.

    AccessibilityServiceInfo config = new AccessibilityServiceInfo();
    config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
    config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

    if (Build.VERSION.SDK_INT >= 16)
        //Just in case this helps
        config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

    setServiceInfo(config);
}

@Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
        if (event == null ) {
            return;
        } else if(event.getPackageName() == null && event.getClassName() == null){
            return;
        }

            if (activityInfo != null){

                Log.d("CurrentActivity", componentName.flattenToShortString());
        }

}

private ActivityInfo tryGetActivity(ComponentName componentName) {
    try {
        return getPackageManager().getActivityInfo(componentName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
}
@Override
public void onInterrupt() {
}                
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />

Затем запустите службу и доступность приложения в настройках вашего устройства-> доступность-> Приложение для этой службы.

Пожалуйста, попробуйте использовать getRunningServices() вместо getRunningAppProcesses() метод.

 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);
Другие вопросы по тегам