AccessibilityService 智能安装,升级应用

        最近忙了2个月的项目,挺累的,但是通过自己的努力,学到不少知识,今天先讲讲AccessibilityService的用法(应用自动升级)。

AccessibilityService,android辅助功能,类似钩子。具体信息自己百度哈,我只用到了自动升级的功能,其实也就是模拟用户点击事件。

        1、首先,我们需要继承AccessibilityService类,其中有一个比较重要的方法需要我们重写,那就是onAccessibilityEvent(AccessibilityEvent event),该方法在每当窗口有活动是,就会回调到onAccessibilityEvent()方法,具体代码如下:

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
   AccessibilityNodeInfo nodeInfo = event.getSource();// 得到的是被点击的单体对象
   // AccessibilityNodeInfo nodeInfo =getRootInActiveWindow();//获得是整个窗口的对象
   System.out.println("******Accessibility Event******");
   System.out.println("nodeInfo被点击的单体对象是否为空" + nodeInfo);
   if (nodeInfo != null) {
      int eventType = event.getEventType();
      if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
            || eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
         // if (handledMap.get(event.getWindowId()) == null) {
         System.out.println("***---***");
         // boolean handled = iterateNodesAndHandle1(nodeInfo);
         installAPK(event);
      }
   }
}

/*
 * 方法1
 */
private void installAPK(AccessibilityEvent event) {
   AccessibilityNodeInfo source = getRootInActiveWindow();
   List<AccessibilityNodeInfo> nextInfos = source.findAccessibilityNodeInfosByText("下一步");
   nextClick(nextInfos);
   List<AccessibilityNodeInfo> installInfos = source.findAccessibilityNodeInfosByText("安装");
   nextClick(installInfos);
   List<AccessibilityNodeInfo> wcInfos = source.findAccessibilityNodeInfosByText("打开");
   nextClick(wcInfos);

   runInBack(event);

}

private void runInBack(AccessibilityEvent event) {
   System.out.println("global_action_back");
   event.getSource().performAction(AccessibilityService.GLOBAL_ACTION_BACK);
}

private void nextClick(List<AccessibilityNodeInfo> infos) {
   System.out.println("infos是否为空:" + infos);
   if (infos != null)
      for (AccessibilityNodeInfo info : infos) {
         System.out.println("视图类型:" + info.getClassName() + "包含" + info.getChildCount() + "子视图");
         if (info.isEnabled() && info.isClickable())
            System.out.println("执行点击");
         info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
      }
}

在installAPK方法中,我们模拟下一步,安装,打开方法,当使用系统uri方法进行安装,升级应用时会跳出安装弹窗,步骤就是下一步,安装,安装完之后会有打开按钮,该方法就是模拟用户点击这些按钮进行安装


2、继承AccessibilityService之后怎么用呢

在xml文件中声明service,和一般的一样,如下:

<!-- 自动更新服务 -->
<service
    android:name=".service.MyAccessibilityService"
    android:label="门禁助手(自动安装)"
    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/accessibility_service_config" />
</service>
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/accessibility_service_config" />
这几个都不能少,否则将无法启动service    meta-data中的resource如下,在xml文件夹(工程默认没有xml文件夹,需要手动在res文件夹中建立)中建立一个accessibility_service_config.xml文件,内容如下:

<!--
给AccessibilityService 设置的属性,也可以在代码里的connnect函数里手动设置,可以向下兼容。
accessibilityEventTypes:响应时间的类型,事件分很多种:单击、长按、滑动等,需要指定,我设置了所有事件都响应:TYPES_ALL_MASK
accessibilityFeedbackType:设置回馈给用户的方式,是语音播出还是振动。这个属性如果不设置的话 我们那个onAccessibilityEvent 这个回调函数 根本回调不了 所以这里要注意
notificationTimeout:响应时间的设置
packageNames:这个属性 就是捕获什么app的行为的,比如我这里写的包名是packageinstaller 那就肯定只能捕获安装器的 事件了
有的rom 安装器可能不是这个包名 那你就要进行特殊设置了,此外这个属性你如果什么都不写 就意味着 你可以捕获所有手机的动作
如果你要做流氓软件的话 可以packageNames 里面什么都不写。。。甚至可以操作支付宝 给你打钱。。。如果你知道用户密码的话。
当然你如果真这么做了 相信捕获一次用户输入密码的行为 也是很容易的
description:这个就是对你那个申请服务的时候说明了,可以写的煽情一点 让用户打开这个服务的可能性更高一点。。。
-->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/app_name"
    android:notificationTimeout="100"
    android:packageNames="com.android.packageinstaller" />
各个字段的用意也有说明了


3、service建立,声明之后就可以启动应用了,启动之后怎么看有没有启动该service呢,第一次启动肯定没有启动的,需要在设置中手动开启智能服务。进入手机设置,看到有一个辅助功能选项(不同手机可能名字不一样,有的叫无障碍等),进去之后能看到自己的应用xml文件中声明的名字

就是在声明service中的label

android:label="门禁助手(自动安装)"

点击该项,打开既可以了,看一下log,

@Override
protected void onServiceConnected() {
   System.out.println("Accessibility 服务连接");
打开之后出现了上面的打印,说明服务以及开启。


4、自己发送一个安装apk intent,进行测试

                String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/test.apk";
                File file = new File(apkPath);
                Uri uri = Uri.fromFile(file);
                Intent localIntent = new Intent(Intent.ACTION_VIEW);
                localIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                localIntent.setDataAndType(uri, "application/vnd.android.package-archive");
                startActivity(localIntent);
发送之后会出现下一步,然后会自动跳过,进入安装,然后自动进入安装,安装完成之后出现打开界面,到这里之后,如果是对本应用升级,因为在升级过程中,本应用会停止掉,后面的完成,打开界面将无法进行模拟。这个时候,我们可以在我们的应用中监听PACKAGE_REPLACED广播,在安装完成之后会收到该广播,然后启动应用即可。

第二种方法,不接收广播,在安装完成之后application也会启动,AccessibilityService会重新连接,在onServiceConnected方法中模拟打开按钮即可,方法如下:

@Override
protected void onServiceConnected() {
   System.out.println("Accessibility 服务连接");
   AccessibilityNodeInfo source = getRootInActiveWindow();
   if(source != null) {
      List<AccessibilityNodeInfo> wcInfos = source.findAccessibilityNodeInfosByText("打开");
      if (wcInfos != null)
         nextClick(wcInfos);
   }
   super.onServiceConnected();
}
模拟打开按钮,activity会启动,而且完成,打开按钮由于模拟了点击打开按钮,系统安装界面会退出去,体验较好。


以上方法是我在实际项目中使用的方法,应该是没有问题的,当然,也发现有部分系统兼容问题,比如说AccessibilityService没有再次连接,这个问题就没办法了










评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值