AccessibilityService - performGlobalAction не работает в собственном приложении

Я пытаюсь отправить системное событие возврата через AccessibilityService, и это работает нормально, но только если я не в своем собственном приложении.

Я всегда получаю true от performGlobalAction независимо от того, нахожусь я в собственном приложении или нет, но я вижу, что событие действительно выполняется, только если я не нахожусь в своем собственном приложении, а в любом другом (в смысле что отображается предыдущее действие или подобное)

Есть идеи, почему это происходит? Мое приложение представляет собой приложение боковой панели с оверлеем, нарисованным сверху в WindowManager, и все работает (AccessibilityService запущен и обрабатывает мои пользовательские события, и служба всегда возвращает сообщения об успешном завершении моих событий, но мое собственное приложение не реагирует на обратную связь событие кнопки).

Мой сервис выглядит следующим образом:

public class MyAccessibilityService extends AccessibilityService {

    public static void sendBackIntent(Context context) {
        Intent intent = new Intent(context, MyAccessibilityService.class);
        intent.putExtra("action", GLOBAL_ACTION_BACK);
        context.startService(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Bundle extras = intent.getExtras();
        Integer action = null;
        if (extras != null) {
            action = extras.getInt("action");
        }

        if (action != null) {
            switch (action) {
                case GLOBAL_ACTION_BACK:
                    boolean result = performGlobalAction(action);
                    L.d("Action %d executed: %b", action, result);
                    break;
                default:
                    L.e("Unhandled action %d", action);
                    break;
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

    }

    @Override
    public void onInterrupt() {
    }
}

Изменить

Чтобы прояснить это:

  • Я НЕ запускаю эту службу через MyAccessibilityService.sendBackIntent(context), я отправляю следующее намерение: if (isAccessibilityserviceRunning) MyAccessibilityService.sendBackIntent(context)
  • Я запускаю свой сервис через системное сервисное меню, просто включив его там, а затем позволяю системе запускать его автоматически.
  • Я настроил все для AccessibilityService в accessibilityservice.xml и использую это для определения настроек моих служб, и это тоже работает отлично, все события, которые я хочу получать, принимаются надежно и правильно

ИЗМЕНИТЬ 2

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


person prom85    schedule 13.09.2017    source источник


Ответы (1)


Мне кажется, что вы делаете очень странные вещи. Похоже, вы относитесь к своему AccessibilityService как к нормальному Service. Часть этого предполагает, что это ваша реализация следующих методов:

public static void sendBackIntent(Context context);

@Override
public int onStartCommand(Intent intent, int flags, int startId);

Просто по сигнатурам этих двух методов и вашему вызову

context.startService(intent);    

В рамках вашего статического метода я могу сказать, что вы не понимаете AccessibilityServices и то, как они должны выполнять свою работу. Вы не можете запустить службу специальных возможностей или взаимодействовать с ней так, как вы пытаетесь. Конечно, вы можете использовать службы специальных возможностей для выполнения глобальных действий, но они не будут делать это точно и глобально, если вы не запустите их правильно из меню служб специальных возможностей (вы знаете, где отображается TalkBack).

По сути, ваш код не работает в контексте, в котором вы думаете, что он работает. Итак, он работает и делает что-то. Но AccessibilityServices и их соответствующие возможности заключаются в их способности глобально подключаться к Операционной системе. API Android не будет правильно связывать AccessibilityService, когда вы пытаетесь запустить свою службу с помощью:

context.startService(intent);

Вам необходимо запустить службу специальных возможностей из меню настроек служб специальных возможностей.

Даже если ваш сервис уже запущен, такой звонок небезопасен! Нет никакой гарантии, что ваши пользователи будут запускать службу до открытия вашей Activity. После того, как вы вызвали context.startService и попытались запустить службу AccessibilityService таким образом, это помешает меню настроек специальных возможностей запустить службу и правильно привязать ее к ОС. Фактически, однажды в этой ситуации пользователю придется: Выключить коммутатор для вашей службы в меню настроек специальных возможностей, принудительно остановить (возможно, даже удалить) ваше приложение, перезагрузить свое устройство, запустить вашу службу и ЗАТЕМ начать свою деятельность в чтобы добиться правильного поведения.

Если вы этого не сделаете, он не будет правильно связываться с ОС и его поведение не определено. Прямо сейчас вы, по сути, создали хак в ОС и столкнулись с указанным неопределенным поведением, которое может ШИРОКО варьироваться в зависимости от версии, производителя и т. Д., Потому что это поведение не рассматривается в интеграционных тестах AOSP.

Фактически, вы НЕ МОЖЕТЕ запускать службы доступности с помощью вызова context.startService(). Это очень важная функция безопасности Android, поскольку службы доступности могут получать доступ к содержимому экрана, а пользователям необходим точный контроль над поставщиками и приложениями, которым они разрешают этот доступ. Итак, хотя вы можете получить НЕКОТОРЫЕ поведение, это неопределенное и опасное поведение. Вам нужно что-то вроде следующего:

Со следующим XML-кодом конфигурации службы:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeWindowContentChanged"
    android:accessibilityFlags="flagRequestTouchExplorationMode"
    android:canRetrieveWindowContent="true"
    android:canRequestTouchExplorationMode="true"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="100"
    android:settingsActivity="com.service.SettingsActivity"
    />

И следующий сервис доступности.

class MyA11yService extends AccessibilityService {
    @Override public boolean onGesture(int gestureId) {
        switch (gestureId) {
            case GESTURE_SWIPE_UP_AND_DOWN:
                CLog.d("Performing gesture.");
                performGlobalAction(GLOBAL_ACTION_BACK);
                return true;

            default:
                return false;
        }
    }
}

Вызов performGlobalAction отлично работает в любом Context. Теперь, вместо того, чтобы выполнять это действие для жеста SWIPE_UP_DOWN, вы хотите настроить своего рода межпроцессное взаимодействие с той частью этого, которая должна запускать действие «глобальной кнопки возврата». Но эта информация предназначена для другого вопроса, хотя, если вы понимаете информацию в этом посте, я уверен, что вам нужно действовать, будет ясно.

person ChrisCM    schedule 14.09.2017
comment
Комментарии не предназначены для расширенного обсуждения или сеансов отладки; этот разговор был перемещен в чат. Если здесь есть важная информация, ее следует отредактировать в вопросе или ответе. - person Cody Gray; 14.09.2017
comment
Итак, в какой-то момент этой угрозы я опубликовал комментарий об удалении комментариев после того, как переместил часть этого контента. Кто-то его атаковал, и теперь его больше нет. Будем признательны за предупреждения перед удалением такого большого количества контента. @ prom85: если у вас есть еще один вопрос, который вы задаете в другом сообщении, я думаю, что это правильный ответ (или, по крайней мере, информация, которую должны видеть другие, которые путают с подобными проблемами) для этого конкретного вопроса. - person ChrisCM; 14.09.2017
comment
Я не вижу ничего, что вы писали об этом среди удаленных комментариев. В любом случае информация не была удалена; это было заархивировано в чате. Нет реального способа предупредить вас об этом, кроме как оставить другой шумный комментарий, что нам настоятельно не рекомендуется делать. Мы получаем автоматические флажки, когда цепочка комментариев становится слишком длинной, и предлагаемый курс действий - переместить ее в чат. Если у вас есть предложения по улучшению рабочего процесса или проблема с политикой, вы можете задать вопрос на странице Meta Stack Overflow. Вы получите сообщение о том, что комментарии носят временный характер и не обсуждаются. - person Cody Gray; 15.09.2017