1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Docker for Macを使って複数のAndroid端末に並列でADBで何かをする

Posted at

自分用メモ。

Docker for Mac でコンテナからホスト側に接続したAndroidデバイスにadbする方法
https://qiita.com/YusukeIwaki/items/5d31c10bc9bfe6d62845

を少し発展させて、2つのDockerコンテナから2つの端末にADB接続するようにしたい。

複数ADB.png


-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?

android.googlesource.com/platform/system/core/+/master/adb/client/commandline.cpp
    // 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で。

docker-compose.yml
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しているというのは、確かに言われてみたらそうだよなーとは思うんだけど、ソース見るまで自分は気づかなかった。

appium/appium-espresso-driver|lib/driver.js
    // 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);
appium/appium-uiautomator2-driver|lib/driver.js
    // 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を同じにしてしまった時のエラーログが

Appiumで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}

こんな感じなので、パット見でポートの取り合いになっているということにも気付きづらい。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?