Search code examples
androidandroid-9.0-pieaccessibilityserviceandroid-8.1-oreo

How to click in UI window on Android Oreo (and higher versions) with AccessibilityService?


I have followed this suggestion to uninstall my app programatically. Everything works fine till Android 7.1. But, when I tested the same on Android 8.1 and 9, this seems to not work, because no click is performed on the UI window.
What is the bug in this case? Has anything changed in these newer versions?

MyAccessibility:

public void onAccessibilityEvent(AccessibilityEvent event) {

 AccessibilityNodeInfo source = event.getSource();

    if (source == null) {
        return;
    }
    
    if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {

        List<AccessibilityNodeInfo> list = source.findAccessibilityNodeInfosByViewId("android:id/button1");
        for (AccessibilityNodeInfo node : list) {
            node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        }
   }

}

XML (main):

<service
            android:name=".MyAccessibility"
            class=".MyAccessibility"
            android:enabled="true"
            android:exported="false"
            android:label="@string/accessibility_service_label"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action
                    android:name=".MyAccessibility"
                    android:value=".MyAccessibility" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>

            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config" />
        </service>

XML (AccessibilityService):

<?xml version="1.0" encoding="utf-8"?>

<accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews|flagReportViewIds"
    android:canPerformGestures="true"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description"
    android:notificationTimeout="100"
    tools:ignore="UnusedAttribute" />

Test environment:

  • Motorola G5(S) Plus - Android 7.1 and 8.1 (Updated)
  • Motorola G7 Plus - Android 9.0

Solution

  • I did some tests and figured out when trying to uninstall an application on Android 8 and later, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED event fires but AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED event doesn't fire. In addition, when you call AccessibilityEvent.getSource() method on an event of AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED type, it returns null. So I used getRootInActiveWindow() method when AccessibilityEvent.getSource() method returns null and in addition on API 26 and above, I tried to handle AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED event as well.

    Please try this code:

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    
        AccessibilityNodeInfo source = event.getSource();
    
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && source == null)
            source = getRootInActiveWindow();
    
        if (source == null) {
            return;
        }
    
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            List<AccessibilityNodeInfo> list = source.findAccessibilityNodeInfosByViewId("android:id/button1");
            for (AccessibilityNodeInfo node : list) {
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }
    

    I modified accessibility_service_config.xml file as well for more accuracy.

    <accessibility-service
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
        android:accessibilityFeedbackType="feedbackAllMask"
        android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews|flagReportViewIds"
        android:canPerformGestures="true"
        android:canRetrieveWindowContent="true"
        android:description="@string/accessibility_service_description"
        android:notificationTimeout="100"
        tools:ignore="UnusedAttribute"
        android:packageNames="com.google.android.packageinstaller, com.android.packageinstaller"/>
    

    I have tested this code on API 24, API 27, API 28 and API 29 and it worked.