Search code examples
androidaccessibilityservicesystem-alert-window

How does AccessibilityService draw on top of other apps?


Background

SAW (system alert window) permission can be used to draw content on top of other apps.

I was told a very long time ago that accessibility service can do this too, but I never found any tutorial, sample, documentation and even an app that does it... until recently:

https://play.google.com/store/apps/details?id=com.github.ericytsang.screenfilter.app.android

In fact, this app seems to be able to draw everywhere, as opposed to SAW permission. It draws even on top of the settings app and system dialogs, while SAW permission isn't allowed as such.

The problem

Sadly, as accessibility is quite a unique and rarely thing to use, just as I wrote, I couldn't find how such a thing works with drawing on top of other apps.

What I've found

The only thing I know is that this app somehow does it, and this is what it shows when it asks to grant it:

enter image description here

But that's not enough. I know that for some old POC I've made of using accessibility service, it showed the same, and checking it out, I can't see what triggers it. Pretty sure it's the minimal thing the users will see for any kind of accessibility service, so this won't help.

The questions

  1. How does AccessibilityService draw on top of other apps?

  2. Does it work the same as SAW permission ? Can you, for example, handle touch events on what it draws?

  3. What are the restrictions of using it, if there are any ?


Solution

  • Using TYPE_ACCESSIBILITY_OVERLAY as type in the WindowManager.LayoutParams when adding the view from within the accessibility service seems to do the trick. I did a quick test and the overlay window was shown even in the settings menu. The overlay window also received touch events. This worked also without the SYSTEM_ALERT_WINDOW permission in the manifest and also without setting the "Display over other apps" permission interactively by the user. I did my testing using target SDK 29.

    Sorry, I cannot answer to your third question about what specific restrictions apply.


    EDIT: By looking at the old tutorial of Google here, here's a short sample:

    GlobalActionBarService.java

    public class GlobalActionBarService extends AccessibilityService {
        FrameLayout mLayout;
    
        @Override
        protected void onServiceConnected() {
            // Create an overlay and display the action bar
            WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
            mLayout = new FrameLayout(this);
            WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
            lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
            lp.format = PixelFormat.TRANSLUCENT;
            lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            lp.gravity = Gravity.TOP;
            LayoutInflater inflater = LayoutInflater.from(this);
            inflater.inflate(R.layout.action_bar, mLayout);
            wm.addView(mLayout, lp);
        }
    
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
        }
    
        @Override
        public void onInterrupt() {
        }
    }
    

    manifest

    <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
        package="com.lb.myapplication">
    
        <application
            android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"
            android:theme="@style/Theme.MyApplication" tools:ignore="AllowBackup">
            <activity
                android:name=".MainActivity" android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <service
                android:name=".GlobalActionBarService" android:exported="false"
                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
                <intent-filter>
                    <action android:name="android.accessibilityservice.AccessibilityService" />
                </intent-filter>
                <meta-data
                    android:name="android.accessibilityservice" android:resource="@xml/global_action_bar_service" />
            </service>
        </application>
    
    </manifest>
    

    global_action_bar_service.xml

    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault"
        android:canPerformGestures="true" android:canRetrieveWindowContent="true" />
    

    action_bar.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
        android:layout_height="wrap_content" android:orientation="horizontal">
    
        <Button
            android:id="@+id/power" android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="@string/power" />
    
        <Button
            android:id="@+id/volume_up" android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="@string/volume" />
    
        <Button
            android:id="@+id/scroll" android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="@string/scroll" />
    
        <Button
            android:id="@+id/swipe" android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="@string/swipe" />
    </LinearLayout>