简介

由于许多Android用户由于某些原因(视力,身体,年龄)要求他们以不同的方式与手机设备交互。

安卓提供了辅助功能特性和服务来帮助这些用户更容易的操作他们的设备,包括文字转语音(原生不支持中文,国内ROM可能会有,我的测试OPPO自带中文哟!),触觉反馈,手势操作,轨迹球和手柄操作。开发者可以利用这些服务使得程序更好用,也可以创建自己的辅助服务。

随着Android版本的不断升级,Android Accessibility功能也越来越强大,Android 4.0版本以前,系统辅助服务功能比较单一,仅仅能过单向获取窗口元素信息,比如获取输入框用户输入内容。到Android 4.1版本以后(现在主流厂商的版本都在KITKAT4.4),系统辅助服务增加了与窗口元素的双向交互,此时可以通过辅助功能服务操作窗口元素,比如点击按钮等。

由于系统辅助服务能够实时获取您当前操作应用的窗口元素信息,这有可能给你带来隐私信息的泄露风险,比如获取非密码输入框的输入内容等。同时通过辅助功能也可以模拟用户自动化点击应用内元素,也会带来一定的安全风险。

已经有其他同学使用AccessibilityService实现了抢红包~以及自动安装等,大家可以自行百度。

demo示例说明

接下来我们将通过AccessibilityService实现点击元素之后语言反馈所点击的内容。

测试辅助抓包应用为【微信】

下载地址

链接:http://pan.baidu.com/s/1pJVyo0z 密码:00i6

Manifest声明

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.leestar.example.accessibilityservice"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="19"
  8. android:targetSdkVersion="21" />
  9. <application
  10. android:allowBackup="true"
  11. android:icon="@drawable/ic_launcher"
  12. android:label="@string/app_name"
  13. android:theme="@style/AppTheme" >
  14. <activity
  15. android:name=".MainActivity"
  16. android:label="@string/app_name" >
  17. <intent-filter>
  18. <action android:name="android.intent.action.MAIN" />
  19. <category android:name="android.intent.category.LAUNCHER" />
  20. </intent-filter>
  21. </activity>
  22. <service
  23. android:name=".AccessibilityServiceExample"
  24. android:label="@string/accessibility_service_label"
  25. android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
  26. <intent-filter>
  27. <action android:name="android.accessibilityservice.AccessibilityService" />
  28. </intent-filter>
  29. <meta-data
  30. android:name="android.accessibilityservice"
  31. android:resource="@xml/accessibility_service_config" />
  32. </service>
  33. </application>
  34. </manifest>

AccessibilityService的XML配置文件

保存路径为<project_dir>/res/xml/accessibility_service_config.xml

  1. <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:description="@string/accessibility_service_description"
  3. android:packageNames="com.tencent.mm"
  4. android:accessibilityEventTypes="typeAllMask"
  5. android:accessibilityFlags="flagDefault"
  6. android:accessibilityFeedbackType="feedbackSpoken"
  7. android:notificationTimeout="100"
  8. android:canRetrieveWindowContent="true"
  9. android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
  10. />

其中

android:packageNames为需要捕获的包名

android:accessibilityEventTypes为需要回调的事件

android:notificationTimeout为事件回调的延迟事件

其他具体含义请查询官方文档~

创建继承自AccessibilityService的服务类

具体见代码以及注释,里面也实现了语言反馈功能。

  1. package com.leestar.example.accessibilityservice;
  2. import android.accessibilityservice.AccessibilityService;
  3. import android.annotation.TargetApi;
  4. import android.content.Intent;
  5. import android.os.Build;
  6. import android.speech.tts.TextToSpeech;
  7. import android.util.Log;
  8. import android.view.accessibility.AccessibilityEvent;
  9. import android.view.accessibility.AccessibilityNodeInfo;
  10. public class AccessibilityServiceExample extends AccessibilityService {
  11. private AccessibilityNodeInfo rootNodeInfo;
  12. private TextToSpeech tts = null;
  13. private boolean ttsIsInit = false;
  14. /**
  15. * 可选。系统会在成功连接上你的服务的时候调用这个方法,在这个方法里你可以做一下初始化工作,例如设备的声音震动管理,
  16. * 也可以调用setServiceInfo()进行配置工作。
  17. */
  18. @Override
  19. protected void onServiceConnected() {
  20. // TODO Auto-generated method stub
  21. super.onServiceConnected();
  22. tts = new TextToSpeech(getApplicationContext(), new android.speech.tts.TextToSpeech.OnInitListener() {
  23. @Override
  24. public void onInit(int status) {
  25. if (status == TextToSpeech.SUCCESS) {
  26. ttsIsInit = true;
  27. }
  28. }
  29. });
  30. }
  31. /**
  32. * 可选。在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作。
  33. */
  34. @Override
  35. public boolean onUnbind(Intent intent) {
  36. // TODO Auto-generated method stub
  37. return super.onUnbind(intent);
  38. }
  39. /**
  40. * 根据XML的android:accessibilityEventTypes来进行事件回调,所有的业务逻辑都要在回调中完成
  41. */
  42. @TargetApi(Build.VERSION_CODES.KITKAT)
  43. @Override
  44. public void onAccessibilityEvent(AccessibilityEvent event) {
  45. rootNodeInfo = event.getSource();
  46. if (rootNodeInfo == null)
  47. return;
  48. switch (event.getEventType()) {
  49. case AccessibilityEvent.TYPE_VIEW_CLICKED:
  50. // 语音反馈
  51. if (ttsIsInit) {
  52. // event.getContentDescription根据触发事件节点来自动查找子树的ContentDescription
  53. //所以你会发现UIAutomatorViewer上的相关节点信息为空,但是event里赋值了。
  54. // event.getText同上
  55. if (event.getContentDescription() != null) {
  56. tts.speak(event.getContentDescription().toString(), TextToSpeech.QUEUE_ADD, null);
  57. } else if (event.getText() != null) {
  58. tts.speak(event.getText().toString(), TextToSpeech.QUEUE_ADD, null);
  59. }
  60. }
  61. // 获取精确节点信息,可以通过UIAutomatorViewer检索,实现相关模拟操作
  62. //AccessibilityNodeInfo提供了2种自动查找字节点的函数
  63. //rootNodeInfo.findAccessibilityNodeInfosByText
  64. //rootNodeInfo.findAccessibilityNodeInfosByViewId(viewId)
  65. if (rootNodeInfo.getChild(0) != null
  66. && rootNodeInfo.getChild(0).getClassName().equals("android.widget.TextView")
  67. && rootNodeInfo.getChild(0).getText() != null
  68. && rootNodeInfo.getChild(0).getText().toString().equals("我")) {
  69. // 如果发现按的是【我】,模拟全局返回按钮,即退出微信
  70. performGlobalAction(GLOBAL_ACTION_BACK);
  71. // 模拟点击元素
  72. // if (rootNodeInfo.isClickable()) {
  73. // rootNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  74. // }
  75. }
  76. break;
  77. default:
  78. break;
  79. }
  80. Log.i("leestar", AccessibilityEvent.eventTypeToString(event.getEventType()));
  81. Log.i("leestar", rootNodeInfo.getClassName().toString());
  82. }
  83. /**
  84. * 这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用多次。
  85. */
  86. @Override
  87. public void onInterrupt() {
  88. Log.i("leestar", "onInterrupt");
  89. }
  90. }

MainActivity检测服务是否开启

  1. package com.leestar.example.accessibilityservice;
  2. import java.util.List;
  3. import android.accessibilityservice.AccessibilityServiceInfo;
  4. import android.app.Activity;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.os.Bundle;
  8. import android.provider.Settings;
  9. import android.view.View;
  10. import android.view.WindowManager;
  11. import android.view.accessibility.AccessibilityManager;
  12. import android.widget.Button;
  13. public class MainActivity extends Activity {
  14. private Button button_switch;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState) {
  17. // TODO Auto-generated method stub
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20. button_switch = (Button) findViewById(R.id.button_switch);
  21. }
  22. @Override
  23. protected void onResume() {
  24. super.onResume();
  25. updateServiceStatus();
  26. }
  27. @Override
  28. protected void onDestroy() {
  29. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  30. super.onDestroy();
  31. }
  32. public void onButtonClick(View view) {
  33. startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
  34. }
  35. private void updateServiceStatus() {
  36. boolean ServiceEnabled = false;
  37. // 循环遍历所有服务,查看是否开启
  38. AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
  39. List<AccessibilityServiceInfo> accessibilityServices = accessibilityManager
  40. .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
  41. for (AccessibilityServiceInfo info : accessibilityServices) {
  42. if (info.getId().equals(getPackageName() + "/.AccessibilityServiceExample")) {
  43. ServiceEnabled = true;
  44. break;
  45. }
  46. }
  47. if (ServiceEnabled) {
  48. button_switch.setText("关闭测试服务");
  49. // Prevent screen from dimming
  50. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  51. } else {
  52. button_switch.setText("开启测试服务");
  53. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  54. }
  55. }
  56. }

UiAutomatorViewer

在需要精确元素节点操作检索的时候,就需要用到UiAutomatorViewer工具,一般情况下,Android SDK下会自带这个工具



找到uiautomatorviewer.bat即可,打开之后,点击看起来是这个样子的



我想你应该知道怎么做了,树形结构UI,AccessibilityNodeInfo通过getChild、getParent尽情的识别吧~!

参考

http://www.android-doc.com/guide/topics/ui/accessibility/services.html

Android自动化之AccessibilityService的更多相关文章

  1. Android自动化框架介绍

    随着Android应用得越来越广,越来越多的公司推出了自己移动应用测试平台.例如,百度的MTC.东软易测云.Testin云测试平台…….由于自己所在项目组就是做终端测试工具的,故抽空了解了下几种常见的 ...

  2. Android自动化测试中AccessibilityService获取控件信息(1)

    Android自动化测试中AccessibilityService获取控件信息(1) 分类: android自动化测试2014-03-24 15:31 3455人阅读 评论(16) 收藏 举报 and ...

  3. Android自动化框架 模拟操作 模拟测试

    转自:http://bbs2.c114.net/home.php?mod=space&uid=1025779&do=blog&id=5322 几种常见的Android自动化测试 ...

  4. Android自动化框架

    Android自动化框架 已有 2085 次阅读2014-8-26 12:19 | Android 几种常见的Android自动化测试框架及其应用 随着Android应用得越来越广,越来越多的公司推出 ...

  5. Android自动化学习笔记:编写MonkeyRunner脚本的几种方式

    ---------------------------------------------------------------------------------------------------- ...

  6. Android自动化学习笔记之MonkeyRunner:官方介绍和简单实例

    ---------------------------------------------------------------------------------------------------- ...

  7. Android自动化测试之Monkeyrunner学习笔记(一)

    Android自动化测试之Monkeyrunner学习笔记(一) 因项目需要,开始研究Android自动化测试方法,对其中的一些工具.方法和框架做了一些简单的整理,其中包括Monkey.Monkeyr ...

  8. Android自动化压力测试图解教程——Monkey工具

    [置顶] Android自动化压力测试图解教程--Monkey工具 标签: 测试androidprofiling工具测试工具文档 2012-04-01 10:16 38185人阅读 评论(10) 收藏 ...

  9. android自动化环境搭建

    android自动化环境安装指南 1.appium相关安装(eclipse下)见http://www.cnblogs.com/wangcp-2014/p/5717589.html参考selenium的 ...

随机推荐

  1. C# 判断是否是在设计模式下有效的方法

    public static bool IsDesignMode() { bool returnFlag = false; #if DEBUG if (LicenseManager.UsageMode ...

  2. 可epoll队列

    什么是可epoll队列? 就可以使用epoll来监控队列中是否有数据的队列,当然也支持select和poll. 应用场景 一个线程,需要将队列(共享内存队列或普通队列均可)中的数据取出来,然后通过网络 ...

  3. ASP.NET JSON数据转实体类方式

    实体类 public class FlieList { public string file_unid { get; set; } public string file_name { get; set ...

  4. Java概述、环境变量、注释、关键字、标识符、常量

    Java语言的特点        有很多小特点,重点有两个开源,跨平台 Java语言是跨平台的 Java语言的平台        JavaSE        JavaME--Android       ...

  5. [Selenium With C#基础教程] Lesson-06 单选按钮

    作者:Surpassme 来源:http://www.jianshu.com/p/08ee1929875f 声明:本文为原创文章,如需转载请在文章页面明显位置给出原文链接,谢谢. 单选按钮通常用在需要 ...

  6. Visual Studio Error

    Visual Studio Error 注意:文中所有“系统”用词,均指Windows Console操作系统IO Debug Error 错误类型 #0表示调用约定错误 可以考虑在指针前面加上_st ...

  7. Ubuntu sudo apt-get update提示 Failed to fetch,解决办法

    问题: 执行sudo apt-get update提示:Failed to fetch chen@ubuntu:~/soft/Python-$ sudo apt-get update Get: htt ...

  8. Pycharm中安装Pygame并写第一个程序

    第一步:打开Pycharm 第二步:点File ->Default Settings->Project Interpreter->点加号 第三步: 搜索Pygame->Inst ...

  9. leetcode 31. Next Permutation JAVA

    题目: 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列. 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列). 必须原地修改,只允许使用额外常数 ...

  10. UWA发布 | 2017 Unity手游体检蓝皮书 — ARPG篇

    报告目录: 一.ARPG手游总体性能开销分析 二.ARPG手游CPU模块性能开销分析 三.ARPG手游内存模块性能开销分析 四.ARPG手游资源管理分析 五.UWA对于ARPG手游研发团队的建议 一. ...