自分用メモ。
Docker for Mac でコンテナからホスト側に接続したAndroidデバイスにadbする方法
https://qiita.com/YusukeIwaki/items/5d31c10bc9bfe6d62845
を少し発展させて、2つのDockerコンテナから2つの端末にADB接続するようにしたい。
-s
オプションと同様のことを実現する環境変数 ANDROID_SERIAL
普段、複数端末をPCにさしている場合などは adb -s E32094823 shell
のように -sでデバイスシリアルIDを指定することが多い。これをDockerでやりたいわけだが、できればこういうのは環境変数で持たせたい。
$ adb
Android Debug Bridge version 1.0.41
Version 29.0.1-5644136
Installed as /Users/yusuke-iwaki/Library/Android/sdk/platform-tools/adb
global options:
-a listen on all network interfaces, not just localhost
-d use USB device (error if multiple devices connected)
-e use TCP/IP device (error if multiple TCP/IP devices available)
-s SERIAL use device with given serial (overrides $ANDROID_SERIAL)
-t ID use device with given transport id
-H name of adb server host [default=localhost]
-P port of adb server [default=5037]
-L SOCKET listen on given socket for adb server [default=tcp:localhost:5037]
-s SERIAL use device with given serial (overrides $ANDROID_SERIAL)
お!?
ANDROID_SERIAL?
// If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
if (transport_type == kTransportAny && serial == nullptr) {
serial = getenv("ANDROID_SERIAL");
}
-s で何も指定されていなかったら、 getenv("ANDROID_SERIAL")
で代入しているので、やはり -s
と同じ効果がありそうだ。
docker-composeで。
version: "3"
services:
workspace_hogeperia:
image: your_own/adb
command: adb logcat
environment:
- ANDROID_SERIAL=CB1A2B3C45
workspace_fugafone:
image: your_own/adb
command: adb logcat
environment:
- ANDROID_SERIAL=X51223098765
こんな感じで環境変数で分けておけば、 docker-compose up
すれば
workspace_fugafone_1 | 07-19 16:27:44.972 1455 2348 E ActivityManager: at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:240)
workspace_fugafone_1 | 07-19 16:27:44.972 1455 2348 E ActivityManager: at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3026)
workspace_fugafone_1 | 07-19 16:27:44.972 1455 2348 E ActivityManager: at android.os.Binder.execTransact(Binder.java:674)
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: Sending non-protected broadcast jp.co.sharp.android.keyguard.action.LOCKSCREEN_TOUCH_EVENT from system 1728:com.android.systemui/u0a48 pkg com.android.systemui
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: java.lang.Throwable
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:19258)
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:19863)
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:20005)
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:240)
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3026)
workspace_fugafone_1 | 07-19 16:27:44.989 1455 7369 E ActivityManager: at android.os.Binder.execTransact(Binder.java:674)
workspace_hogeperia_1 | 08-21 16:42:57.992 652 841 E PPDaemon: void ScreenRefresher::ProcessRefreshWork(): pthread_cond_timedwait failed. err: Success
workspace_hogeperia_1 | 08-21 16:42:57.993 652 841 E PPDaemon: void ScreenRefresher::ProcessRefreshWork(): pthread_cond_timedwait failed. err: Success
workspace_hogeperia_1 | 08-21 16:42:57.995 652 841 E PPDaemon: void ScreenRefresher::ProcessRefreshWork(): pthread_cond_timedwait failed. err: Success
workspace_hogeperia_1 | 08-21 16:42:57.996 652 841 E PPDaemon: void ScreenRefresher::ProcessRefreshWork(): pthread_cond_timedwait failed. err: Success
workspace_hogeperia_1 | 08-21 16:42:57.999 652 841 E PPDaemon: void ScreenRefresher::ProcessRefreshWork(): pthread_cond_timedwait failed. err: Success
workspace_hogeperia_1 | 08-21 16:42:58.027 7445 7496 W [Photo Analyzer]: face provider: storage is not granted.
workspace_hogeperia_1 | 08-21 16:42:58.034 32045 32060 E DatabaseUtils: Writing exception to parcel
workspace_hogeperia_1 | 08-21 16:42:58.034 32045 32060 E DatabaseUtils: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/extended_images/media from pid=7445, uid=10085 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
workspace_hogeperia_1 | 08-21 16:42:58.034 32045 32060 E DatabaseUtils: at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:608)
workspace_hogeperia_1 | 08-21 16:42:58.034 32045 32060 E DatabaseUtils: at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:483)
workspace_hogeperia_1 | 08-21 16:42:58.034 32045 32060 E DatabaseUtils: at android.content.ContentProvider$Transport.query(ContentProvider.java:212)
workspace_hogeperia_1 | 08-21 16:42:58.034 32045 32060 E DatabaseUtils: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:118)
workspace_hogeperia_1 | 08-21 16:42:58.034 32045 32060 E DatabaseUtils: at android.os.Binder.execTransact(Binder.java:565)
workspace_hogeperia_1 | 08-21 16:42:58.038 7445 7496 W [Photo Analyzer]: [MessageHandlerThread]processFaceRecognition() please check permission:Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/extended_images/media from pid=7445, uid=10085 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
workspace_fugafone_1 | 07-19 16:27:45.022 1455 7369 I chatty : uid=1000(system) Binder:1455_16 identical 2 lines
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: Sending non-protected broadcast jp.co.sharp.android.keyguard.action.LOCKSCREEN_TOUCH_EVENT from system 1728:com.android.systemui/u0a48 pkg com.android.systemui
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: java.lang.Throwable
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:19258)
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:19863)
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:20005)
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:240)
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3026)
workspace_fugafone_1 | 07-19 16:27:45.037 1455 7369 E ActivityManager: at android.os.Binder.execTransact(Binder.java:674)
workspace_fugafone_1 | 07-19 16:27:45.043 530 582 I NfcNciHal: nci_hal_lp_timeout_cback ()
workspace_fugafone_1 | 07-19 16:27:45.043 530 582 I NfcNciHal: nfc_hal_dm_power_mode_execute () event = 2 p=0 sz=8 kw=0
workspace_fugafone_1 | 07-19 16:27:45.043 530 582 I NfcNciHal: nfc_hal_dm_set_nfc_wake () DEASSERT
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: Sending non-protected broadcast jp.co.sharp.android.keyguard.action.LOCKSCREEN_TOUCH_EVENT from system 1728:com.android.systemui/u0a48 pkg com.android.systemui
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: java.lang.Throwable
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:19258)
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:19863)
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:20005)
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:240)
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3026)
workspace_fugafone_1 | 07-19 16:27:45.054 1455 7369 E ActivityManager: at android.os.Binder.execTransact(Binder.java:674)
wo
2つのコンテナから2つの adb logcat
が並列実行できたぞ、っと。
ちなみにAppiumは一筋縄ではいかない
adbを素で使うケースだとわかりやすいんだけど、Appiumのように内部でadbをラップしてるようなライブラリを使う場合には注意が必要。
- appiumは内部でadbをラップしてなにかやっているので、ANDROID_SERIALを素直に読んでくれなかった。
- 明示的にでdesired capのudidパラメータに環境変数のANDROID_SERIALを入れる、みたいなことをやる必要がある
- appiumは、こっそり
adb forward
を内部で実行して、Mac側のポートをバインドしてAndroidデバイス側のサーバーにコマンドを転送する仕組みをしているので、並列動作させるにはポート番号をずらす必要がある- desired capのsystemPortパラメータをコンテナごとに変える必要がある
内部でadb forwardしているというのは、確かに言われてみたらそうだよなーとは思うんだけど、ソース見るまで自分は気づかなかった。
// set up the modified espresso server etc
this.initEspressoServer();
// Further prepare the device by forwarding the espresso port
logger.debug(`Forwarding Espresso Server port ${DEVICE_PORT} to ${this.opts.systemPort}`);
await this.adb.forwardPort(this.opts.systemPort, DEVICE_PORT);
// set up the modified UiAutomator2 server etc
await this.initUiAutomator2Server();
// Further prepare the device by forwarding the UiAutomator2 port
logger.debug(`Forwarding UiAutomator2 Server port ${DEVICE_PORT} to ${this.opts.systemPort}`);
await this.adb.forwardPort(this.opts.systemPort, DEVICE_PORT);
systemPortを同じにしてしまった時のエラーログが
workspace_fugafone_1 | 05:31:15 appium.1 | [debug] [WD Proxy] Matched '/status' to command name 'getStatus'
workspace_fugafone_1 | 05:31:15 appium.1 | [debug] [WD Proxy] Proxying [GET /status] to [GET http://docker.for.mac.localhost:8200/wd/hub/status] with no body
workspace_fugafone_1 | 05:31:15 appium.1 | [WD Proxy] Got an unexpected response: {"errno":"ECONNREFUSED","code":"ECONNREFUSED","syscall":"connect","address":"192.168.65.2","port":8200}
workspace_fugafone_1 | 05:31:16 appium.1 | [debug] [WD Proxy] Matched '/status' to command name 'getStatus'
workspace_fugafone_1 | 05:31:16 appium.1 | [debug] [WD Proxy] Proxying [GET /status] to [GET http://docker.for.mac.localhost:8200/wd/hub/status] with no body
workspace_fugafone_1 | 05:31:16 appium.1 | [WD Proxy] Got an unexpected response: {"errno":"ECONNREFUSED","code":"ECONNREFUSED","syscall":"connect","address":"192.168.65.2","port":8200}
workspace_fugafone_1 | 05:31:17 appium.1 | [debug] [WD Proxy] Matched '/status' to command name 'getStatus'
workspace_fugafone_1 | 05:31:17 appium.1 | [debug] [WD Proxy] Proxying [GET /status] to [GET http://docker.for.mac.localhost:8200/wd/hub/status] with no body
workspace_fugafone_1 | 05:31:17 appium.1 | [WD Proxy] Got an unexpected response: {"errno":"ECONNREFUSED","code":"ECONNREFUSED","syscall":"connect","address":"192.168.65.2","port":8200}
こんな感じなので、パット見でポートの取り合いになっているということにも気付きづらい。