Android "外挂" 基础——Accessibility系列之AccessibilityService

谷歌做AccessibilitySerivice辅助工具,为的是给操作不便的人提供方便,而广大开发拿它干嘛了?抢微信红包,操作别的APP,做外挂。

刀是把好刀,可惜没用来干好事。我只是想看看刀,也不知道是切菜还是砍人。废话不说,开讲。

AccessibilityService 是个抽象类,继承自service,口说无凭源码为证:

java.lang.Object
   ↳ 	android.content.Context
  	   ↳ 	android.content.ContextWrapper
  	  	   ↳ 	android.app.Service
  	  	  	   ↳ 	android.accessibilityservice.AccessibilityService

谷歌对这个类的解释是:

An accessibility service runs in the background and receives callbacks by the system when AccessibilityEvents are fired. Such events denote some state transition in the user interface, for example, the focus has changed, a button has been clicked, etc. Such a service can optionally request the capability for querying the content of the active window. Development of an accessibility service require

翻译过来是:一个辅助服务在后台运行,并在系统重启时辅助激发事件接收回调。这样的事件表示在用户界面中的一些状态转移,例如,焦点已改变,一个按钮被点击等这样的服务可以选择请求的能力,用于查询活动窗口的内容。辅助功能服务的发展需要扩展此类和实现它的抽象方法。(抄来的翻译)

如果要做一个自己的服务类,那就集成AccessibilityService,并实现它的两个方法

1. 创建服务类

public class MyAccessibility extends AccessibilityService{

	@Override
	public void onAccessibilityEvent(AccessibilityEvent event) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onInterrupt() {
		// TODO Auto-generated method stub
		
	}

}

上面代码中两个方法在父类是抽象的,必须写。此外Accessibilityservice中还有几个常用的方法:

Android



  
  
    
    
    
    
详细请看文档, 点击打开链接

2. 声明、配置服务类

像其他Service服务一样,需要在AndroidManifest.xml中声明.除此之外,该服务还必须配置以下两项:

    配置,其name为固定的android.accessibilityservice.AccessibilityService
    声明BIND_ACCESSIBILITY_SERVICE权限,以便系统能够绑定该服务(4.1版本后要求)

注意:任何一点配置错误,系统都检测不到该服务,因此其固定配置如下:

       
            
                
            
        
配置服务的方式一般有两种:

1.通过进行配置。

    
            
                
            

            
        
在res文件下新建xml文件夹,并且新建Accessibility_service xml文件



2.通过写代码setServiceInfo(AccessibilityServiceInfo info)来配置

代码写在自己的AccessibilityService类里,继承AccessibilityService,重载它的方法

@Override
    protected void onServiceConnected() {
        AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
        serviceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        serviceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
        serviceInfo.packageNames = new String[]{"com.tencent.mm"}; 
        serviceInfo.notificationTimeout=100;
   
在此处提一点不算是点的点,辅助服务,需要手动在手机里开通辅助权限,当App安装成功后

在设置->辅助功能中便可以找到我们的服务.该服务默认处在关闭状态,需要手动开启.

4. 在onAccessibilityEvent(AccessibilityEvent event) 中书写自己业务代码,就可以了。

我不太喜欢那出来代码在博客里讲代码,我给你们demo,你们完全可以自己研究了。参考我的上一篇文章

Android外挂----微信自动聊天点击打开链接


说一说AccessibilityService的几个知识点吧:

1.onAccessibilityEvent(AccessibilityEvent event)是本类的核心。这个方法有个参数event,它封装着来自界面相关事件的信息,我们可以获取它:

        @Override
	public void onAccessibilityEvent(AccessibilityEvent event) {
		String className = event.getPackageName().toString();
		String pageName = event.getClassName().toString();
                AccessibilityNodeInfo root = this.getRootInActiveWindow();
		System.out.println(pageName);
		int eventType = event.getEventType();
        switch (eventType) {
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                //界面点击
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                //界面文字改动
                break;
        }
}


这里我们对AccessibilityEvent进行简单的说明:

当用户发生发生变化时,系统会发送一系列的AccessibilityEvent事件,比如按钮被电击时会发送TYPE_VIEW_CLICKED类型的事件.

方法 说明
getEventType() 事件类型
getSource() 获取事件源对应的结点信息                                                                  
getClassName() 获取事件源对应类的类型,比如点击事件是有某个Button产生的,那么此时获取的就是Button的完整类名
getText() 获取事件源的文本信息,比如事件是有TextView发出的,此时获取的就是TextView的text属性.如果该事件源是树结构,那么此时获取的是这个树上所有具有text属性的值的集合






2.看到那句红色的代码了吗?AccessibilityNodeInfo root,是整个服务窗口活动的布局。你可以根据id获取自己想要的控件如:

	AccessibilityNodeInfo root = this.getRootInActiveWindow();
		List tp =root.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/y1");
		List gx = null;
		if (tp!=null &&tp.size()>0) {
			gx = tp.get(0)
					.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/gr");
		}
		if (gx!=null && gx.size()>1) {
			readmessage = gx.get(gx.size()-1).getText().toString();
		}

别问我this是什么,this当然是你自己的AccessibilityService类了。

需要注意,该服务可能配置了只检测了部分事件,而不是全部事件,这就意味着,当内容树发生变化后,该服务可能并不知道,即该服务无法及时的了解当前的内容树是否发生了变化.比如说,你的服务只检测了点击事件,但是此时界面的输入焦点已经变化,这样整个结点树也发生了变化,但是你的服务却不知道,此时你在结点中拿到的窗口内容可能已经不是最新的了.因此,如果你想及时的获知当前窗口的内容,那么就在配置的时候,设置监听全部事件.


正如上面所提到的,要想获取窗口内容,,在配置AccessibilityService时需设置canRetrieveWindowContent为true.之后,便可以通过AccessibilityEvent.getSource(),findFocus(int),getWindow()或者getRootInActiveWindow()获取窗口内容.




3.怎么获取到别的app的id?我给你截图,你一目了然。

在eclipse中,连接手机,然后点它,能看到手机控件详情,你也可以在SDK中的tools 包下面找到uiautomatorviewer.bat 打开看手机详情。studio中,我不关心,你可以自己找找。示意图如下:在eclipse中,连接手机,然后点它,能看到手机控件详情,你也可以在SDK中的tools 包下面找到uiautomatorviewer.bat 打开看手机详情。studio中,我不关心,你可以自己找找。示意图如下:Android

在eclipse中,连接手机,然后点它,能看到手机控件详情,你也可以在SDK中的tools 包下面找到uiautomatorviewer.bat 打开看手机详情。studio中,我不关心,你可以自己找找。示意图如下:
Android


你可以从这里,获知很多有价值的东西:
比如APP包名,package;
当前某一个控件的ID,resource-id;
当前控件是否可点击,clickable;
当前控件是否可滑动,srollable;
当前控件的坐标。
用你自己的鼠标,随心所欲的点,获取你所需的属性,然后对症下药地进行操作。




4. 服务的生命周期

要理解该中服务的生命周期只需要记住以下三点即可:

    该种服务完全由系统管理,并遵循已有的服务周期.
    开启一个服务只能由用户在设置中打开,而关闭则只能由用户在设置中关闭或者服务本身通过diableSelf()方法关闭(当然,现在有些第三放软件也可以强制关闭该类型服务)
    系统绑定该服务之后,会调用onServiceConnected()方法,这个方法可以被重写,在其中,你可以做一些初始化的操作.

5. 检测服务是否开启

方法一:借助服务管理器AccessibilityManager来判断,但是该方法不能检测app本身开启的服务.

private boolean enabled(String name) {
        AccessibilityManager am = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
        List serviceInfos = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
        List installedAccessibilityServiceList = am.getInstalledAccessibilityServiceList();
        for (AccessibilityServiceInfo info : installedAccessibilityServiceList) {
            Log.d("MainActivity", "all -->" + info.getId());
            if (name.equals(info.getId())) {
                return true;
            }
        }
        return false;
    }

方法二:我们知道大部分的系统属性都在settings中进行设置,比如wifi,蓝牙状态等,而这些信息主要是存储在settings对应的的数据库中(system表和serure表),同样我们也可以通过直接读取setting设置来判断相关服务是否开启:


private boolean checkStealFeature1(String service) {
        int ok = 0;
        try {
            ok = Settings.Secure.getInt
           (getApplicationContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
        } catch (Settings.SettingNotFoundException e) {
        }

        TextUtils.SimpleStringSplitter ms = new TextUtils.SimpleStringSplitter(':');
        if (ok == 1) {
            String settingValue = Settings.Secure.getString
            (getApplicationContext().getContentResolver(),
             Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (settingValue != null) {
                ms.setString(settingValue);
                while (ms.hasNext()) {
                    String accessibilityService = ms.next();
                    if (accessibilityService.equalsIgnoreCase(service)) {
                        return true;
                    }

                }
            }

示例:

我不太喜欢那出来代码在博客里讲代码,我给你们demo,你们完全可以自己研究了。

参考我的上一篇文章Android外挂----微信自动聊天点击打开链接

别人的好文章也不容错过:http://blog.csdn.net/dd864140130/article/details/51794318#











你可能感兴趣的:(合抱之木,始于毫末,Android)