ByteScope

am broadcast

端末を変える

ブロードキャスト intent を送ります。SystemUI の demo mode などプラットフォームのレシーバをシェルから動かす方法です。

コマンド
adb shell am broadcast
コマンド
アクティビティと intent(am)
リスク
端末を変える

構文

am broadcast -a <action> [-e <key> <value>] […]

フラグと引数

トークン意味
-a <action>ブロードキャストの action。
-e <key> <value>文字列の extra を 1 つ。繰り返せます。

使用例

  • settings put global sysui_demo_allowed 1

    まずゲートを開けます。この設定が off の間、SystemUI は demo のブロードキャストを全部無視します。買ったばかりの端末では off です。

  • am broadcast -a com.android.systemui.demo -e command enter

    demo mode に入ります。他の demo コマンドより先に実行する必要があります。

  • am broadcast -a com.android.systemui.demo -e command notifications -e visible false

    自分の通知アイコンを隠します。スクリーンショットで実際に効くのはここです。

  • am broadcast -a com.android.systemui.demo -e command exit

    ステータスバーを現実に返します。

出力の読み方

出力例

Broadcasting: Intent { act=com.android.systemui.demo (has extras) }
Broadcast completed: result=0

`Broadcast completed: result=0` は intent が配送されたという意味です。誰かがそれに反応したという意味では **ありません**。レシーバは無視して構わないし、demo mode のゲートが閉じているとき、あるいはメーカーがレシーバを削除しているときの SystemUI はまさにそうします。確かめるのはこの行ではなく端末の画面です。

出力フィールド

フィールド意味
Broadcasting: Intent { … }プラットフォームが解釈した intent。
Broadcast completed: result=0配送されました。何かが応答した証拠ではありません。
Permission Denialこのビルドでは shell ユーザーがそのブロードキャストを送れません。

必要な権限

認可済みの `adb` 接続以外は何も要りません。root も `adb root` も `su` も、USB デバッグより先の開発者向けオプションも不要です。`am` は `instrument` 以外をすべて `cmd activity "$@"` に渡す 5 行のシェルスクリプト(AOSP の `cmds/am/am.sh`)なので、intent は `shell` ユーザーの資格で送られます。その資格でできないのが「保護されたブロードキャスト」の送信です。`ActivityManagerService` がシステムの呼び出し元と認めるのは `ROOT_UID`、`SYSTEM_UID`、`PHONE_UID`、`BLUETOOTH_UID`、`NFC_UID`、`SE_UID`、`NETWORK_STACK_UID` だけで、shell はそのどれでもありません。どこかのパッケージが `<protected-broadcast>` として宣言した action は、配送される前に拒否されます。`com.android.systemui.demo` はその一覧に載っていません。ケーブルから demo mode が動くのは、それだけが理由です。しかも demo のゲートは権限とは別物で、付与される能力ではなく `settings put` で書き込む Global 設定です。スクリプトに組み込む前に知っておきたい点があと 2 つあります。1 つは、この呼び出しがブロックすること。`--async` を付けないと `runSendBroadcast` は結果レシーバの中でタイムアウトなしの `wait()` に入るので、完了しないレシーバがいるとエラーを返さずシェルが固まります。もう 1 つは、重要な行が両方のストリームに散らばること。下の拒否は標準エラーに来るのに、サブコマンドの打ち間違いは標準出力に来ます。`2>&1` で集めて、綺麗に見える実行を信じる前に最初の 1 行を読んでください。

エラーメッセージの読み解き

メッセージ意味
Permission Denial: not allowed to send broadcast android.intent.action.BOOT_COMPLETED from pid=4821, uid=2000`ActivityManagerService` が、どのレシーバにも intent を見せる前に投げた `SecurityException` です。その action がどこかのパッケージの `<protected-broadcast>` 一覧に載っていて、shell はシステムの呼び出し元ではないからです。これを解除するフラグはありません。`--receiver-permission` も `--user` も、debuggable ビルドの root も効きません。一覧全体は `dumpsys package protected-broadcasts` が印字するので、action を打ち間違えたと思う前にそちらを確認してください。
Exception occurred while executing 'broadcast':`BasicShellCommandHandler.exec` が `Throwable` を捕まえ、空行とこのバナーとスタックトレースを標準エラーに書きました。原因を名乗るのは次の行です。`java.lang.IllegalArgumentException: Unknown option: --async` は、そのリリースがこのフラグを知らないという意味で、`--async` は tag android-11.0.0_r1 の `ActivityManagerShellCommand` には無く、android-12.0.0_r1 以降にあります。`No intent supplied` は `-a` の action を丸ごと書き忘れたということです。
Unknown command: brodcast`handleDefaultCommands` がサブコマンドを認識しませんでした。1 行に罠が 2 つあります。まず、これは標準エラーではなく **標準出力** に出るので、標準エラーだけを見ているスクリプトは綺麗な実行として記録します。次に、戻り値は -1 で、何ひとつ送られていません。端末の画面から結論しようとしていたことは、すべて 1 つ前の状態についての話です。
cmd: Can't find service: activity`cmd` がサブコマンドを渡すべき `activity` サービスを見つけられず、ブロードキャストはキューにすら入りませんでした。`system_server` がまだ公開していない起動途中なら想定内です。端末が壊れたと考えず、起動が終わってから送り直してください。

何が変わるか、そして戻る道

ブロードキャスト自体は何も永続的に変えませんが、受け取った側は変えられます。名前を挙げるべき例が SystemUI の demo mode です。ステータスバーを偽の時刻と偽のバッテリーで固定し、`exit` コマンドを誰かが送るまで端末はその状態のままなので、使う人は「端末が壊れた」と思い込みます。exit コマンドは常に手元に置いてください。

バージョンとメーカーに関する注意

  • demo mode は `global sysui_demo_allowed` で塞がれていて、既定値は 0 です。虚空にブロードキャストする前に設定を読んでください。
  • `enter` が最初の demo コマンドである必要があります。それなしでは他は無視されます。

これには UI があります

Android ADB Toolbox はこのコマンドを USB 経由で実行し、返ってきたものを解析します。Chromium 系ブラウザだけで、SDK も不要、どこにもアップロードしません。

Android ADB Toolbox を開く

関連コマンド

am broadcast と同じ作業で出てくるコマンド。

コマンド何をするか
am startコンポーネント指定または intent 指定でアクティビティを起動します。ディープリンクの解決を確かめる最短経路です。
settings put設定プロバイダに値を書きます。開発者向けオプションのほとんどの裏にある仕組みです。
screencapスクリーンショットを撮ります。端末上のファイルに書くか、PNG として標準出力に流します。

他のアクティビティと intent(am)コマンド

コマンド何をするか
am force-stopあるアプリのプロセスとサービスをすべて終了させます。

Web ADB ツール群のパーサを書きながら、AOSP・toybox・カーネルのソースに突き合わせて確認しています。バージョンやメーカーで本当に挙動が変わるところは、世界の半分の端末で間違いになる数値を出す代わりに、そう書いてあります。

よくある質問

adb shell のコマンドはどう実行しますか?

端末で開発者向けオプションと USB デバッグを有効にし、ケーブルを繋いだうえで、platform-tools を入れた機械から adb shell <コマンド> を実行します。adb shell だけを実行すれば対話プロンプトが出て、接頭辞なしで打てます。スクリーンショットのようにバイナリを出力するコマンドでは adb shell ではなく adb exec-out を使ってください。素のシェルサービスは端末を割り当て、それが改行のバイトを書き換えてデータを壊します。

adb shell のコマンドに root は必要ですか?

このリファレンスにあるものには不要です。すべて shell ユーザーとして動き、自分の Android ユーザー向けのパッケージ管理、ほとんどの dumpsys サービスの読み取り、設定プロバイダへの書き込み、入力の注入、共有ストレージの読み取りができます。root が必要なのはこのリファレンスがあえて避けていること、つまり他のアプリのプライベートデータの読み取り(debuggable なビルドに対する run-as を除く)、システムパーティションへの書き込み、/data/anr の直接読み取りです。

自分の端末では出力が違って見えるのはなぜ?

Android のバージョンか、メーカーのどちらかです。一部のフラグやフィールドは特定のリリースから入ったもので、それはコマンドごとに注記しています。残りはメーカーによる改変で、いちばん影響が大きいのは dumpsys です。ダンプはデバッグ出力で、そのテキストはサービスの作者が最後に印字した内容そのもの、しかもメーカーはそれを変えて自分の変更をバックポートします。フィールドは位置ではなく名前で読み、見慣れない形は失敗ではなく差分として扱ってください。

端末ではなくブラウザから実行できますか?

多くのコマンドは可能です。このサイトの Web ADB ツール群は、Chromium 系のデスクトップブラウザから WebUSB で端末と話し、SDK のインストールも不要、どこにもアップロードしません。各ページは、該当するツールがあればそこへリンクしています。知っておく価値のある制限が 2 つあります。ADB インターフェイスを保持できるプログラムは同時に 1 つだけなので、デスクトップの adb server は先に止める必要があります。そして adb forward と adb reverse はブラウザでは一切動きません。

打つより押したい場合は、Web ADB Toolkit がこの種のコマンドをブラウザから USB 経由で実行します。あるいは全コマンドを見る。