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

9

58

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?

Swoon answered 22/2, 2011 at 16:48 Comment(0)
S
69

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;
}
Swoon answered 24/2, 2011 at 15:5 Comment(5)
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.Romans
@Swoon 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.Duly
See my answer for a version of this code that's less messy and closer to the Android implementation.Confiscate
@YabinSong, this question is about accessibility services, but colour correction looks like a normal accessibility setting.Confiscate
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 solutionEscuage
G
75

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);
Gorrono answered 17/2, 2013 at 16:14 Comment(3)
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".Vulgarian
Teh ACTUAL solution!Siu
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).Swingletree
S
69

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;
}
Swoon answered 24/2, 2011 at 15:5 Comment(5)
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.Romans
@Swoon 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.Duly
See my answer for a version of this code that's less messy and closer to the Android implementation.Confiscate
@YabinSong, this question is about accessibility services, but colour correction looks like a normal accessibility setting.Confiscate
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 solutionEscuage
C
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.

Confiscate answered 12/11, 2016 at 22:7 Comment(4)
best answer so farMegathere
Doesn't work for xiaomi(MiUi) accesibility service. While this method returns true the service is not startedKriemhild
@Beloo, this answer is about whether the service is enabled, not whether it is startedConfiscate
@Confiscate If the service is enabled by the user doesn't that mean it's started? If not what is the difference?Snapper
B
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.

Bishop answered 9/2, 2015 at 17:40 Comment(4)
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.Tal
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.Confiscate
@Bishop won't work when device restarts, serviceInfo is nullCarpal
@Confiscate could you explain more your approachCarpal
C
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:{}]
Camp answered 6/8, 2018 at 7:20 Comment(0)
W
1

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 {
      
   }
}
Worry answered 26/9, 2022 at 15:17 Comment(0)
R
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.

Reprobate answered 4/12, 2022 at 16:59 Comment(0)
D
-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

Distrust answered 26/8, 2016 at 18:34 Comment(1)
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.Confiscate
C
-5

this need more times

AccessibilityManager am = (AccessibilityManager) context
        .getSystemService(Context.ACCESSIBILITY_SERVICE);
Curettage answered 23/2, 2014 at 10:30 Comment(1)
and needs further check to determine if a serivce is enabled or not accessibilityManager.getEnabledAccessibilityServiceList(1), on could also rely on onAccessibilityStateChanged listenerLyudmila

© 2022 - 2024 — McMap. All rights reserved.