Android 静默安装二(无障碍服务版)

本文介绍了如何在Android项目中,利用AccessibilityService权限,实现无障碍情况下自动检测并安装应用,避开系统限制,实现非侵入式安装。作者详细描述了自定义AccessibilityService的工作原理和关键代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

近期开发上线一个常驻app,项目已上线,今天随笔记录一下静默安装相关内容。我分三篇静默安装(root版)、静默安装(无障碍版)、监听系统更新、卸载、安装。
先说说我的项目需求:要求app一直运行,通过指令进行自动安装并在安装成功后自动开启。行业人事都了解,非root权限不可能无声无息的完成此要求。我分两步完成了此功能开发。今天记录一下无障碍权限下实现自动安装app。
本文使用AccessibilityService执行系统安装程序自动安装指定文件。

一、自定义AccessibilityService并监听系统弹窗节点


/**
 * 自动安装服务
 */
class AutoInstallService : AccessibilityService() {
    // 检查节点
    private fun checkNodes(node: AccessibilityNodeInfo):Boolean{
        if (node==null) {
            return false
        }
        if (node.className.isEmpty()) {
            return false
        }
        try {
            // 检查当前窗体
            if (node.className.equals("android.widget.Button")) {
                if (node.text.toString().isEmpty()) {
                    return false
                }
                // 模拟点击
                if (node.text.equals("安装")||
                    node.text.equals("完成")||
                    node.text.equals("打开")||
                    node.text.equals("确定")
                ) {
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                    return true
                }
                // 检查滑动节点
            } else if (node.className.equals("android.widget.ScrollView")){
                node.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
            }
            // 检查下级窗体
            for (i in 0..node.childCount) {
                var child = node.getChild(i)
                if (checkNodes(child)) {
                    return true
                }
            }
        }catch (e:Exception){
            e.printStackTrace()
        }
        return false
    }
    private var nodes:MutableMap<Int,Boolean> = LinkedHashMap()
    // 分析系统弹窗节点
    override fun onAccessibilityEvent(p0: AccessibilityEvent?) {
        // 监听系统窗体
        p0?.let {
            it.source?.let {
                obj->{
                var eventType = it.eventType
                if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
                    // 监听窗体节点
                    if (nodes.get(it.windowId) == null) {
                        if (checkNodes(obj))nodes.put(it.windowId,true)
                    }
                   }
                }
            }
        }
    }
    // 销毁
    override fun onDestroy() {
        super.onDestroy()
        jumpToAccessServiceSetUi(this)
    }
   // 连接成功后退出设置页面
    override fun onServiceConnected() {
        super.onServiceConnected()
        // 连接成功,执行返回按钮
        performGlobalAction(GLOBAL_ACTION_BACK)
        Thread.sleep(500L)
        performGlobalAction(GLOBAL_ACTION_BACK)
    }

    override fun onInterrupt() {

    }
    // 跳转辅助服务
     fun jumpToAccessServiceSetUi(context:Context){
        context?.let {
            try {
                it.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
            }catch (e:Exception){
                var intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
                it.startActivity(intent)
                e.printStackTrace()
            }
        }
    }

    /**
     * 检查辅助服务是否开启
     * @appcaliionId 应用id
     * @ct     设备id
     */
    fun checkAccessServiceState(appcaliionId:String,ct:Context):Boolean{
        try {
            var state = Settings.Secure.getInt(ct.contentResolver, Settings.Secure.ACCESSIBILITY_ENABLED, 0)
            if (state != 1) {
                return false
            } else{
                var serviceName = Settings.Secure.getString(
                    ct.contentResolver,
                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
                )
                if (serviceName.isEmpty()) {
                    return serviceName.contains(appcaliionId)
                }
                return false
            }
        } catch (e:Exception){
            e.printStackTrace()
        }
        return false
    }



}

这是我自定的AccessibilityService,内部包含了跳转到开启AccessibilityService设置页面,分析系统弹窗节点,添加安装节点,自动执行。开启AccessibilityService服务,销毁后重新开启AccessibilityService。

2、AccessibilityService注册

在清单文件中注册自定义的AutoInstallService

 <service android:name="com.zhujing.nadedemospace.AutoInstallService"
            android:label="自动安装服务"
            android:exported="true"
            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_config" />
        </service>

accessibility_config自定义配置

<accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:canRetrieveWindowContent="true"/>

三、使用

// 开启无障碍
 findViewById<View>(R.id.open_accessbt).setOnClickListener {
            if (!AutoInstallService().checkAccessServiceState("com.zhujing.nadedemospace",this)) {
                AutoInstallService().jumpToAccessServiceSetUi(this)
            }
        }
 // 安装应用
         findViewById<View>(R.id.install_apk).setOnClickListener {
            //
            var intent = Intent(Intent.ACTION_GET_CONTENT)
            intent.type = "*/*"
            intent.addCategory(Intent.CATEGORY_OPENABLE)
            startActivityForResult(intent,100)
        }
        // 调用系统安装方法
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 100 && resultCode == RESULT_OK && data?.data != null){
            var uri = data?.data
            var intent = Intent(Intent.ACTION_VIEW)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            intent.setDataAndType(uri,"application/vnd.android.package-archive")
            startActivity(intent)
        }
    }
    

四、总结

我使用的是AccessibilityService无障碍服务实现自动安装的,严格意义上这并不算静默安装。andorid系统被限制的角度来说,这也是一种曲线救国的实现方式。能够满足,无需用户手动操作实现应用安装。欢迎各位指导……

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值