近期开发上线一个常驻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系统被限制的角度来说,这也是一种曲线救国的实现方式。能够满足,无需用户手动操作实现应用安装。欢迎各位指导……