Bind to Accessibility Service in Android Sep 18th 2020 Words: 294

Background

I’m building an Android app that simulates user input. Accessibility is a great non-root solution thanks to continued gesture introduced in API 26.

However, the lifecycle of the accessibility service is almost controlled by system alone, and onAccessibilityEvent seems to be the only place to implement the workflow. This may be enough for apps that simply does “find a button in the view and click it”, but for my app, where all the logic is implemented somewhere else, I have to communicate to accessibility service in another component. For example, one activity sends the commands “tap at (123,456) for me”, and my accessibility should executes this.

Solution

My solution is to use static variable to access the instance of accessibility service, although it is not elegant.

MyAccessibilityService.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.myapp

import android.accessibilityservice.AccessibilityService
import android.content.Intent
import android.view.accessibility.AccessibilityEvent

class MyAccessibilityService : AccessibilityService() {
companion object {
var instance: MyAccessibilityService? = null
}

override fun onServiceConnected() {
instance = this
super.onServiceConnected()
}

override fun onUnbind(intent: Intent?): Boolean {
instance = null
return super.onUnbind(intent)
}

override fun onDestroy() {
instance = null
super.onDestroy()
}

override fun onAccessibilityEvent(event: AccessibilityEvent?) {}

override fun onInterrupt() {}
}

Now add an auxillary service named MyService and use it as a proxy to access methods in MyAccessibilityService, now you you can do anything to MyService, for example, implement AIDL to bind it remotely.

MyService.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.myapp

import android.app.Service
import android.content.Intent
import android.os.IBinder

class InputService : Service() {
private val binder = object : IServiceInterface.Stub() {
override fun isReady(): Boolean {
return MyAccessibilityService.instance !== null
}

override fun foo(bar: int) {
TODO("Do something use MyAccessibilityService.instance")
}
}

override fun onBind(intent: Intent): IBinder {
return binder;
}
}