# 画面設計書 14-Firebase Game Loopベンチマーク画面

## 概要

本ドキュメントは、TFLite Benchmarkツールアプリにおける「Firebase Game Loopベンチマーク画面」（BenchmarkModelActivity - Firebase版）の画面設計書である。本画面はFirebase Test Lab上でGame Loop Intentを受け取り、TFLiteモデルのベンチマークシナリオを実行するUIなしのActivityである。

### 本画面の処理概要

**業務上の目的・背景**：Firebase Test Labを利用してTensorFlow Liteモデルの推論性能を自動テストするためのベンチマークツールである。Firebase Game Loop テストフレームワークを活用し、クラウド上の実機デバイスでベンチマークシナリオを自動実行することで、CI/CDパイプラインに組み込んだ継続的な性能検証を可能にする。10個のテストシナリオが事前定義されており、シナリオ番号に応じたベンチマーク設定で実行される。

**画面へのアクセス方法**：本ActivityはFirebase Test Labから`com.google.intent.action.TEST_LOOP`アクションのIntentで起動される。`android:theme="@android:style/Theme.NoDisplay"`が設定されたUIなしのActivityである。ローカルでは`adb shell am start -a com.google.intent.action.TEST_LOOP -n org.tensorflow.lite.benchmark.firebase/.BenchmarkModelActivity --ei scenario 1`で起動可能。

**主要な操作・処理内容**：
1. IntentのアクションがTEST_LOOPであることを検証する
2. Intent Extraからscenarioキーでシナリオ番号（int）を取得する（デフォルト: 0）
3. Intentのdata URIからFirebase Test Labのレポートファイルを取得する
4. ContentResolverを通じてレポートファイルのParcelFileDescriptorを開く
5. BenchmarkModel.run(context, scenario, reportFd)でネイティブベンチマークを実行する
6. レポートファイルのParcelFileDescriptorをクローズする
7. finish()でActivityを終了する

**画面遷移**：Firebase Test LabからTEST_LOOP Intentで起動され、ベンチマーク完了後にfinish()で自動終了する。TEST_LOOP以外のIntentを受け取った場合は即座にfinish()で終了する。

**権限による表示制御**：UIが存在しないため表示制御はない。`READ_EXTERNAL_STORAGE`と`WRITE_EXTERNAL_STORAGE`権限がモデルファイル読み込みとレポート出力に必要である。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 81 | TFLite推論エンジン | 主機能 | BenchmarkModel.run()を通じてFirebase Test Lab上でTFLiteモデルのベンチマークシナリオを実行 |
| 82 | TFLiteデリゲート | 補助機能 | ベンチマークシナリオ内でのデリゲート設定に対応 |

## 画面種別

UIなし（バックグラウンド処理）。Theme.NoDisplay。Firebase Game Loop専用。

## URL/ルーティング

- パッケージ名: `org.tensorflow.lite.benchmark.firebase`
- Activity名: `.BenchmarkModelActivity`
- Intent Filter:
  - Action: `com.google.intent.action.TEST_LOOP`
  - Category: `android.intent.category.DEFAULT`
  - Data mimeType: `application/javascript`

## 入出力項目

| 項目名 | 型 | I/O | 必須 | 説明 |
|--------|-----|-----|------|------|
| intent.action | String | 入力 | はい | `com.google.intent.action.TEST_LOOP`であること |
| scenario | int (Intent Extra) | 入力 | いいえ（デフォルト: 0） | ベンチマークシナリオ番号。0-9の範囲 |
| intent.data | Uri | 入力 | いいえ | Firebase Test Labから提供されるレポートファイルのURI |
| レポートファイル | ParcelFileDescriptor | 出力 | - | Firebase Test Labのレポートファイルにベンチマーク結果を書き込む |
| ログ出力 | Logcat | 出力 | - | TAG `tflite_BenchmarkModelActivity` でシナリオ番号と結果をログ出力 |

## 表示項目

UIなしのため表示項目は存在しない。ベンチマーク結果はFirebase Test Labレポートファイルおよびlogcatに出力される。

## イベント仕様

### 1-Activity起動（onCreate）

1. `getIntent()`でIntentを取得する
2. `intent.getAction()`がTEST_LOOPであるか検証する
   - TEST_LOOPでない場合、エラーログを出力してfinish()で終了する
3. `intent.getIntExtra("scenario", 0)`でシナリオ番号を取得する
4. `intent.getData()`でレポートファイルURIを取得する
5. レポートファイルURIが非nullの場合：
   - `getContentResolver().openAssetFileDescriptor(reportFile, "w")`でレポートファイルを書き込みモードで開く
   - `getParcelFileDescriptor()`でファイルディスクリプタを取得する
6. `BenchmarkModel.run(this, scenario, reportFd)`でネイティブベンチマークを実行する
   - reportFdはParcelFileDescriptorがnullの場合-1を渡す
7. ParcelFileDescriptorが非nullの場合、close()でファイルディスクリプタをクローズする
8. `finish()`でActivityを終了する

## データベース更新仕様

### 操作別データベース影響一覧

本画面はデータベース操作を行わない。

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| - | - | - | データベース操作なし |

## メッセージ仕様

| メッセージID | レベル | メッセージ内容 | 出力条件 |
|-------------|--------|--------------|---------|
| M-01 | ERROR | "Received non Firebase Game Loop test intent {action}" | TEST_LOOP以外のIntentアクション受信時 |
| M-02 | INFO | "Running TensorFlow Lite benchmark with scenario: {scenario}" | ベンチマーク実行開始時 |
| M-03 | INFO | "Logging the result to {path}" | レポートファイルURIが有効な場合 |
| M-04 | ERROR | "Error while opening Firebase Test Lab report file" | レポートファイルのオープン失敗時（FileNotFoundException/NullPointerException） |
| M-05 | ERROR | "Failed to close Firebase Test Lab result file" | レポートファイルのクローズ失敗時（IOException） |

## 例外処理

| 例外 | 発生条件 | 処理 |
|------|---------|------|
| 非TEST_LOOP Intent | com.google.intent.action.TEST_LOOP以外のIntent受信 | エラーログ出力後、即座にfinish()で終了 |
| FileNotFoundException | レポートファイルURIのオープン失敗 | エラーログ出力。parcelFileDescriptorはnullのまま、reportFd=-1でベンチマーク実行 |
| NullPointerException | openAssetFileDescriptor()がnullを返した場合 | エラーログ出力。parcelFileDescriptorはnullのまま、reportFd=-1でベンチマーク実行 |
| IOException | レポートファイルのclose()失敗 | エラーログを出力。ベンチマーク自体は完了済み |

## 備考

- 本Activityは`android:theme="@android:style/Theme.NoDisplay"`および`android:noHistory="true"`が設定されている
- minSdkVersion: 23、targetSdkVersion: 23
- Firebase Game Loopテストシナリオ数: 10（AndroidManifest.xmlの`com.google.test.loops`メタデータで定義）
- Intent Filterで`application/javascript` mimeTypeが指定されているが、これはFirebase Game Loopフレームワークの要件である
- No.11のBenchmarkModelActivityとは別パッケージ（org.tensorflow.lite.benchmark.firebase）の同名クラスである
- ネイティブライブラリ: `tensorflowlite_benchmark_firebase`（No.11の`tensorflowlite_benchmark`とは異なる）
- BenchmarkModel.run()はcontext、scenario、reportFdの3引数を受け取り、nativeLibraryDirを内部で解決する

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

本画面で扱うデータは、Firebase Game LoopのTEST_LOOP Intent（scenario番号とレポートファイルURI）である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AndroidManifest.xml | `tensorflow/lite/tools/benchmark/experimental/firebase/android/AndroidManifest.xml` | Activity宣言、Theme.NoDisplay、Intent Filter（TEST_LOOPアクション、application/javascript mimeType）、com.google.test.loopsメタデータ（10シナリオ）を確認（18-51行目） |

**読解のコツ**: Firebase Game Loopのインテントフィルター構造とcom.google.test.loopsメタデータの意味を理解する。loopsの値がシナリオの総数を示す。

#### Step 2: エントリーポイントを理解する

BenchmarkModelActivityのonCreateがエントリーポイントである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | BenchmarkModelActivity.java | `tensorflow/lite/tools/benchmark/experimental/firebase/android/src/org/tensorflow/lite/benchmark/firebase/BenchmarkModelActivity.java` | onCreate内でTEST_LOOPアクション検証→scenario取得→レポートファイルオープン→BenchmarkModel.run()→ファイルクローズ→finish()のフローを確認（38-73行目） |

**主要処理フロー**:
1. **41行目**: `Intent intent = getIntent()` でIntentを取得
2. **42-45行目**: TEST_LOOPアクション検証。非TEST_LOOPの場合はfinish()で終了
3. **46行目**: `intent.getIntExtra("scenario", 0)` でシナリオ番号取得
4. **50行目**: `intent.getData()` でレポートファイルURIを取得
5. **51-58行目**: URIが非nullの場合、ContentResolverでレポートファイルを書き込みモードで開く
6. **61行目**: reportFdの決定（ParcelFileDescriptorがnullなら-1）
7. **62行目**: `BenchmarkModel.run(this, scenario, reportFd)` でネイティブベンチマーク実行
8. **64-69行目**: ParcelFileDescriptorのクローズ処理
9. **72行目**: `finish()` でActivity終了

#### Step 3: ネイティブブリッジを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | BenchmarkModel.java | `tensorflow/lite/tools/benchmark/experimental/firebase/android/src/org/tensorflow/lite/benchmark/firebase/BenchmarkModel.java` | ネイティブライブラリ名（tensorflowlite_benchmark_firebase）、run()メソッドでのnativeLibraryDir解決とJNI呼び出しを確認（21-34行目） |

**主要処理フロー**:
- **23行目**: 静的初期化子で`tensorflowlite_benchmark_firebase`ネイティブライブラリをロード
- **28-30行目**: run() - context.getApplicationInfo().nativeLibraryDirを取得し、nativeRun()に渡す
- **33行目**: `private static native void nativeRun(String libraryDir, int scenario, int reportFd)` JNIメソッド宣言

### プログラム呼び出し階層図

```
BenchmarkModelActivity.onCreate()
    |
    +-- getIntent()                                 [Intent取得]
    |
    +-- intent.getAction() == TEST_LOOP ?           [アクション検証]
    |       |
    |       +-- (No)  --> Log.e() --> finish()      [非TEST_LOOPで即終了]
    |
    +-- intent.getIntExtra("scenario", 0)           [シナリオ番号取得]
    |
    +-- intent.getData()                            [レポートファイルURI取得]
    |       |
    |       +-- (非null) --> getContentResolver()
    |                            .openAssetFileDescriptor(uri, "w")
    |                            .getParcelFileDescriptor()
    |
    +-- BenchmarkModel.run(context, scenario, reportFd)
    |       |
    |       +-- context.getApplicationInfo().nativeLibraryDir
    |       +-- nativeRun(libraryDir, scenario, reportFd)   [JNI]
    |
    +-- parcelFileDescriptor.close()                [ファイルクローズ]
    |
    +-- finish()                                    [Activity終了]
```

### データフロー図

```
[入力]                           [処理]                              [出力]

Firebase TEST_LOOP Intent   -->  BenchmarkModelActivity         -->  Logcat (TAG: tflite_BenchmarkModelActivity)
  action: TEST_LOOP               .onCreate()
  scenario: int                        |
  data: Uri (reportFile)          アクション検証
                                       |
                                  シナリオ番号取得
                                       |
                                  レポートファイルオープン
                                       |
                                  BenchmarkModel.run()           -->  レポートファイル (Firebase Test Lab)
                                       |                                (ParcelFileDescriptor経由)
                                  nativeRun() [JNI]
                                       |
                                  ファイルクローズ
                                       |
                                  finish()                       -->  Activity終了
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| BenchmarkModelActivity.java | `tensorflow/lite/tools/benchmark/experimental/firebase/android/src/org/tensorflow/lite/benchmark/firebase/BenchmarkModelActivity.java` | ソース | メインActivity。TEST_LOOP Intent処理、シナリオ取得、レポートファイル管理 |
| BenchmarkModel.java | `tensorflow/lite/tools/benchmark/experimental/firebase/android/src/org/tensorflow/lite/benchmark/firebase/BenchmarkModel.java` | ソース | ネイティブベンチマーク実行のJNIブリッジクラス。Firebase版はscenarioとreportFdを受け取る |
| AndroidManifest.xml | `tensorflow/lite/tools/benchmark/experimental/firebase/android/AndroidManifest.xml` | 設定 | Activity宣言、Intent Filter（TEST_LOOP）、com.google.test.loopsメタデータ、権限定義 |
