Skip to content

UUIDを持つBLEのアドバタイズを全てcsvに書き込む様にする#5

Draft
hihumikan wants to merge 1 commit intomainfrom
feature/scan-all-ble-export-uuid
Draft

UUIDを持つBLEのアドバタイズを全てcsvに書き込む様にする#5
hihumikan wants to merge 1 commit intomainfrom
feature/scan-all-ble-export-uuid

Conversation

@hihumikan
Copy link
Copy Markdown
Member

@hihumikan hihumikan commented Mar 1, 2026

Summary by CodeRabbit

リリースノート

  • 新機能

    • BLE広告データ解析エンジンを追加しました。
  • 改善

    • Bluetoothビーコンスキャンの安定性と信頼性を向上させました。
    • アプリ停止時にキューに入っているデータの保存を確実にしました。
  • テスト

    • BLE広告データ解析機能のテストスイートを追加しました。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 1, 2026

Walkthrough

BLE広告データを解析するための新しいユーティリティ「BleAdvertisementParser」を導入し、BLERepository内の手動iBeacon解析を置き換えました。BLEApiのスキャンコールバック署名を更新し、OtherFileStorageApiにキューフラッシング機能を追加しました。

Changes

Cohort / File(s) Summary
BLE Advertisement Parser
app/src/main/java/net/kajilab/elpissender/utils/BleAdvertisementParser.kt
新しいパーサーオブジェクトを追加。UUIDと識別子を抽出するparseUuids()parseIdentifiers()の2つのパブリックメソッドを提供。iBeacon、サービスUUID、製造者データなど複数のBLE広告構造に対応。
Repository Integration
app/src/main/java/net/kajilab/elpissender/repository/BLERepository.kt
BleAdvertisementParserを使用するよう統合。手動UUID抽出および低レベル解析コードを削除。スキャン結果からUUIDを抽出し、タイムスタンプとRSSI付きでキューに追加するロジックを実装。
Scan API Updates
app/src/main/java/net/kajilab/elpissender/api/BLEApi.kt
スキャンフィルタの依存関係を削除し、startBLEBeaconScan()のコールバック署名を(ScanResult?)から(ScanResult)に変更。onBatchScanResultsコールバックハンドラを追加し、エラーログを実装。
Storage Enhancement
app/src/main/java/net/kajilab/elpissender/api/OtherFileStorageApi.kt
stop()メソッドにキューのフラッシュ機能を追加。停止時に残存するキュー項目をコピーしてクリアし、保存処理を実行。
Parser Tests
app/src/test/java/net/kajilab/elpissender/utils/BleAdvertisementParserTest.kt
BleAdvertisementParser.parseUuids()の3つの単体テストを追加。iBeacon、サービスUUID、重複排除、空入力ケースをカバー。

Sequence Diagram

sequenceDiagram
    participant Scanner as BLE Scanner
    participant BLEApi as BLEApi
    participant Repository as BLERepository
    participant Parser as BleAdvertisementParser
    participant Queue as Data Queue
    
    Scanner->>BLEApi: onBatchScanResults(results)
    loop For each ScanResult
        BLEApi->>Repository: resultBeacon(ScanResult)
        Repository->>Parser: parseUuids(scanRecord.bytes)
        Parser->>Parser: extractStructures()<br/>parseStructureUuids()
        Parser-->>Repository: List<UUID>
        loop For each UUID
            Repository->>Queue: addQueue(timestamp, UUID, RSSI)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 ウサギが新しいパーサーを持ってきた、
BLE信号を解きほぐす魔法で、
iBeaconの謎もスッキリ解決、
キューはしっかり最後まで流れ落ちる、
データの森も迷わなくなったよ!✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed プルリクエストのタイトルは、BLE広告データから全てのUUIDを抽出してCSVファイルに書き込むという、変更セットの主要な目的を明確に説明しています。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/scan-all-ble-export-uuid

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
app/src/main/java/net/kajilab/elpissender/utils/BleAdvertisementParser.kt (1)

426-429: data classByteArrayを使用する際の注意点。

data classequals()hashCode()ByteArrayの参照比較を行うため、内容が同じでも異なるインスタンスは等価とみなされません。

現在の実装ではAdvertisementStructureはプライベートかつイテレーション用途のみで使用されているため、実質的な問題はありませんが、将来的に比較やセットへの格納が必要になった場合は注意が必要です。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/net/kajilab/elpissender/utils/BleAdvertisementParser.kt`
around lines 426 - 429, AdvertisementStructure uses a ByteArray field which
causes its generated equals()/hashCode() to do reference comparisons; to fix,
either change the type to an immutable collection like List<Byte> for
value-based equality or keep ByteArray but override equals() and hashCode() in
AdvertisementStructure to use data.contentEquals(other.data) and
data.contentHashCode() (and retain the data class semantics otherwise),
referencing AdvertisementStructure, its data property, and the
equals()/hashCode() behavior when making the change.
app/src/main/java/net/kajilab/elpissender/api/BLEApi.kt (1)

62-77: onScanResultonBatchScanResultsの両方を定義していますが、バッチスキャンは有効化されていません。

現在のScanSettingsではsetReportDelay()が設定されていないため、スキャン結果はonScanResultのみを通じて配信されます。onBatchScanResultsはバッチレポートが有効な場合のみ呼び出されます。

バッチスキャンを有効にする場合は、ScanSettingssetReportDelay()を追加してください:

💡 バッチスキャンを有効化する場合の修正案
 val scanSettings =
     ScanSettings.Builder()
         .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
+        .setReportDelay(1000) // ミリ秒単位でバッチ間隔を指定
         .build()

現状のままでも動作に問題はありませんが、onBatchScanResultsのコードは実行されません。意図的な防御的コーディングであれば問題ありませんが、確認をお願いします。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/net/kajilab/elpissender/api/BLEApi.kt` around lines 62 -
77, The code defines both onScanResult and onBatchScanResults but ScanSettings
does not enable batching, so onBatchScanResults will never be called; either
remove/annotate the unused onBatchScanResults handler or enable batch reporting
by updating the ScanSettings construction to call setReportDelay(...) (and keep
onBatchScanResults) so batched results are delivered; locate the BLE scan setup
(where ScanSettings is built) and adjust accordingly and/or remove the unused
onBatchScanResults method in BLEApi.kt to match intended behavior.
app/src/test/java/net/kajilab/elpissender/utils/BleAdvertisementParserTest.kt (1)

79-95: エッジケースのテスト追加を検討してください。

現在のテストは正常系をカバーしていますが、以下のようなエッジケースのテスト追加を推奨します:

  • 不正な形式のADストラクチャ(length=0、途中で切れたデータ)
  • 複数のiBeaconを含むアドバタイズメント
  • 16-bit/32-bit/128-bit UUIDの各パターン
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/test/java/net/kajilab/elpissender/utils/BleAdvertisementParserTest.kt`
around lines 79 - 95, Add unit tests to BleAdvertisementParserTest that cover
the edge cases missing from the current suite: write tests invoking
BleAdvertisementParser.parseUuids for (1) an AD structure with length=0, (2) a
truncated AD payload (cut off mid-structure) to ensure it fails gracefully or
returns empty, (3) an advertisement containing multiple iBeacon structures to
ensure all UUIDs are returned, and (4) advertisements containing 16-bit, 32-bit,
and 128-bit UUID encodings to verify correct parsing for each size; place these
as new `@Test` methods in the same test class and assert expected behavior (empty
or correct identifiers) for each case.
app/src/main/java/net/kajilab/elpissender/repository/BLERepository.kt (1)

42-42: CSVフォーマットにおけるスペースの扱いを確認してください。

データフォーマットが"$time , $uuid , $rssi"となっており、カンマの前後にスペースが含まれています。これは一般的なCSVフォーマット(カンマのみ)とは異なります。

後続の処理でCSVパーサーを使用する場合、スペースがフィールド値の一部として扱われる可能性があります。意図的なフォーマットであれば問題ありませんが、確認をお願いします。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/net/kajilab/elpissender/repository/BLERepository.kt` at
line 42, The CSV field string assigned to data ("data = \"$time , $uuid ,
$rssi\"") includes spaces around commas which may be treated as part of field
values by CSV parsers; update the assignment in BLERepository.kt (the data
variable/assignment line) to use a standard CSV format without spaces (e.g.,
"$time,$uuid,$rssi") or, if spaces are required, document and/or explicitly trim
fields when parsing so downstream CSV consumers won't see leading/trailing
spaces.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/net/kajilab/elpissender/api/OtherFileStorageApi.kt`:
- Around line 68-72: 停止時の toArray()→clear() に競合があり queue の項目が欠落するため、queue
のスナップショット取得とクリアを原子的に行うよう修正してください:queue への参照(queue)に対し synchronized
ブロック(または同等のロック)を導入し、停止時のブロック(現在の if (queue.isNotEmpty()) {...} 内)と定期排出側の
runnable(該当 Runnable/スレッド内の toArray()/clear()
を実行している箇所)の両方を同じロックで保護して、コピー作成(toArray 相当)と queue.clear() を一連の操作として実行し、そのコピーを
saveArrayDeque(...) に渡すようにしてください。

In `@app/src/main/java/net/kajilab/elpissender/utils/BleAdvertisementParser.kt`:
- Around line 94-96: The branch handling TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT
currently calls parseServiceDataUuid and thus only returns a single UUID; change
it to call parsePlainUuidList (with the same offset/length usage as other
plain-UUID parsers, e.g., parsePlainUuidList(data, 2)) so the full list of
16-bit UUIDs is parsed; update the mapping that uses
TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT to invoke parsePlainUuidList instead of
parseServiceDataUuid.

---

Nitpick comments:
In `@app/src/main/java/net/kajilab/elpissender/api/BLEApi.kt`:
- Around line 62-77: The code defines both onScanResult and onBatchScanResults
but ScanSettings does not enable batching, so onBatchScanResults will never be
called; either remove/annotate the unused onBatchScanResults handler or enable
batch reporting by updating the ScanSettings construction to call
setReportDelay(...) (and keep onBatchScanResults) so batched results are
delivered; locate the BLE scan setup (where ScanSettings is built) and adjust
accordingly and/or remove the unused onBatchScanResults method in BLEApi.kt to
match intended behavior.

In `@app/src/main/java/net/kajilab/elpissender/repository/BLERepository.kt`:
- Line 42: The CSV field string assigned to data ("data = \"$time , $uuid ,
$rssi\"") includes spaces around commas which may be treated as part of field
values by CSV parsers; update the assignment in BLERepository.kt (the data
variable/assignment line) to use a standard CSV format without spaces (e.g.,
"$time,$uuid,$rssi") or, if spaces are required, document and/or explicitly trim
fields when parsing so downstream CSV consumers won't see leading/trailing
spaces.

In `@app/src/main/java/net/kajilab/elpissender/utils/BleAdvertisementParser.kt`:
- Around line 426-429: AdvertisementStructure uses a ByteArray field which
causes its generated equals()/hashCode() to do reference comparisons; to fix,
either change the type to an immutable collection like List<Byte> for
value-based equality or keep ByteArray but override equals() and hashCode() in
AdvertisementStructure to use data.contentEquals(other.data) and
data.contentHashCode() (and retain the data class semantics otherwise),
referencing AdvertisementStructure, its data property, and the
equals()/hashCode() behavior when making the change.

In
`@app/src/test/java/net/kajilab/elpissender/utils/BleAdvertisementParserTest.kt`:
- Around line 79-95: Add unit tests to BleAdvertisementParserTest that cover the
edge cases missing from the current suite: write tests invoking
BleAdvertisementParser.parseUuids for (1) an AD structure with length=0, (2) a
truncated AD payload (cut off mid-structure) to ensure it fails gracefully or
returns empty, (3) an advertisement containing multiple iBeacon structures to
ensure all UUIDs are returned, and (4) advertisements containing 16-bit, 32-bit,
and 128-bit UUID encodings to verify correct parsing for each size; place these
as new `@Test` methods in the same test class and assert expected behavior (empty
or correct identifiers) for each case.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37ee816 and 60442e2.

📒 Files selected for processing (5)
  • app/src/main/java/net/kajilab/elpissender/api/BLEApi.kt
  • app/src/main/java/net/kajilab/elpissender/api/OtherFileStorageApi.kt
  • app/src/main/java/net/kajilab/elpissender/repository/BLERepository.kt
  • app/src/main/java/net/kajilab/elpissender/utils/BleAdvertisementParser.kt
  • app/src/test/java/net/kajilab/elpissender/utils/BleAdvertisementParserTest.kt

Comment on lines +68 to +72
if (queue.isNotEmpty()) {
val copy = queue.toArray()
queue.clear()
saveArrayDeque(copy)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

停止時フラッシュでキュー項目を取りこぼす可能性があります。

Line 69-70 の toArray()clear() が非同期保護なしのため、別スレッドから同時に追加されると、copy に載らないデータが clear() で消えて CSV 未出力になります。PR目的に対してデータ欠損リスクがあるので、スナップショット化とクリアを原子的にしてください(synchronized 等)。同じ根本原因が runnable 側(Line 41-43)にもあるため、両方を同じ方式で保護するのが必要です。

修正例(排出処理を原子的にする)
 class OtherFileStorageApi(
@@
     private val queue: ArrayDeque<String>,
 ) {
+    private val queueLock = Any()
@@
             override fun run() {
-                // 1:コピー作成
-                val pressureCopy = queue.toArray()
-                // 2:本体をクリア
-                queue.clear()
+                val pressureCopy =
+                    synchronized(queueLock) {
+                        queue.toArray().also { queue.clear() }
+                    }
                 // 3:コピーをファイルに書き込む
                 saveArrayDeque(pressureCopy)
@@
     fun stop(): File {
         // 別スレッドを停止
         handler.removeCallbacks(runnable)
-        if (queue.isNotEmpty()) {
-            val copy = queue.toArray()
-            queue.clear()
-            saveArrayDeque(copy)
-        }
+        val copy =
+            synchronized(queueLock) {
+                if (queue.isEmpty()) emptyArray() else queue.toArray().also { queue.clear() }
+            }
+        if (copy.isNotEmpty()) saveArrayDeque(copy)
         return File(filePath)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/net/kajilab/elpissender/api/OtherFileStorageApi.kt` around
lines 68 - 72, 停止時の toArray()→clear() に競合があり queue の項目が欠落するため、queue
のスナップショット取得とクリアを原子的に行うよう修正してください:queue への参照(queue)に対し synchronized
ブロック(または同等のロック)を導入し、停止時のブロック(現在の if (queue.isNotEmpty()) {...} 内)と定期排出側の
runnable(該当 Runnable/スレッド内の toArray()/clear()
を実行している箇所)の両方を同じロックで保護して、コピー作成(toArray 相当)と queue.clear() を一連の操作として実行し、そのコピーを
saveArrayDeque(...) に渡すようにしてください。

Comment on lines +94 to +96
TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT,
TYPE_SERVICE_DATA_16_BIT,
-> parseServiceDataUuid(data, 2)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

TYPE_SERVICE_SOLICITATION_UUIDS_16_BITのパース処理に誤りがあります。

TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT(0x14)は16-bit UUIDのリストを含むため、parsePlainUuidListを使用すべきです。現在の実装ではparseServiceDataUuidを使用しているため、最初のUUIDのみが抽出され、残りのUUIDが失われます。

BLE仕様:

  • 0x14 (Service Solicitation 16-bit): 16-bit UUIDのリスト
  • 0x16 (Service Data 16-bit): 1つの16-bit UUID + データペイロード
🐛 修正案
-            TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT,
             TYPE_SERVICE_DATA_16_BIT,
             -> parseServiceDataUuid(data, 2)
+            TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT -> parsePlainUuidList(data, 2)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/net/kajilab/elpissender/utils/BleAdvertisementParser.kt`
around lines 94 - 96, The branch handling TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT
currently calls parseServiceDataUuid and thus only returns a single UUID; change
it to call parsePlainUuidList (with the same offset/length usage as other
plain-UUID parsers, e.g., parsePlainUuidList(data, 2)) so the full list of
16-bit UUIDs is parsed; update the mapping that uses
TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT to invoke parsePlainUuidList instead of
parseServiceDataUuid.

@hihumikan hihumikan marked this pull request as draft March 17, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant