本节涉及部分:(
[○]
本节覆盖,[√]
为前面已覆盖,[×]
为确认废弃)
app/src/
├── androidTest
│ └── java
│ └── com
│ └── github
│ └── uiautomator
│ ├── ApplicationTest.java
│ └── stub
│ ├── AccessibilityEventListener.java
│ ├── AccessibilityNodeInfoDumper.java
│ ├── AutomatorHttpServer.java
│ ├── AutomatorServiceImpl.java
│ ├── AutomatorService.java
│ ├── ConfiguratorInfo.java
│ ├── DeviceInfo.java
│ ├── Helper.java
│ ├── Log.java
│ ├── NotImplementedException.java
│ ├── ObjInfo.java
│ ├── Point.java
│ ├── Rect.java
│ ├── Selector.java
│ ├── Stub.java
│ ├── TouchController.java
│ └── watcher
│ ├── ClickUiObjectWatcher.java
│ ├── PressKeysWatcher.java
│ └── SelectorWatcher.java
└── main
├── aidl
│ └── android
│ └── view
│ └── IRotationWatcher.aidl
├── AndroidManifest.xml [√]
├── java
│ └── com
│ └── github
│ └── uiautomator
│ ├── AdbBroadcastReceiver.java [○]
│ ├── compat
│ │ ├── InputManagerWrapper.java
│ │ └── WindowManagerWrapper.java
│ ├── Console.java
│ ├── FastInputIME.java
│ ├── FloatView.java
│ ├── IdentifyActivity.java
│ ├── MainActivity.java
│ ├── MinicapAgent.java
│ ├── MinitouchAgent.java
│ ├── MockLocationProvider.java
│ ├── monitor
│ │ ├── AbstractMonitor.java
│ │ ├── BatteryMonitor.java [○]
│ │ ├── HttpPostNotifier.java
│ │ ├── RotationMonitor.java [○]
│ │ └── WifiMonitor.java [○]
│ ├── RotationAgent.java
│ ├── ScreenClient.java
│ ├── ScreenHttpServer.java
│ ├── Service.java
│ ├── ToastActivity.java
│ ├── ToastHelper.java
│ └── util
│ ├── InternalApi.java
│ ├── MemoryManager.java
│ ├── OkhttpManager.java
│ └── Permissons4App.java
└── res
└── [...]
入口声明
看 AndroidManifest.xml
,这个 receiver 声明如下:
<receiver
android:name=".AdbBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="send.mock" />
<action android:name="stop.mock" />
</intent-filter>
</receiver>
可以看出,这是一个 exported 的 receiver,包含了两个动作 send.mock 和 stop.mock。
什么是 Receiver?
关于 Receiver,有些原理不是很懂,问了一下 ChatGPT:
问:在 android 里面的 receiver 是什么概念?
在 Android 应用程序中,Receiver
是一种广播接收器组件,它用于接收系统或其他应用程序发送的广播 Intent。当某个事件发生时,Android 系统会发送一条广播 Intent,经由 Receiver
接收并处理这个 Intent,例如:连接电源、拔出耳机、网络状态变化、时间和日期变化等。
Receiver
组件通常需要在 AndroidManifest.xml 文件中进行声明,这样才能够注册为一个接收器并响应相应的广播 Intent。在 Receiver
的声明中需要指定一个 Intent Filter
,它用于过滤可以接收的广播 Intent。例如:
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.BATTERY_LOW"/>
<action android:name="android.intent.action.BATTERY_OKAY"/>
</intent-filter>
</receiver>
在上面的代码中,我们声明了一个名为 MyReceiver 的类作为 Receiver
,并通过 intent-filter
指定了可以接收 BATTERY_LOW
和 BATTERY_OKAY
两种广播 Intent。当电池电量低于20%时,系统会发送一个 BATTERY_LOW
的广播 Intent,MyReceiver 就会接收到这个 Intent 并执行相应的操作。
在 Receiver
的回调方法中,我们可以获取到 Intent 中包含的数据,例如:Extra 数据、Action、Category、Data 等,并据此实现对特定事件的响应。比如,在上面的代码中,我们可以在 MyReceiver 类中实现 onReceive() 方法并对电池电量低于20%进行提醒或关闭一些不必要的服务等。
总之,Receiver
是 Android 应用程序中的一种重要组件,它可以监听系统事件或其他应用程序的事件,并进行响应和处理。开发者可以根据自己的需要定义一个或多个 Receiver
组件来实现特定功能。
(回答结束)
如何调用
查了一下,在 python 的 uiautomator2 的 API 中,貌似并没有调用这个 mock 方法的调用入口,在这个 Android 项目内部也没有相关的调用,因此应该是可以通过外部直接拉起这个 receiver 的功能的,我们来问一下 ChatGPT 如何调用,然后待会测试一下。
问:如何向 receiver 发送 intent?可以通过 adb 来发送吗?
向 Receiver
发送 Intent 所需的步骤取决于您想要发送的 Intent 的类型。如果您想要发送一个系统广播 Intent,则可以使用 context.sendBroadcast()
方法实现。如果您想要向自定义的 Receiver
发送 Intent,则需要创建一个 Intent 对象,并使用 context.sendBroadcast(intent)
或者 context.sendOrderedBroadcast(intent, permission)
来发送 Intent。其中,第二个方法可以指定安全权限限制,只有符合条件的应用程序才能接收该 Intent。
在 Android 应用程序中,可以通过以下代码来发送一个指定 Action 的广播 Intent:
Intent intent = new Intent("com.example.MyReceiver.MY_ACTION");
intent.putExtra("message", "Hello Receiver!");
sendBroadcast(intent);
这里我们创建了一个名为 com.example.MyReceiver.MY_ACTION
的 Intent
对象,并向其中添加了一个 Extra 数据。然后调用 sendBroadcast(intent)
方法将 Intent 广播出去。如果我们的应用程序中有一个 Receiver
组件注册了对应的 Action,并且已经被激活,则该组件将会接收到这个 Intent,执行相关的操作。
至于是否可以使用 ADB 命令来发送 Intent,则是可以的。通过以下命令可以发送一个指定 Action 的广播 Intent:
adb shell am broadcast -a <Action>
例如,要发送上述示例中的 com.example.MyReceiver.MY_ACTION
Action,可以执行以下命令:
adb shell am broadcast -a com.example.MyReceiver.MY_ACTION --es message "Hello Receiver!"
这里使用了 --es
参数来添加 Extra 数据。当然,要确保您的应用程序已经安装并且包含指定 Action 的 Receiver
组件。
(回答结束)
尝试调用
阅读源码 AdbBroadcastReceiver,接受的 Intent 应该有两种情况:
- action=stop.mock
- 没有其他参数
- 如果已经开启了 mock,关闭掉
- action=send.mock
- 通过 intent.getStringExtra(xxx) 获取一下几个参数
- lat: 解析成 Double,纬度,默认为 0
- lon: 解析成 Double,经度,默认为 0
- alt: 解析成 Double,海拔,默认为 0
- accurate: 解析成 Float,精度范围,默认为 0
- 通过 intent.getStringExtra(xxx) 获取一下几个参数
因此我们尝试用 adb 命令向其推送一下北京天安门的虚拟坐标试试:
adb shell am broadcast -a send.mock --es lat "39.416" --es lon "116.514" --es accurate "5"
控制台返回:
Broadcasting: Intent { act=send.mock flg=0x400000 (has extras) }
Broadcast completed: result=0
但是貌似位置并没有发生变化,需要调试一下了(我的手机是 M1 10 Ultra / MIUI14.0.1 / Android 13)
在代码里面有一句,应该是能够输出结果的日志,但是日志并没有输出:
Log.i(TAG, String.format("setting mock to Latitude=%f, Longitude=%f Altitude=%f Accuracy=%f", lat, lon, alt, accurate));
然后找了一下在 GitHub 上有两条相关的 Issue:
提出的说法跟这个 Receiver 的静态注册跟动态注册有关系,这个有待考证。
其他的 Receiver
顺带在代码里面查了一下有没有 registerReceiver
关键字,发现还有几个地方存在动态注册 receiver 的位置,这些理论上都是一些放出的 API 可以让我们进行调用:
FastInputIME.java
这个 Service 的注册入口是 exported 的,因此更可能通过 adb 主动拉起,是用于处理文本输入的。
这个 Service 继承自 android.inputmethodservice.InputMethodService
,因此会注册为系统的输入法,具体内容及机制后续有必要再展开,本节只分析其相关的 Receiver。
支持的 Intent 格式如下:
-a ADB_INPUT_TEXT --es text "base64:xxx"
-a ADB_INPUT_KEYCODE --es code 123
-a ADB_CLEAR_TEXT
-a ADB_SET_TEXT --es text "base64:xxx"
-a ADB_EDITOR_CODE --es code 123
-a ADB_GET_CLIPBOARD
–Activity.RESULT_CANCEL
/Activity.RESULT_OK: "base64:xxx"
BatteryMonitor.java
这个 registerReceiver
在 BatteryMonitor
的构造函数中注册,这个会在主 Service.java
里面的 onCreate
钩子上触发。触发之后,这个 monitor 实例会存放在 Service 类的 private monitors
字段中。
这个会捕获系统 USB 插拔以及电量变化的时候向 notifier 的 /info/battery
接口抛出消息,notifier 是实例化 BatteryMonitor 时传入的一个 HttpPostNotifier 服务回调。
支持的 Intent 格式如下:
-a Intent.ACTION_BATTERY_CHANGED -es BatteryManager.EXTRA_LEVEL 0
-a android.hardware.usb.action.USB_STATE -es BatteryManager.EXTRA_LEVEL 0
RotationMonitor.java
与 BatteryMonitor 一样,这个也是一个 AbstractMonitor
的子类,但是貌似没有默认注册,也没有任何其他代码的引用,因此很可能是无效的。
事实上代码里面有注释:Deprecated, use RotationAgent to watch rotation. 因此后面看 RotationAgent 的时候可以再看看实现,这个直接无视更好。
触发的话,会将结果上报到 reporter 的 /info/rotation
接口抛出消息,内容为 windowService.getDefaultDisplay().getRotaion()
。
支持的 Intent 格式如下:
-a Intent.ACTION_CONFIGUATION_CHANGED
WifiMonitor.java
与 BatteryMonitor 一样,这个也是 Monitor,会在 Service 启动时注册。
在网络状态变化以及连接状态变化时会向 notifier 的 /info/wifi
接口上报消息。
一、监听wifi的打开与关闭,与wifi的连接无关,上报的内容可能是如下几种:
- wifi 关闭中,上报两条:
- “wifi”
new WifiInfos(wifiStatus: false, ssid: "").toString()
- 其余情况不会上报
二、监听网络连接,包括wifi和移动数据的打开和关闭,以及连接上可用的连接都会接到监听
- 连接成功并且连接可用时上报
new WifiInfos(true, wInfo.getSSID()).toString()
- 其余情况不会上报
总之,效果就是断网和连上网的时候,notifier 会收到通知。
支持的 Intent 格式如下:
-a WifiManager.NETWORK_STATE_CHANGED_ACTION
-a ConnectivityManager.CONNECTIVITY_ACTION
总结
这里分析了几个 Receiver
的定义,理论上通过这些 Receiver 起码可以向外暴露接口,通过一些 Intent 的传入实现一下这些功能,但事实上并没有测试通过:
- 位置模拟
- 输入法
- 剪贴板获取
- 网络状态监听
- 电量监听
- 横竖屏切换(已废弃)
然后这里面还引出一个在 Service 启动的时候拉起的 monitor
机制,这个后面再专题看一下。