【Android】解析AccessibilityService(辅助服务)的使用
辅助功能是Android系统提供的一种服务,派生自Service类。这个服务提供了增强的用户界面,目的是为了帮助残障人士。它一般提供了页面元素查找功能和元素点击功能。
通过辅助功能,开发者可以实现一些非常丰富的功能:
抢红包
微信自动回复
检查微信好友
进程清理
判断应用当前状态
防卸载
浏览器劫持
跳过用户授权
关于更多AccessibilityService的安全信息可以查看这篇文章:
https://www.freebuf.com/articles/terminal/114045.html
AccessibilityService(辅助功能类)派生自Service,它是一个服务类。AccessibilityService是一个抽象类,所以要使用辅助功能的话,就要从AccessibilityService类派生一个实例类,完成配置、监听,再根据监听到的元素完成各种动作。
例如,下面这个案例:
MainActivity.java
import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import com.example.accessibilityservicetest.R; public class MainActivity extends Activity { private static String TAG="test"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //如果没开启,就提醒开启辅助功能 if(!isAccessibilitySettingsOn(this)){ Intent intent=new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS); startActivity(intent); } } //判断是否开启辅助功能 private boolean isAccessibilitySettingsOn(Context mContext) { int accessibilityEnabled = 0; final String service = getPackageName() + "/" + MyCustomAccessibilityService.class.getCanonicalName(); try { accessibilityEnabled = Settings.Secure.getInt( mContext.getApplicationContext().getContentResolver(), android.provider.Settings.Secure.ACCESSIBILITY_ENABLED); Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled); } catch (Settings.SettingNotFoundException e) { Log.e(TAG, "Error finding setting, default accessibility to not found: " + e.getMessage()); } TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); if (accessibilityEnabled == 1) { Log.v(TAG, "***ACCESSIBILITY IS ENABLED*** -----------------"); String settingValue = Settings.Secure.getString( mContext.getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); if (settingValue != null) { mStringColonSplitter.setString(settingValue); while (mStringColonSplitter.hasNext()) { String accessibilityService = mStringColonSplitter.next(); Log.v(TAG, "-------------- > accessibilityService :: " + accessibilityService + " " + service); if (accessibilityService.equalsIgnoreCase(service)) { Log.v(TAG, "We've found the correct setting - accessibility is switched on!"); return true; } } } } else { Log.v(TAG, "***ACCESSIBILITY IS DISABLED***"); } return false; } }
MyCustomAccessibilityService.java
public class MyCustomAccessibilityService extends AccessibilityService { //该方法在初始化辅助功能时调用 @Override protected void onServiceConnected() { super.onServiceConnected(); } //获取到指定的监听事件 @Override public void onAccessibilityEvent(AccessibilityEvent event) { //辅助功能的时间类型 int eventType=event.getEventType(); //输出事件的字符串type String typeStr=event.eventTypeToString(eventType); //根据事件类型来分发我们需要的操作,这里以窗口变化为例 if(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED==eventType){ //判断我们的辅助功能,是否在约定的界面执行,以设置界面为例 if("com.android.settings".equals(event.getPackageName())){ //doSometing } }else if(AccessibilityEvent.TYPE_GESTURE_DETECTION_START==eventType){ }else{ //在完成自己的操作时候,可以关闭自己的服务,下次使用再开启 //API>=24 //disableSelf() } //通过event遍历nodeInfo AccessibilityNodeInfo info= event.getSource(); //findFocus(int) //getWindows() //getRootInActiveWindow() //遍历节点 for(int i=0;i<info.getChildCount();i++){ AccessibilityNodeInfo childNode= info.getChild(i); //获取子节点中的某个特定node,一下通过id查找 List<AccessibilityNodeInfo> list = childNode.findAccessibilityNodeInfosByViewId("com.android" +".settings:id/xxxx"); // 通过text查找 //List<AccessibilityNodeInfo> list = info.findAccessibilityNodeInfosByText("xxxx"); Log.i("InfoType",childNode.getClassName().toString()); Log.i("InfoText", childNode.getText().toString()); Log.i("InfoPkgName",childNode.getPackageName().toString()); Log.i("InfoViewId", childNode.getViewIdResourceName()); //进行点击操作 for(AccessibilityNodeInfo anodeinfo : list){ if(anodeinfo.isClickable()){ anodeinfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } } } //辅助功能被中断时候调用该方法 @Override public void onInterrupt() { } }
acessibilityserviceconfig.xml
<?xml version="1.0" encoding="utf-8"?> <accessibility-service android:description="@string/accessibility_description" android:accessibilityEventTypes="typeAllMask" android:packageNames="com.example.accessibilityservicetest,com.android.settings" android:accessibilityFeedbackType="feedbackGeneric" android:notificationTimeout="100" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" xmlns:android="http://schemas.android.com/apk/res/android"/>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.accessibilityservicetest" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.accessibilityservicetest.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.example.accessibilityservicetest.MyCustomAccessibilityService" android:label="@string/app_name" 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/acessibilityserviceconfig"/> </service> </application> </manifest>
上面的acessibilityserviceconfig.xml文件是关于这次辅助功能的配置信息,在下面做出如下说明:
AccessibilityEventTypes 此服务希望接收的事件类型
constant value 描述
typeAllMask ffffffff 所有类型的事件
typeAnnouncement 4000 一个应用产生一个通知事件
typeAssistReadingContext 1000000 辅助用户读取当前屏幕事件
typeContextClicked 800000 view中上下文点击事件
typeGestureDetectionEnd 80000 监测到的手势事件完成
typeGestureDetectionStart 40000 开始手势监测事件
typeNotificationStateChanged 40 收到notification弹出消息事件
typeTouchExplorationGestureEnd 400 触摸浏览事件完成
typeTouchExplorationGestureStart 200 触摸浏览事件开始
typeTouchInteractionEnd 200000 用户触屏事件结束
typeTouchInteractionStart 100000 触摸屏幕事件开始
typeViewAccessibilityFocusCleared 10000 无障碍焦点事件清除
typeViewAccessibilityFocused 8000 获得无障碍的焦点事件
typeViewClicked 1 点击事件
typeViewFocused 8 view获取到焦点事件
typeViewHoverEnter 80 一个view的悬停事件
typeViewHoverExit 100 一个view的悬停事件结束,悬停离开该view
typeViewLongClicked 2 view的长按事件
typeViewScrolled 1000 view的滚动事件,adapterview、scrollview
typeViewSelected 4 view选中,一般是具有选中属性的view,例如adapter
typeViewTextChanged 10 edittext中文字发生改变的事件
typeViewTextSelectionChanged 2000 edittext文字选中发生改变事件
typeViewTextTraversedAtMovementGranularity 20000 UIanimator中在一个视图文本中进行遍历会产生这个事件,多个粒度遍历文本。一般用于语音阅读context
typeWindowContentChanged 800 窗口的内容发生变化,或者更具体的子树根布局变化事件
typeWindowStateChanged 20 新的弹出层导致的窗口变化(dialog、menu、popupwindow)
typeWindowsChanged 400000 屏幕上的窗口变化事件,需要API 21+
accessibilityFeedbackType 此服务提供的反馈类型
constant value 描述
feedbackAllMask ffffffff 取消所有的可用反馈方式
feedbackAudible 4 可听见的(非语音反馈)
feedbackGeneric 10 通用反馈
feedbackHaptic 2 触觉反馈(震动)
feedbackSpoken 1 语音反馈
feedbackVisual 8 视觉反馈
accessibilityFlags 辅助功能附加的标志,多个使用 ' | '分隔
constant value 描述
flagDefault 1 默认的配置
flagEnableAccessibilityVolume 80 这个标志要求系统内所有的音频通道,使用由STREAM_ACCESSIBILTY音量控制USAGE_ASSISTANCE_ACCESSIBILITY
flagIncludeNotImportantViews 2 表示可获取到一些被表示为辅助功能无权获取到的view
flagReportViewIds 10 使用该flag表示可获取到view的ID
flagRequestAccessibilityButton 100 如果辅助功能可用,提供一个辅助功能按钮在系统的导航栏 API 26+
flagRequestEnhancedWebAccessibility 8 此类扩展的目的是为WebView中呈现的内容提供更好的辅助功能支持。这种扩展的一个例子是从一个安全的来源注入JavaScript。如果至少有一个具有此标志的辅助功能服务, 则系统将使能增强的web辅助功能。因此, 清除此标志并不保证该设备不会使能增强的web辅助功能, 因为可能有另一个使能的服务在使用它。
flagRequestFilterKeyEvents 20 能够监听到系统的物理按键
flagRequestFingerprintGestures 200 监听系统的指纹手势 API 26+
flagRequestTouchExplorationMode 4 系统进入触控探索模式。出现一个鼠标在用户的界面
flagRetrieveInteractiveWindows 40 该标志知识的辅助服务要访问所有交互式窗口内容的系统,这个标志没有被设置时,服务不会收到TYPE_WINDOWS_CHANGE事件。
canRequestEnhancedWebAccessibility(boolean)
辅助功能服务是否能够请求WEB辅助增强的属性。例如: 安装脚本以使应用程序内容更易于访问。
canRequestFilterKeyEvents(boolean)
辅助功能服务是否能够请求过滤KeyEvent的属性,是否可以请求KeyEvent事件流。flagRequestFilterKeyEvents搭配使用
canRequestTouchExplorationMode (boolean)
此属性用于,能够让辅助功能服务通过手势,来请求触摸浏览模式,其被触摸的项,将被朗读出来,flagRequestTouchExplorationMode搭配使用
canRetrieveWindowContent (boolean)
辅助功能服务是否能够取回活动窗口内容的属性。 与上边的flagRetrieveInteractiveWindows搭配使用,无法在运行时更改此设置。
description
辅助功能服务目的或行为的简短描述。
notificationTimeout
同一类型的两个辅助功能事件发送到服务的最短间隔(毫秒,两个辅助功能事件之间的最小周期)
packageNames
从此服务能接收到事件的软件包名称 (不适合所有软件包)(多个软件包用逗号分隔)。
settingsActivity
允许用户修改辅助功能的activity组件名称
summary
同description
可以通过DDMS来查看手机界面的节点元素的各种信息: