Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 350
android developer Avatar asked Apr 12 '21 17:04

android developer


People also ask

How do I know which app is drawing on my screen?

Check Apps Allowed To Draw Over The Screen So, find out which apps have permission to draw over the screen, as explained follows: Step 1: Go to Settings on your Android smartphone. Step 2: Navigate to Apps and Notification > Special app access. Step 3: Here, look for Display over the apps.

What is Retrieve window content?

RETRIEVE_WINDOW_CONTENT. retrieve screen content. Allows the app to retrieve the content of the active window. Malicious apps may retrieve the entire window content and examine all its text except passwords.


Video Answer


2 Answers

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>
like image 75
Thomas Avatar answered Oct 16 '22 17:10

Thomas


The answer is: You must use WindowManager which puts views to a window in the service to draw it on top of other apps.

    val typeApplication =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        else
            WindowManager.LayoutParams.TYPE_PHONE

    val layoutParams = WindowManager.LayoutParams(
        windowWidth,
        ViewGroup.LayoutParams.WRAP_CONTENT,
        typeApplication,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT
    )
    // inflater view with above layoutParams variable

And the application granted the Overlay permission and make sure accessibility is enabled in the device setting (Setting-> Accessibility -> Enable your app). Or use this intent to go to it

Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)

. You can check the Overlay permission by the following code

  // check
  Settings.canDrawOverlays(applicationContext)
  // Use this intent to enable permision
  Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
         Uri.parse("package:$packageName"))

Does it work the same as SAW permission? Can you, for example, handle touch events on what it draws? I've never used SAW before so I'm not sure about this question.

Hope can help you!

like image 1
Wilson Tran Avatar answered Oct 16 '22 19:10

Wilson Tran