Android的辅助功能accessibility的具体文档可以查看:google accessibility说明文档
accessibility是一个非常强大的功能,可以实现监听手机上的各种事件,比如窗口的变化,查找屏幕上当前显示的文字,以及模拟点击等功能,并且通过accessibility可以完成很多一般应用无法完成事件,比如发送物理或虚拟返回键的指令是通过如下代码实现的:
1 2 3 4 5 6 7 8 9 10 |
new Thread() { public void run() { try { Instrumentation inst = new Instrumentation(); inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); } catch (Exception e) { e.printStackTrace(); } } }.start(); |
另外辅助功能还可以实现各种脚本和无root权限伪静默安装.绿色守护的非root模式和uc的静默安装还有那些抢红包的应用都是用该方式实现的,不过如果一些恶意应用拿到了辅助功能的权限是灾难性的.
辅助功能的开启关闭是在Android的系统设置中的辅助功能或者叫无障碍选项中.
如何编写一个辅助功能服务 1.创建一个class继承于AccessibilityService,会强制重写2个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * 当系统检测到一个匹配你辅助服务过滤器中设置参数的AccessibilityEvent时,调用该方法,运行时多次调用 * * @param event 在用户交互使用时系统返回的event事件 */ @Override public void onAccessibilityEvent(AccessibilityEvent event)/** * 当你的服务对系统事件的反馈被中断时,调用该方法。该方法同上面方法一样,也是被多次调用的 */ @Override public void onInterrupt() |
- 可以在res中创建xml文件夹,创建一个xml文件,文件名随意,在xml文件中对辅助服务进行配置,格式大致如下:
1 2 3 4 5 6 7 8
- 重写onServiceConnected() (当应用成功连接到你的辅助性服务时,系统调用该方法)在该方法中进行配置
1 2 3 4 5 6 7
AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo(); serviceInfo.eventTypes = ...; serviceInfo.feedbackType = ...; serviceInfo.notificationTimeout = ...; serviceInfo.packageNames = new String[]{...}; serviceInfo.flags =...; setServiceInfo(serviceInfo);
-
accessibilityEventTypes / eventTypes
事件类型
typeAllMask / AccessibilityEvent.TYPES_ALL_MASK
全局事件响应typeViewClicked / AccessibilityEvent.TYPE_VIEW_CLICKED
点击事件
-
accessibilityFeedbackType / feedbackType
反馈方式
feedbackGeneric / AccessibilityServiceInfo.FEEDBACK_GENERIC
通用的反馈feedbackAudible / AccessibilityServiceInfo.FEEDBACK_AUDIBLE
声音反馈feedbackSpoken / AccessibilityServiceInfo.FEEDBACK_SPOKEN
语音反馈
-
notificationTimeout / notificationTimeout
响应毫秒值
-
packageNames
监听的应用的包名,可指定多个包名,xml文件中使用,隔开
-
accessibilityFlags / flags
用于之后node.getViewIdResourceName()
的权限
-
description
辅助功能的描述
-
canRetrieveWindowContent
从一个AccessibilityEvent中调查完全视图层级的能力隐式地暴露私有用户信息给你的辅助服务,必须通过配置XML文件请求这个级别的访问权,不在你的服务配置xml文件中包含这个设置,那么对getSource()
的调用会失败。
1 2 3 4 5 6 7 8 |
|
label:对应了在系统辅助功能开关界面中,你的service的名字
description:则是点击对应的服务进入开关界面后,该服务的简介
permission:对应的权限(亦可在service中单独写出来)
1 |
|
intent-filter:指定了执行的组件为辅助功能类
如果是使用xml文件配置的辅助服务,还需要在service节点下添加meta-data节点,在resource指定自己编写的xml文件
1 2 3 |
|
可以使用 event.getEventType()来获取各种事件消息:
- 基本窗口view的变化都可以使用这个type来监听:
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED
- 打开popupwindow,菜单,对话框时候会触发:
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- 更加精确的代表了基于当前event.source中的子view的内容变化:
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
- 窗口的变化:
AccessibilityEvent.TYPE_WINDOWS_CHANGED
- 使用event获取:
AccessibilityNodeInfo nodeInfo = event.getSource();
- 在accessibility中直接获取:
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
nodeInfo.recycle();
接下来就可以使用下面的方法来获取对应的所有节点的集合
findAccessibilityNodeInfosByViewId(String str)
findAccessibilityNodeInfosByText(String str)
获取id的方法:
- 使用DDMS的hierarchy View来查找对应的viewId,但是有很多手机是没有办法获取,只会提示:Unable to get view server version from device XXXXX
- 在这个时候,在AccessibiltiyService的配置中添加的flag。flagReportViewIds就可以派上用场了
在窗口改变时,获取并遍历所有的node,即打印出node对应的文字和id
传入方法id的格式为: 应用的包名 + “:id/“ + 获取到的id
performAction(AccessibilityNodeInfo.ACTION_CLICK) //AccessibilityNodeInfo中有各种各样的事件以常量的形势声明. |
返回键
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
HOME键
performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
最近打开应用列表
performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
打开通知栏
performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
锁屏
performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
设置
performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
判断对应的辅助功能有没有打开的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public boolean isAccessibilitySettingsOn(Context mContext) { int accessibilityEnabled = 0; final String service = getPackageName() + "/" + MyAccessibilityService.class.getCanonicalName(); //这里改成自己的class try { accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED); } catch (Settings.SettingNotFoundException ignored) { } TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); if (accessibilityEnabled == 1) { String settingValue = https://www.it610.com/article/Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); if (settingValue != null) { mStringColonSplitter.setString(settingValue); while (mStringColonSplitter.hasNext()) { String accessibilityService = mStringColonSplitter.next(); if (accessibilityService.equalsIgnoreCase(service)) { return true; } } } } return false; } |
是否有自己,有就表示开了.(后面的用java程序悄悄打开对应的辅助功能其实就是去修改这个值)
打开系统设置辅助功能的界面:
1 2 |
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); startActivity(intent); |
在此之前先写一个简单的辅助功能应用:
- 编写一个类继承AccessibilityService,只在onStartCommand做一个返回键的全局操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
public class MyAccessibilityService extends AccessibilityService {@Override public void onAccessibilityEvent(AccessibilityEvent event) {}@Override public void onInterrupt() {}@Override public int onStartCommand(Intent intent, int flags, int startId) { performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); return super.onStartCommand(intent, flags, startId); } }
- xml配置最普通的内容
1 2 3 4 5 6 7 8
- 在清单文件进行注册
1 2 3 4 5 6 7 8 9 10 11 12
- 在Activity的xml文件中设置一个button,为了方便,声明onClick属性为onClick,在activity中编写onClick方法为如下内容
1 2 3 4 5 6 7 8
public void onClick(View view) { if (!isAccessibilitySettingsOn(this)) { Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); startActivity(intent); return; } startService(new Intent(this, MyAccessibilityService.class)); }
关闭该应用辅助功能,接下来就是编写能修改系统设置的纯java代码:使用adb的settings命令将设置的数据库中对应字段改变
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 32 |
public class Temp { public static void main(String[] args) { System.out.println("running"); //这里是包名+辅助功能类名 String cmd1 = "settings put secure enabled_accessibility_services com.nesscurie.accessibility/com.nesscurie.accessibility.MyAccessibilityService"; String cmd2 = "settings put secure accessibility_enabled 1"; execShell(cmd1); execShell(cmd2); }//运行命令行的方法 private static void execShell(String cmd) { try { Process p = Runtime.getRuntime().exec(cmd); BufferedReader br = new BufferedReader(new InputStreamReader( p.getInputStream())); String readLine = br.readLine(); while (readLine != null) { System.out.println(readLine); readLine = br.readLine(); } if (br != null) { br.close(); } p.destroy(); p = null; } catch (IOException e) { e.printStackTrace(); } } } |
adb shell cd data/local/tmp app_process -Djava.class.path=Temp.dex data/local/tmp Temp |
【Android Accessibility大致解析,通过adb运行纯java代码打开应用的辅助功能】可以看到打印出runnig后,系统设置的该应用的辅助功能界面刷新为开,本来是需要系统权限(设置运行在系统进程中,具有系统权限)才能进行修改的选项,就这么悄悄的打开了.