AccessibilityService實(shí)現(xiàn)行為捕獲
語音:Kotlin 版本:1.2.41
最近接到一個(gè)很奇葩的需求:捕獲系統(tǒng)語音通話聊天(包括微信榆苞、電話账忘、QQ),把通話內(nèi)容記錄成音頻文件上傳到服務(wù)端遍坟。
接到需求的第一時(shí)間當(dāng)然是度娘谷歌一陣,一頓亂搜之后發(fā)現(xiàn)并沒有太多能夠用來參考的文獻(xiàn)激挪。
沒有現(xiàn)成的輪子可用豆同,只能擼起袖子,自己動(dòng)手豐衣足食驹溃。
AccessibilityService簡介
AccessibilityService中文翻譯為輔助服務(wù)城丧,它能夠幫助我們獲取第三方應(yīng)用的操作信息。
AccessibilityService的所帶來的安全隱患在這里不多贅述豌鹤,主要介紹一些關(guān)于它的特性和用法亡哄。
本文章將用大家常用的微信來舉例,獲取微信語音通話界面的控件布局布疙,提取到關(guān)鍵常量之后蚊惯,用AccessibilityService來捕獲。
先來看一段官方對(duì)AccessibilityService的描述:
Accessibility services should only be used to assist users with disabilities in using Android devices and apps. They run in the background and receive 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 requires extending this class and implementing its abstract methods.
Lifecycle
The lifecycle of an accessibility service is managed exclusively by the system and follows the established service life cycle. Starting an accessibility service is triggered exclusively by the user explicitly turning the service on in device settings. After the system binds to a service, it calls onServiceConnected(). This method can be overridden by clients that want to perform post binding setup.
An accessibility service stops either when the user turns it off in device settings or when it calls disableSelf().
從描述中我們可以得到如下幾個(gè)信息:
1.AccessibilityService只能用于幫助殘障人士使用Android設(shè)備和應(yīng)用灵临;
2.運(yùn)行在后臺(tái)截型,可以實(shí)現(xiàn)無感知運(yùn)作;
3.能夠監(jiān)測焦點(diǎn)的切換俱诸、點(diǎn)擊等事件菠劝;
4.APP只能對(duì)其進(jìn)行注冊(cè),無法自動(dòng)開啟;
5.啟動(dòng)AccessibilityService需要用戶進(jìn)入“設(shè)置-無障礙”手動(dòng)開啟赶诊,關(guān)閉亦是如此笼平。
放上官方文檔的鏈接(需爬梯):
https://developer.android.com/reference/android/accessibilityservice/AccessibilityService
AccessibilityService基本配置方式
創(chuàng)建MyAccessibilityService繼承AccessibilityService
覆寫onServiceConnected意敛、onAccessibilityEvent乾戏、onInterrupt方法蟆炊。
三個(gè)方法分別為AccessibilityService的三個(gè)生命周期楞陷。
可以大致理解為“服務(wù)啟動(dòng)時(shí)”缸剪、“服務(wù)監(jiān)聽中”蒿叠、“斷開服務(wù)”
/*
* Create by parker
* Author: parker
* Create: 2018/12/7 10:48 AM
*
*/
class MyAccessibilityService : AccessibilityService() {
override fun onServiceConnected() {
super.onServiceConnected()
}
override fun onAccessibilityEvent(event: AccessibilityEvent) {
}
override fun onInterrupt() {
}
}
在AndroidManifest.xml中注冊(cè):
<service
android:name=".MyAccessibilityService"
android:enabled="true"
android:exported="false"
android:label="@string/my_as"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:process=":BackgroundService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config" />
</service>
在res下新建accessibility_config.xml文件:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true"
android:notificationTimeout="100"
android:description="@string/description"
android:packageNames="com.tencent.mm" />
屬性作用如下:
android:description :輔助功能描述皿伺,描述該輔助功能用來干嘛的
android:packageNames :指定輔助功能監(jiān)聽的包名狐赡,不指定表示監(jiān)聽所有應(yīng)用
android:accessibilityEventTypes:輔助功能處理事件類型滋捶,一般配置為typeAllMask表示接收所有事件
android:accessibilityFlags:輔助功能查找截點(diǎn)方式痛悯,一般配置為flagDefault默認(rèn)方式。
android:accessibilityFeedbackType:操作相應(yīng)按鈕以后輔助功能給用戶的反饋類型重窟,包括聲音载萌,震動(dòng)等。
android:notificationTimeout:相應(yīng)時(shí)間設(shè)置
android:canRetrieveWindowContent:是否允許輔助功能獲得窗口的節(jié)點(diǎn)信息巡扇,為了能正常實(shí)用輔助功能扭仁,請(qǐng)務(wù)必保持該項(xiàng)配置為true
配置完成后將demo跑進(jìn)測試機(jī)器
進(jìn)入 設(shè)置→輔助服務(wù)→無障礙
如果在“無障礙”下出現(xiàn)我們配置的服務(wù),表示配置成功
實(shí)現(xiàn)微信語音電話狀態(tài)監(jiān)聽
監(jiān)聽工作流程如下:
·捕獲微信進(jìn)入聊天狀態(tài)
·啟動(dòng)語音錄制服務(wù)
·捕獲微信退出聊天狀態(tài)
·退出語音錄制服務(wù)
首先需要借助工具來分析微信的布局厅翔。通過捕獲微信布局中的關(guān)鍵常量乖坠,來判斷微信當(dāng)前的狀態(tài)。
布局分析工具
谷歌在AS 3.0之后刀闷,取消老舊的DDMS熊泵,為我們提供了界面更為友好的全新工具:LayoutInspector
打開方式:Tools → Lyaout Inspector 如圖:
點(diǎn)擊之后,系統(tǒng)彈出Choose Porcess提示我們選擇需要監(jiān)聽的進(jìn)程涩赢,這里我們選擇微信戈次,先不要點(diǎn)“OK”:
在微信上啟動(dòng)語音聊天,然后點(diǎn)擊Choose Process的OK筒扒,選擇VideoActivity,如圖:
捕獲到的微信聊天界面的布局如圖所示:
通過分析左邊的View Tree我們發(fā)現(xiàn)怯邪,微信中提示已接通、通話結(jié)束等狀態(tài)的控件id為eg7.
這是一個(gè)很有用的發(fā)現(xiàn)花墩,它的文本內(nèi)容可以直接作為判斷微信通話是否連接或者斷開的依據(jù)悬秉。
ok 知道了微信語音聊天了開始和結(jié)束的狀態(tài)判定依據(jù),接下來就是用代碼來實(shí)現(xiàn)監(jiān)聽的過程冰蘑。
核心代碼如下:
override fun onAccessibilityEvent(event: AccessibilityEvent){
//獲取當(dāng)前界面的布局信息
val noteInfo: AccessibilityNodeInfo = when {
rootInActiveWindow != null -> rootInActiveWindow
event.source != null -> event.source
else -> return
}
//獲取id為eg7的控件 并根據(jù)內(nèi)容執(zhí)行相應(yīng)的操作
val wechatToast = noteInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/eg7")
//如果id為eg7的控件存在
if (wechatToast.size>0){
//判斷這個(gè)控件的內(nèi)容
if (wechatToast[0].text.toString() == "已接通"){
//此時(shí)語音聊天已經(jīng)接通
//TODO:do something you want
}else if (wechatToast[0].text.toString().contains("聊天結(jié)束")){
//此時(shí)語音聊天已經(jīng)結(jié)束
//TODO:do something you want
}
}
}
至此和泌,微信聊天狀態(tài)已經(jīng)捕獲完成,TODO處可實(shí)現(xiàn)語音錄制相關(guān)代碼祠肥。
語音錄制過程會(huì)在下一篇文章中詳細(xì)介紹武氓。