55

I've written an Android app that requires the use of the AccessibilityService. I know how to check to see if Accessibility is enabled or disabled on the phone, but I cannot work out a way to determine if my app has been specifically enabled within the accessibility menu.

I'm wanting to prompt the user if the AccessibilityService is not running, but can't find a good way of doing this. Is there any API methods that I might be missing that would let me know which accessibility services are enabled on the device?

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Andrew
  • 7,548
  • 7
  • 50
  • 72

9 Answers9

70

Since API Level 14, it is also possible to obtain the enabled accessibility services through the AccessibilityManager:

public static boolean isAccessibilityServiceEnabled(Context context, Class<? extends AccessibilityService> service) {
    AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
    List<AccessibilityServiceInfo> enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);

    for (AccessibilityServiceInfo enabledService : enabledServices) {
        ServiceInfo enabledServiceInfo = enabledService.getResolveInfo().serviceInfo;
        if (enabledServiceInfo.packageName.equals(context.getPackageName()) && enabledServiceInfo.name.equals(service.getName()))
            return true;
    }

    return false;
}

Usage:

boolean enabled = isAccessibilityServiceEnabled(context, MyAccessibilityService.class);
azizbekian
  • 60,783
  • 13
  • 169
  • 249
Martin
  • 1,101
  • 10
  • 6
  • 2
    A typo in your 1st part example code. The getEnabledAccessibilityServiceList(AccessibilityEvent.TYPES_ALL_MASK) should be changed as getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) The 2nd parameter of getEnabledAccessibilityServiceList() is "feedbackTypeFlags". – Sam Lu May 02 '16 at 00:30
  • Teh ACTUAL solution! – BamsBamx Sep 30 '16 at 14:34
  • 10
    Actually, this is not working, because I can see my accessibility service is turned on, but `getEnabledAccessibilityServiceList()` returns an empty list (running on Pixel 2 emulator API 28). – azizbekian Apr 17 '19 at 08:52
68

I worked this one out myself in the end:

public boolean isAccessibilityEnabled() {
    int accessibilityEnabled = 0;
    final String LIGHTFLOW_ACCESSIBILITY_SERVICE = "com.example.test/com.example.text.ccessibilityService";
    boolean accessibilityFound = false;
    try {
        accessibilityEnabled = Settings.Secure.getInt(this.getContentResolver(),android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
        Log.d(LOGTAG, "ACCESSIBILITY: " + accessibilityEnabled);
    } catch (SettingNotFoundException e) {
        Log.d(LOGTAG, "Error finding setting, default accessibility to not found: " + e.getMessage());
    }

    TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');

    if (accessibilityEnabled==1) {
        Log.d(LOGTAG, "***ACCESSIBILIY IS ENABLED***: ");

        String settingValue = Settings.Secure.getString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
        Log.d(LOGTAG, "Setting: " + settingValue);
        if (settingValue != null) {
             TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
             splitter.setString(settingValue);
             while (splitter.hasNext()) {
                 String accessabilityService = splitter.next();
                 Log.d(LOGTAG, "Setting: " + accessabilityService);
                 if (accessabilityService.equalsIgnoreCase(ACCESSIBILITY_SERVICE_NAME)){
                     Log.d(LOGTAG, "We've found the correct setting - accessibility is switched on!");
                     return true;
                 }
             }
        }

        Log.d(LOGTAG, "***END***");
    }
    else {
        Log.d(LOGTAG, "***ACCESSIBILIY IS DISABLED***");
    }
    return accessibilityFound;
}
Zsolt Safrany
  • 13,290
  • 6
  • 50
  • 62
Andrew
  • 7,548
  • 7
  • 50
  • 72
  • 3
    I would recommend just using the code from splitter and below. The upper code is waaaaay too expensive. This was the reason my app was sooo slow and didn't know the reason. – Andreas Lymbouras Jun 28 '14 at 23:11
  • @Andrew I am getting `null` for `settingValue`. Not sure why is that. I have permission included. I'm trying to find a way to check is `color correction` enabled. – Carrie May 25 '16 at 20:53
  • See [my answer](http://stackoverflow.com/a/40568194/238753) for a version of this code that's less messy and closer to the Android implementation. – Sam Nov 12 '16 at 22:09
  • @YabinSong, this question is about accessibility *services*, but colour correction looks like a normal accessibility *setting*. – Sam Nov 13 '16 at 00:58
  • But in android 8.0 its not working its ask for permission and is enabled but when killed app permission become off each time its ask permission when kill permission can you please help me for this solution – MARSH Aug 08 '22 at 11:16
37

Checking if the service is enabled

/**
 * Based on {@link com.android.settingslib.accessibility.AccessibilityUtils#getEnabledServicesFromSettings(Context,int)}
 * @see <a href="https://github.com/android/platform_frameworks_base/blob/d48e0d44f6676de6fd54fd8a017332edd6a9f096/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java#L55">AccessibilityUtils</a>
 */
public static boolean isAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
    ComponentName expectedComponentName = new ComponentName(context, accessibilityService);

    String enabledServicesSetting = Settings.Secure.getString(context.getContentResolver(),  Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
    if (enabledServicesSetting == null)
        return false;

    TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
    colonSplitter.setString(enabledServicesSetting);

    while (colonSplitter.hasNext()) {
        String componentNameString = colonSplitter.next();
        ComponentName enabledService = ComponentName.unflattenFromString(componentNameString);

        if (enabledService != null && enabledService.equals(expectedComponentName))
            return true;
    }

    return false;
}

Usage:

boolean enabled = isAccessibilityServiceEnabled(context, MyAccessibilityService.class);

Detecting when the service is enabled or disabled

Make a callback:

ContentObserver observer = new ContentObserver() {
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        boolean accessibilityServiceEnabled = isAccessibilityServiceEnabled(context, MyAccessibilityService.class);
        //Do something here
    }
};

Subscribe:

Uri uri = Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
context.getContentResolver().registerContentObserver(uri, false, observer);

Unsubscribe when you're done:

context.getContentResolver().unregisterContentObserver(observer);

Note that this doesn't work with the getEnabledAccessibilityServiceList() approach since its values are out-of-sync with the Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES values. That's why I think using Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES is a better one-size-fits-all approach.

Sam
  • 40,644
  • 36
  • 176
  • 219
2

Get the ID when your activity service just started. In your Activity service OnSeriviceCeonnected after all the initialize calls. Use this...

AccessibilityServiceInfo serviceInfo = this.getServiceInfo();
    String accessibilityId = serviceInfo.getId();

Requires Jelly Bean API

Then you can use Martin's code (isAccessibilityEnabled) to check running services.

DarthJaiz
  • 29
  • 3
  • Another option (which I used) is to compare the serviceInfo's Description string with the Description used to configure the Accessibility service. The ID cannot be obtained if the Service has not started yet, which only happens when the Accessibility permission is provided. The description can just be loaded from resources however. – sparkonhdfs Jun 16 '16 at 18:54
  • This works, but is more complicated than necessary; the following approaches avoid the need to store the service's id: 1. Querying the `Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES` setting; 2. Calling `getEnabledAccessibilityServiceList()` and matching the `resolveInfo` against the service's `ComponentName`. – Sam Nov 12 '16 at 21:21
  • @DarthJaiz won't work when device restarts, serviceInfo is null – Jack Oct 24 '18 at 07:46
  • @Sam could you explain more your approach – Jack Oct 24 '18 at 07:46
1

Maybe's too late but here's how I check the Accessibility Service's status:

$ adb shell dumpsys accessibility

Result:

ACCESSIBILITY MANAGER (dumpsys accessibility)
User state[attributes:{id=0, currentUser=true, accessibilityEnabled=false, touchExplorationEnabled=false, displayMagnificationEnabled=false}
           services:{}]
Banana droid
  • 690
  • 1
  • 9
  • 27
0

I was looking for the same solution and decided to do this check in my AccessabilityService. It works for me and maybe will be helpful for somebody else.

class AppConnectorService : AccessibilityService() {

    companion object {
        val connected = MutableStateFlow(false)
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
        Timber.d("!!! $event")
    }

    override fun onInterrupt() {
        TODO("Not yet implemented")
    }

    override fun onServiceConnected() {
        super.onServiceConnected()
        connected.tryEmit(true)
        Timber.d("!!! AccessibilityService is connected!!!!!")
    }

    override fun onDestroy() {
        super.onDestroy()
        connected.tryEmit(false)
        Timber.d("!!! AccessibilityService is destroyed!!!!")
    }
}

And to check and listen for changes just use. In Compose:

val isConnected by AppConnectorService.connected.collectAsState()

or coroutine:

coroutineScope.launch {
   AppConnectorService.connected.collect {
      
   }
}
polis
  • 737
  • 7
  • 6
0

A good solution I've come up with is to run your AccessibilityService in a separate process. You can add an android:process attribute in the manifest, e.g.

<service
        android:name=".ExampleService"
        android:process="com.example.service"
        ...

Now your service will be running in a separate process with the given name. From your app you can call

val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.runningAppProcesses.any { it.processName == "com.example.service" }

Which will return true if the service is running and false otherwise.

IMPORTANT: note that it will show you when your service was started, but when you disable it (meaning, after system unbinds from it) the process can still be alive. So you can simply force it's removal:

override fun onUnbind(intent: Intent?): Boolean {
    stopSelf()
    return super.onUnbind(intent)
}

override fun onDestroy() {
    super.onDestroy()
    killProcess(Process.myPid())
}

Then it works perfectly. I see this method more robust than reading values from settings, because it shows the exact thing needed: whether the service is running or not.

alexal1
  • 394
  • 3
  • 7
-1
 AccessibilityManager accessibilityManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE);
        List<AccessibilityServiceInfo> runningservice = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);

        accessibilityManager.addAccessibilityStateChangeListener(new AccessibilityManager.AccessibilityStateChangeListener() {
            @Override
            public void onAccessibilityStateChanged(boolean b) {
                Toast.makeText(MainActivity.this, "permission "+b, Toast.LENGTH_SHORT).show();
            }
        });

Listner will be called whenever state is changed you can keep track of the boolean to check the permission this is by far the simplest and lightest solution to check permission

StarsSky
  • 6,721
  • 6
  • 38
  • 63
  • Your answer focuses on checking the phone's accessibility state, but the question mentions `I know how to check to see if Accessibility is enabled or disabled on the phone`. However, `getEnabledAccessibilityServiceList()` looks closer to what he wants. – Sam Nov 12 '16 at 21:19
-5

this need more times

AccessibilityManager am = (AccessibilityManager) context
        .getSystemService(Context.ACCESSIBILITY_SERVICE);
wanghy
  • 1
  • and needs further check to determine if a serivce is enabled or not accessibilityManager.getEnabledAccessibilityServiceList(1), on could also rely on onAccessibilityStateChanged listener – display name Mar 09 '18 at 06:48