新訳:ADB Helperをハックしてみた(adb.js)

この記事は『Firefox OS / B2G OS Advent Calendar 2016』 12日目の記事です。
Firefox OS で、使用されている「ADB Helper」を再度まとめ直してみました。

目次

 

前回、新訳:ADB Helperをハックしてみた(install.rdf,main.js)に引き続き、
adb-helperをハックしていきたいと思います

  1. フォルダ内のファイル構成
  2. adb.js
  3. 予備知識
  4. モジュール構成
  5. 今回のまとめ
  6. 関連記事
  7. 今年のAdvent Calendar
フォルダ内のファイル構成

 

[ 解凍フォルダ内のjsファイル群 ]
windows
mac
linux

install.rdf

main.js

adb.js

adb-client.js

adb-running-checker.js

adb-socket.js

bootstrap.js

scanner.js

device.js
※1
※1
※1

devtools-import.js
※1
※1
※1

devtools-require.js
※1
※1
※1

fastboot.js
※1
※1
※1

subprocess.js
※2
※2
※2

subprocess_worker_win.js
※3
※3
※3

subprocess_worker_unix.js
※4
※4
※4

 
※1 : version 0.7.1 時点では無かったファイル
※2 : version 0.7.1 時点で有ったファイル
※3 : version 0.7.1 時点で有ったファイル、Windows用ファイル
※4 : version 0.7.1 時点で有ったファイル、mac,linux用ファイル

OS毎に使用されるファイルが有る事が確認できます。

adb.js

 
前回に引き続き、adb.jsファイルからハックしていきたいと思います。
 
ご覧の通り、各OSでの差異はありません。

2014.11.06 時点から、かなりバージョンアップしているのが分かります。

モジュールは結構ありますね・・・・(^^;;;

予備知識
  1. Strict モード(use strict)

    ECMAScript 5 の strict モードは、JavaScript の制限された異形にオプトインする方法です。
    strict モードは単なるサブセットではありません:
    strict モードは意図的に、通常モードとは異なる意味を持っています。
    strict モードをサポートしないブラウザは、strict モードのコードについて
    サポートするブラウザとは異なる動作をする可能性がありますので、
    strict モードに関する側面をサポートするかの機能テストを行わずに
    strict モードを頼らないでください。

    strict モードのコードと非 strict モードのコードは共存できますので、
    スクリプトを順次 strict モードにオプトインすることができます。
    https://developer.mozilla.org/ja/docs/Web/JavaScript/Strict_mode

  2. let 文

    let 文は変数に対するローカルスコープを提供します。
    let 文はコードのある 1 つのブロックのレキシカルスコープに 0 以上の変数を
    結びつけることによって働き、それ以外はブロック文と全く同じです。

    特に、let 文の内側で var を使って定義された変数のスコープは、
    let 文の外側でそれが定義された場合と同じであり、
    そのような変数は従来通り関数スコープを持つことに注意してください。
    https://developer.mozilla.org/ja/docs/Web/JavaScript/New_in_JavaScript/1.7#let_.E6.96.87

  3. module

    module
    現在のモジュールへの参照です。
    特に module.exports は exports オブジェクトと同じです。
    より詳しくは src/node.js を参照してください。
    module は実際はグローバルではなく、各モジュール毎のローカルです。
    http://d.hatena.ne.jp/jovi0608/20111226/1324879536

  4. JavaScript コードモジュールの利用(jsm)

    JavaScript コードモジュールは、Gecko 1.9 で導入されたコンセプトであり、
    特権を持った異なるスコープ間でコードを共有するために用いられます。
    また、モジュールは、グローバルな JavaScript のシングルトンオブジェクトを
    生成するために用いることもできます
    (以前は JavaScript XPCOM オブジェクトを使う必要がありました)。
    JavaScript コードモジュールは、登録されたパスに配置された純粋な JavaScript のコードです。
    Components.utils.import() を使って、 XUL スクリプトや JavaScript XPCOM スクリプトのような
    特定のJavaScript のスコープへモジュールを読み込むことができます。
    https://developer.mozilla.org/ja/docs/Mozilla/JavaScript_code_modules/Using

  5. subprocess

    subprocess
    subprocess.jsm – start a process in your Firefox Extension and read/write data to/from it using stdin/stdout/stderr streams.
    https://github.com/bit/subprocess

モジュール構成

 
上記予備知識をもとにハックしていきます!

ファイル位置
処理概要

1 ~ 37 行目
  • Cc, Ci, Cu, Cr にchrome ライブラリ読み込み
  • subprocess にライブラリ読み込み
  • file にライブラリ読み込み
  • env にライブラリ読み込み
  • XPCOMABI にライブラリ読み込み
  • setTimeout にライブラリ読み込み
  • client を用いて、adb-client.js を読み込み
  • Cu, TextEncoder, TextDecoder に Services.jsm をインポート
  • 変数 promise に Promise.jsm を代入
  • Cu に OS.File for the main thread をインポート

50 行目以降
ADB オブジェクト
  • didRunInitially() プロパティ
  • ready() プロパティ
  • adb_init() 関数
  • adb_start() 関数
  • stop() 関数
  • adb_kill() 関数
  • _isAdbRunning() 関数
  • adb_trackDevices() 関数
  • adb_listDevices() 関数
  • adb_forwardPort() 関数
  • adb_checkFileMode() 関数
  • adb_pull() 関数
  • adb_push() 関数
  • adb_shell() 関数
  • adb_reboot() 関数
  • adb_rebootRecovery() 関数
  • rebootBootloader() 関数
  • adb_root() 関数
  • adb_runCommand() 関数

 
ADB オブジェクトが、かなりのボリュームなので、下表にまとめました。

関数名
処理概要

adb_init() 関数
  • platform に Services.appinfo.OS を代入
  • uri に resource://adbhelperatmozilla.org/ 代入
  • platform の条件分岐(OS 毎)
  • 上記結果を元に Services.io.newURI 用いて url を取得

adb_start() 関数

stop() 関数
  • コメント
    adb サーバーが動作している時だけを停止します。
    起動前に使用した場合、immediately を返します。
  • 引数:sync を用いて、adb サーバーを停止させます

adb_kill() 関数

_isAdbRunning() 関数
  • platform に Services.appinfo.OS を代入
  • Deferred オブジェクト deferred を promise.defer() 用いて生成
  • platform 判定
    • WINNT の時、変数 ps に “C:\\windows\\system32\\tasklist.exe” をセット
    • 上記以外 の時、file.exists() を使用して 変数 ps に値をセット
  • 変数 ps を用いて、サブプロセス subprocess 呼出

adb_trackDevices() 関数
  • コメント
    デバイスの接続/切断をトレースします。
    ソケットが繋がっている間、再利用できません。
  • adb-client.js の connect() 呼出
  • 引数:”adb-track-devices-start” で、
    関数 socket.s.onopen を定義し、同期
  • 引数:”adb-track-devices-stop” で、
    関数 socket.s.onerror を定義し、同期
  • 引数:”adb-track-devices-stop” で、
    関数 socket.s.onclose を定義し、同期
  • 関数 socket.s.ondata を定義し、同期
    • adb-client.js の checkResponse() 呼出
    • adb-client.js の unpackPacket() 呼出、変数 packet へ代入
    • packet.data を判定し、events.emit() を実行

adb_listDevices() 関数
  • コメント
    デバイス名の配列を返します。
  • runCommand を実行し、データ取得成功時に
    デバイス名を配列に格納します。

adb_forwardPort() 関数
  • runCommand を実行し、データ取得成功時に
    データを返します。

adb_checkFileMode() 関数
  • コメント
    ファイルのモードをチェックします。
  • チェック用配列:masks を定義
  • チェック用配列:masks に該当する値が無い時、false を返します。
  • 上記以外の時、チェック用配列:masks に該当する値を返します

adb_pull() 関数
  • コメント
    デバイスからファイルを pull します。
  • Deferred オブジェクト deferred を promise.defer() 用いて生成
  • 各種変数を定義
  • 関数 shutdown を定義します。
  • 関数 extractChunkDataHeader を定義します。
    • 配列変数:headerArray を初期化します
    • 配列変数:headerArray に引数data を代入します。
  • 関数 checkChunkDataHeader を定義します。
    • データ長 + 現在のヘッダ長 > 8 バイト以上の時、
      以下の処理を実行します。
    • 関数 extractChunkDataHeader を実行します。
    • 配列変数:headerArray[0] と定数:DATAが不一致の時、shutdown() を実行し、false を返します。
    • ヘッダの長さにデータ長を加算します。
    • true を返します。
  • 関数 checkDone を定義します。
    • データ長が 8 バイト以外の時、false を返します。
    • 配列変数:doneFlagArray を Uint32Array で、定義します。
    • 配列変数:doneFlagArray[0] が定数:DONE の時、
      true を返します。
    • 上記以外の時、false を返します。
  • 関数 runFSM を定義します。
    • 変数:state を判定し、状態毎の処理を実行します。
  • 関数 setupSocket を定義します。
    • 関数 socket.s.onerror を定義し、引数:”SOCKET_ERROR” で、deferred.reject() を実行します。
    • 関数 socket.s.onopen を定義し、関数runFSM() を実行します。
    • 関数 socket.s.onclose を定義します。
    • 関数 socket.s.ondata を定義し、関数runFSM() を実行します。
  • adb-client.js の connect() 呼出
  • 関数 setupSocket() を実行します。
  • deferred を return

adb_push() 関数
  • コメント
    ファイルをデバイスへ push します。
  • Deferred オブジェクト deferred を promise.defer() 用いて生成
  • 各種変数を定義
  • 関数 shutdown を定義します。
  • 関数 runFSM を定義します。
    • 変数:state を判定し、状態毎の処理を実行します。
  • 関数 setupSocket を定義します。
    • 関数 socket.s.onerror を定義し、引数:”SOCKET_ERROR” で、deferred.reject() を実行します。
    • 関数 socket.s.onopen を定義し、関数runFSM() を実行します。
    • 関数 socket.s.onclose を定義します。
    • 関数 socket.s.ondata を定義し、関数runFSM() を実行します。
  • 関数 OS.File.stat() を実行します。
    • ファイル情報取得成功時、以下の処理を実行します。
    • 取得情報がディレクトリの場合、deferred.reject() を実行します。
    • 取得情報がファイルの場合、 ファイル情報を格納します。
    • ファイル情報取得失敗時、deferred.reject() を実行します。
  • deferred を return

adb_shell() 関数
  • コメント
    ファイルをデバイスへ push します。
  • Deferred オブジェクト deferred を promise.defer() 用いて生成
  • 各種変数を定義
  • 関数 shutdown を定義します。
  • 関数 runFSM を定義します。
    • 変数:state を判定し、状態毎の処理を実行します。
  • adb-client.js の connect() 呼出
  • 関数 socket.s.onerror を定義し、引数:”SOCKET_ERROR” で、deferred.reject() を実行します。
  • 関数 socket.s.onopen を定義し、関数runFSM() を実行します。
  • 関数 socket.s.onclose を定義します。
  • 関数 socket.s.ondata を定義し、関数runFSM() を実行します。
  • deferred を return

adb_reboot() 関数
  • shell() を実行します。

adb_rebootRecovery() 関数
  • shell() を実行します。

adb_rebootBootloader() 関数
  • shell() を実行します。

adb_root() 関数
  • Deferred オブジェクト deferred を promise.defer() 用いて生成
  • 各種変数を定義
  • 関数 shutdown を定義します。
  • 関数 runFSM を定義します。
    • 変数:state を判定し、状態毎の処理を実行します。
  • adb-client.js の connect() 呼出
  • 関数 socket.s.onerror を定義し、引数:”SOCKET_ERROR” で、deferred.reject() を実行します。
  • 関数 socket.s.onopen を定義し、関数runFSM() を実行します。
  • 関数 socket.s.onclose を定義し、deferred.resolve() を実行します。
  • 関数 socket.s.ondata を定義し、関数runFSM() を実行します。
  • deferred を return

adb_runCommand() 関数
  • コメント
    非同期的に adb コマンドを実行します。
  • Deferred オブジェクト deferred を promise.defer() 用いて生成
  • プロパティ:ready = false の時、以下の処理を実行します。
    • 関数:setTimeout を用いて、deferred.reject() を実行します。
    • deferred を return
  • adb-client.js の connect() 呼出
  • 関数 socket.s.onopen を定義し、同期します。
    • adb-client.js の createRequest() 呼出
    • adb コマンドを送信します。
  • 関数 socket.s.onerror を定義し、deferred.reject() を実行します。
  • 関数 socket.s.onclose を定義します。
  • 関数 socket.s.ondata を定義し、同期
    • adb-client.js の unpackPacket() 呼出
    • adb-client.js の unpackPacket() 呼出、変数 packet へ代入
    • packet.data を判定し、deferred.reject() を実行します。
    • deferred.resolve() を実行します。
  • deferred を return

今回のまとめ

 
今回は、ADB Helper の中でも核となるコードをハックしました。
かなり長いブログになってしまったので、ご覧になる方にどう映るか些か心配です。
初めて見る方のソースコード解析のお役に立てば、幸いです。

次回は、adb-client.js をハックする予定です。

関連記事

今年のAdvent Calendar

去年のAdvent Calendar

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です