本节涉及部分:(
[○]
本节覆盖,[√]
为前面已覆盖,[×]
为确认废弃)
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
└── [...]
我们在前一节 Activity 部分已经做了初步的分析,这个 Service 是核心 uiautomator 的控制桥,前面看到是通过 jsonrpc 的接口调用来控制这个服务的启停的,按照设计思维推断,这个 jsonrpc 应该是主要的控制入口,在暴露给 adb 控制机上自动化进行启停的,应该也是这个接口来触发,因此在本节我们需要摸清 Service 内部的职测、工作原理,顺带顺藤摸瓜,找到 jsonrpc 的调用点,以便后续分析整个 jsonrpc 桥服务的运作机制。
主要生命周期钩子
我们看这个 Service 做了什么,主要看各个生命周期注册的行为。
官方科普:developer.android.com/guide/compo…
onCreate
首次服务创建时(后续不会),会调用这个生命周期钩子。
第一段:消息通知,并在前台运行服务
Intent notificationIntent = new Intent(this, MainActivity.class);
String channelId = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channelId = createNotificationChannel("monitor service", "Monitor Service");
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
}
builder = new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_notification)
.setTicker(getString(R.string.monitor_service_ticker))
.setContentTitle(getString(R.string.monitor_service_title))
.setContentText(getString(R.string.monitor_service_text))
.setContentIntent(PendingIntent.getActivity(this, NOTIFICATION_ID, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setWhen(System.currentTimeMillis());
Notification notification = builder.build();
里面的这一段,其实是在触发的时候,会弹出一个消息通知,而非首次启动服务的时候,不会重新触发,因此我们可以看到在手机上,如果出现了一条这样的消息通知,说明 Service 被初始化启动了一次。
然后这一句将在前台运行服务,我们可以看到在 onDestroy()
中会用 stopForeground(true)
清理这个前台运行。
startForeground(NOTIFICATION_ID, notification);
关于这个前台服务,可以看官方文档,事实上因为使用了前台服务,我们还需要在 AndroidManifest.xml
内添加一条权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
第二段:监听器
在当前的 service 上注册 BatteryMonitor
和 WifiMonitor
这两个,Monitor
这块在下一节展开整体的 monitor 机制及源码分析。
HttpPostNotifier notifier = new HttpPostNotifier("http://127.0.0.1:7912");
Context context = getApplicationContext();
addMonitor(new BatteryMonitor(context, notifier));
addMonitor(new WifiMonitor(this, notifier));
基本上说,这个 notifier 是一个调用端,当 monitor 触发时间的时候调这个 notifier 设定好的目标服务去报送请求。
onStartCommand
这个钩子在 “另一个组件(例如 Activity)” 启动服务的时候触发,因此实际可以对应到 adb 启动服务的时候,传入一个 intent 来进入,看代码的话,仅仅是对服务本身的启停做一个控制接口的放出。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.i(TAG, "On StartCommand");
String action = intent.getAction();
if (ACTION_START.equals(action)) {
Log.i(TAG, "Receive start-service action, but ignore it");
} else if (ACTION_STOP.equals(action)) {
stopSelf();
}
return START_NOT_STICKY; // not start again, when killed by system
}
onBind: 不支持
科普一下什么是 Service 的 bind,经过了解,这部分其实是 Android Service 上的声明周期的一个基本节点,可以放出一个跟 Service 通讯的 IBinder 对象。
在本项目中,返回 null 说明不支持绑定。
onDestroy
简单的清理工作,清理 monitor,并且关闭前台,都是基本操作。
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Stopping service");
removeAllMonitor();
Log.i(TAG, "unregister all watchers");
stopForeground(true);
}
注入点
我们看到 Service 类里面其实也没有特别重的控制逻辑,那么问题来了,到底为何我们 POST http://127.0.0.1:7912/uiautomator
的时候可以启动这个服务,在这个接口的内部应该有触发这个 Service 启停的处理,我们需要先找到这个点。
经过后续对整个项目的分析,:7912
上放出的 http 服务接口,是经过 atx-agent
的外层服务反向代理进去内层服务的 :9008
的。
而 :9008
的这个 HttpServer,是通过 androidTest 部分的逻辑,启动一个阻塞的 @Test
测试项来实现的,启动方式已经在第一章中说明,因此与 uiautomator 功能相关的核心逻辑其实是在 androidTest 目录下。
启动方式:
# 停止 AtxAgent
adb shell /data/local/tmp/atx-agent server --stop
# 启动 AtxAgent
adb shell /data/local/tmp/atx-agent server --nouia -d --addr 127.0.0.1 7912
atx-agent 内部会自动维持这个服务活动,执行的是:
adb shell am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner
监听器模式:Monitor 机制
我们看到在 Service 类中声明了 monitors 列表属性,这个与之前看到的 Receiver 是一样的。
这里的 Receiver 主要就是加入了 WifiMonitor 和 BatteryMonitor,以监听电量变化和网络断连的事件。