I can verify that onAccessibilityEvent and onServiceConnected functions are triggered, but nothing expected happens.
Here is my AndroidManifest.XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.telephony"
android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="replace" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="replace" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
tools:ignore="ProtectedPermissions" />
<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.DDDsms">
<receiver android:name=".SMSReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<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="com.example.DDDsms.MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:label="@string/accessibility_service_label"
android:exported="false"
tools:ignore="Instantiatable"
android:enabled="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config"/>
</service>
</application>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission,WrongManifestParent" />
</manifest>
Here is my accessibility_service_config.XML
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:packageNames="DDDsms"
android:notificationTimeout="100"
android:canRequestFilterKeyEvents="true"
android:accessibilityFlags="flagRequestFilterKeyEvents"
android:description="@string/Force_stop" />
Here is my MyAccessibilityService.kt
package com.example.DDDsms
import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.AccessibilityServiceInfo
import android.annotation.SuppressLint
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
@SuppressLint("AccessibilityPolicy")
class MyAccessibilityService : AccessibilityService() {
override fun onAccessibilityEvent(event: AccessibilityEvent) {
//TYPE_WINDOW_STATE_CHANGED == 32
if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event
.eventType
) {
val nodeInfo = event.source ?: return
var list = nodeInfo.findAccessibilityNodeInfosByText("Force stop")
//var list = nodeInfo.findAccessibilityNodeInfosByViewId("com.android.settings:id/force_stop_button")
//We can find button using button name or button id
for (node in list) {
node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
//list = nodeInfo
// .findAccessibilityNodeInfosByViewId("android:id/button1")
list = nodeInfo.findAccessibilityNodeInfosByText("OK");
for (node in list) {
node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
}
}
override fun onInterrupt() {
// TODO Auto-generated method stub
}
// Override onServiceConnected to configure service if needed
override fun onServiceConnected() {
super.onServiceConnected()
// Configuration can be done here or in XML metadata
val info = AccessibilityServiceInfo()
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
info.notificationTimeout = 100
info.feedbackType = AccessibilityEvent.TYPES_ALL_MASK
setServiceInfo(info)
}
}
Here is how I get Accessibility Service started by opening my app info INTENT. ...
checkAndEnableAccessibilityService()
val QuitBtn: Button = findViewById(R.id.quitBtn)
QuitBtn.bringToFront()
QuitBtn.setOnClickListener {
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:DDDsms")
)
startActivityForResult(intent, 0)
}
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
}
...
private fun checkAndEnableAccessibilityService() {
if (!isAccessibilityServiceEnabled()) {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
startActivity(intent)
}
}
private fun isAccessibilityServiceEnabled(): Boolean {
return Settings.Secure.getString(contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
?.contains("${packageName}/${MyAccessibilityService::class.java.canonicalName}") == true
}
Everything works as expected, except Force stop button not clicked. In fact, onAccessibilityEvent is triggered, but it doesn't go beyond this line: val nodeInfo = event.source ?: return Basically, I want my app to automatically click on "Force stop" button, when I click on the Quit button on my app, even though AppInfo Intent does popup. Am I missing something here?
I finally figured out why my accessibility service wasn't doing what was expected of it. It is true Google has disallowed a lot of the system functions from direct access from users' app for security reasons, but since many apps already in Google PlayStore can do this, I figured my app should be able to do this as well.
The reason my app/accessibility service couldn't click on the AppInfo Intent's "Force stop" button and kept returning NULL was because the service needed android.R.styleable.AccessibilityService_canRetrieveWindowContent property set to true in the accessibility_service_config.XML as follows.
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:packageNames="DDDsms"
android:notificationTimeout="100"
android:canRequestFilterKeyEvents="true"
android:accessibilityFlags="flagRequestFilterKeyEvents"
android:description="@string/Force_stop"
android:canRetrieveWindowContent="true" <<<<<<<<<<<<<< Add this line here.
android:settingsActivity="com.example.DDDsms.MyAccessibilityService"/>
I came across this information when I debugged my accessibility service line by line as explained by the debugger.
public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow()
Gets the root node in the currently active window if this service can retrieve window content. The active window is the one that the user is currently touching or the window with input focus, if the user is not touching any window. It could be from any logical display.
Note: In order to access the root node your service has to declare the capability to retrieve window content by setting the android.R.styleable.AccessibilityService_canRetrieveWindowContent property in its meta-data. For details refer to SERVICE_META_DATA.
Returns:
The root node if this service can retrieve window content.
Hopefully, this helps someone.