Android: How do you check if a particular AccessibilityService is enabled

55.6k views Asked by At

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?

9

There are 9 answers

5
Andrew On BEST ANSWER

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;
}
0
alexal1 On

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.

0
Banana droid On

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:{}]
1
Priyank Patel On
 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

1
wanghy On

this need more times

AccessibilityManager am = (AccessibilityManager) context
        .getSystemService(Context.ACCESSIBILITY_SERVICE);
4
DarthJaiz On

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.

4
Sam On

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.

3
Martin On

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);
0
polis On

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 {
      
   }
}