SviCloud 9PというAndroid TV Boxを、OpenClawから操作できるNodeにするための実験を進めました。
最初に分かったのは、Tailscaleを入れただけではOpenClaw Nodeにはならない、という当たり前だけれど大事な点です。TailscaleはGatewayへ到達するためのネットワーク経路を作ってくれますが、端末側でOpenClaw AndroidアプリがNodeとして動いていなければ、こちらから端末操作はできません。
今回の端末ではGoogle Play経由でOpenClaw Androidを入れられなかったため、Android APKをソースからビルドし、9P向けの診断・復旧機能を足しながら進めました。
まずNodeとして接続する
最初のゴールは、9PをGatewayに paired / connected / approved の状態で接続することでした。
Android TVでは、通常のスマホ向けアプリ導線やPlayストア導線がそのまま使えないことがあります。そこでOpenClaw Androidのthird-party debug APKをビルドし、手動インストールで導入しました。
この段階で確認したことは次の通りです。
device.infoやdevice.statusは取得できる- 有線LAN経由でGatewayに接続できる
- Tailscaleを前面起動すればTailnet側の疎通も取れる
- アクセシビリティを有効にすれば
mobile.ui.observe/mobile.ui.actで画面操作も候補にできる
一方で、Android TVでは「画面に出ているオンボーディング表示」と「Nodeとしての実接続状態」が一致しない場面もありました。NodeはGateway上で承認済みなのに、端末側ホームCanvasでは未接続のように見えるケースです。このため、operator接続がない状態でもNode接続済みとして扱う表示修正を入れました。
診断コマンドを増やす
端末の状態を画面越しに読むだけでは限界があるため、OpenClaw Android側に診断コマンドを追加しました。
追加した主なコマンドは次の通りです。
device.root.diagnosedevice.update.capabilitiesdevice.settings.diagnosedevice.accessibility.preparedevice.adb.tcp.enabledevice.update.downloaddevice.app.launch
device.root.diagnose では、su の存在、既知パス、su -c id の実行結果、root管理アプリ候補、関連する getprop をまとめて見られるようにしました。
この9Pでは /system/xbin/su 自体は存在していましたが、OpenClawアプリから su -c id は成功せず、通常アプリへrootシェルは渡されませんでした。つまり「suバイナリはあるが、アプリがrootを取れるわけではない」状態です。
device.settings.diagnose では、ADBや開発者向け設定、アクセシビリティの状態を Settings.Global / Settings.Secure とshell fallbackから読むようにしました。これにより、画面UIを自動操作できない状態でも、ADBが有効か、OpenClaw Accessibility Serviceが有効かを判断できるようになりました。
ADB TCPとADB rootの経路
面白かったのはADBまわりです。
OpenClawアプリ自体はrootを取れませんでしたが、9Pは ro.debuggable=1 のuserdebug系ビルドでした。さらに通常アプリから setprop ctl.start adbd / setprop ctl.restart adbd が通ることが分かりました。
その結果、OpenClaw Node経由でadbdを起動し、LAN側から adb connect し、さらに adb root でroot shellへ入る経路が作れました。
この構成では、OpenClaw Android APK単体でroot権限を持つわけではありません。OpenClawはadbdを起動する入口になり、その後の更新や低レベル操作はADB rootで行います。
APK更新を安定させる
次に詰まったのがAPK更新です。
ADB root接続はできるものの、大きなAPKを adb install や adb push で転送すると、途中で device offline になったり、転送が切れたりしました。小さいファイルは通るのに、APKサイズになると不安定です。
そこでOpenClaw Android側に device.update.download を追加しました。
流れはこうです。
- 作業PCでAPKをビルドする
- 固定URLにAPKを置く
- 9PのOpenClaw Nodeへ
device.update.downloadを投げる - 9P自身がHTTPでAPKをアプリcacheへ保存する
- 返ってきた端末内pathに対して、ADB rootで
pm install -rする NodeForegroundServiceを起動し、Gateway再接続を確認する
これで、大容量転送をADBに任せず、端末自身にダウンロードさせる更新フローにできました。
Device IDを安定させる
APKを更新するたびにNode IDが変わる問題もありました。
原因は、debug/third-partyビルドで暗号化SharedPreferencesが安定して読めない場面があり、Device Identityが再生成されていたことです。Node IDが変わるとGateway側では別端末扱いになり、毎回承認が必要になります。
対策として、debug/third-party限定でDevice Identityの復旧用SharedPreferencesを持つようにしました。
重要なのは、これは公開版やPlay版には入れないことです。Device Identityには秘密鍵が含まれるため、release/playではsecure-onlyを維持し、診断APKだけの回復策として扱いました。
この修正後は、APK更新後も同じNode IDのまま復帰できるようになりました。
再起動後の復帰
再起動後の復帰では、いくつかAndroid TVらしい制限に当たりました。
OpenClaw側では BootReceiver と NodeForegroundService を使って、起動時にNodeサービスを立ち上げる方針にしました。APK更新直後の MY_PACKAGE_REPLACED からの復帰は成功しました。
ただし、Tailscaleは背景からActivityを起動してVPN接続まで進めるのが難しい場面がありました。Tailscaleアプリを明示的に前面起動すると接続でき、Tailnet経由の疎通とOpenClaw Node接続も復帰します。
また、Gateway信頼確認ダイアログが再起動後に出る問題もありました。診断APK限定で、既知のGateway TLS fingerprintを初回接続前にseedする処理を入れ、ダイアログなしで接続できるようにしました。ここも公開版に一般化するのではなく、管理下の診断APKだけに閉じています。
操作環境
9Pを操作する方法もいくつか試しました。
最初はscrcpyを使いましたが、この端末では動画エンコード相性が悪く、低解像度・低fps・旧版scrcpyでも安定しませんでした。
最終的には次の構成に寄せました。
- 映像: HDMIキャプチャ
- 操作: ADB keyevent / OpenClaw UI操作
- リモコンUI: 手元のLinuxサーバー上のWebリモコン
- 外部視聴: HDMIキャプチャをWebRTCで中継
ADBは時々切れるため、手元のLinuxサーバー側にwatchdogを置き、自動で adb connect し直すようにしました。PCやiPhoneからはWeb画面のボタンで9Pへキー入力を送れます。
今回の学び
Android TV BoxをOpenClaw Node化するときは、次の点が重要でした。
- Tailscaleは経路であって、Node化そのものではない
- Android TVではPlayストア導線が使えないことがある
- 端末側のUI表示とGateway上のNode状態は分けて見る
suが存在してもアプリがrootを取れるとは限らない- userdebug端末ではadbd起動経路が使える場合がある
- APK更新はADB転送より、端末自身のHTTPダウンロードの方が安定する場合がある
- Node IDの安定化は運用上かなり重要
- BootReceiverだけでTailscale VPNまで完全復帰できるとは限らない
- 映像と操作は分けて考えると安定しやすい
まだ残っている課題はあります。特に、完全電源OFFや赤LED状態からの復帰、Tailscale VPNの完全自動復帰、Kodi/KonomiTVまわりのUI改善は継続課題です。
それでも、9PはOpenClawからかなり実用的に扱える端末になりました。Android TV Boxは癖が強いですが、診断コマンド、ADB root経路、APK更新補助、Webリモコンを組み合わせると、思ったより深く自動化できます。