A lightweight Android app for viewing nearby PICS Bluetooth traffic-signal broadcasts.
Find a file
2026-11-01 22:11:36 +09:00
app initial commit 2026-11-01 22:11:36 +09:00
gradle/wrapper initial commit 2026-11-01 22:11:36 +09:00
.gitattributes initial commit 2026-11-01 22:11:36 +09:00
.gitignore initial commit 2026-11-01 22:11:36 +09:00
build.gradle initial commit 2026-11-01 22:11:36 +09:00
gradle.properties initial commit 2026-11-01 22:11:36 +09:00
gradlew initial commit 2026-11-01 22:11:36 +09:00
gradlew.bat initial commit 2026-11-01 22:11:36 +09:00
LICENSE initial commit 2026-11-01 22:11:36 +09:00
README.md initial commit 2026-11-01 22:11:36 +09:00
settings.gradle initial commit 2026-11-01 22:11:36 +09:00

PICS Viewer Android PoC

A small native Android app for inspecting nearby 高度化PICS BLE advertisements. Android 12+ (API 31), compiled/targeted against API 35. No server, account, internet permission, pairing, or GATT connection. The UI uses platform Views and Java 17; there are no third-party runtime libraries.

Try the APK

Install pics-viewer-debug.apk from this directory on your phone, or use a host with Android platform-tools and USB debugging:

adb install -r pics-viewer-debug.apk

The package is dev.pics.viewer, labeled PICS Viewer.

  1. Tap Demo at home. It feeds synthetic packets through the real parser and dashboard, cycling red / green / flashing green. No permissions are needed.
  2. Tap Stop, then Scan BLE. Grant Nearby devices and precise location while using the app. Enable Bluetooth if prompted, then tap Scan BLE again.
  3. BLE ads increasing confirms actual advertisement reception. 0x01CE counts matching manufacturer payloads; Parsed counts accepted PICS-shaped payloads. At home, zero PICS packets is expected. Not all Bluetooth devices advertise continuously, so zero BLE ads alone does not establish a failure.
  4. Open Logs → Copy logs and paste the report back into chat if anything fails. If permissions were denied, Logs → Settings opens the app's settings. If reception stays at zero, also check the system Location switch.

Each session stops when the app leaves the foreground (including rotation), on Stop, or after ten minutes. It keeps the screen awake while active. It does not automatically resume. The dashboard retains the last readings after Stop and marks them accordingly. Reopening the activity clears dashboard data, but logs survive. A new scan or demo starts with empty counters and intersection data.

Logs

The app records its version, phone model, Android version/build/security patch, permissions, Bluetooth and Location switch status, start/stop/error events, five-second reception summaries, and sampled PICS payloads. Payload logs are limited to once a second per intersection/type, except changed signal values. Malformed payloads log the first five failures and then every hundredth failure.

Non-PICS device names, addresses, and payloads are not recorded. Up to 32 observed manufacturer IDs are included for reception diagnostics. PICS payloads can include intersection names and locations. Demo records are explicitly labeled.

Logs are stored only in the app's private storage, with no backup or upload. The retained buffer is limited to 1,000 lines and 160,000 characters, whichever is reached first. The view shows the latest 120 lines; Copy exports the whole retained buffer and current diagnostics. The app saves every five seconds, on Stop, and on copy. Force termination may lose the last few seconds. Clear removes saved entries. The copy operation only writes to the clipboard.

Build in this container

Installed tools:

  • JDK 17 (provided by the user)
  • ~/Android/Sdk/cmdline-tools/19.0
  • Android SDK platform 35, build-tools 35.0.0, and platform-tools
  • ~/.local/opt/gradle-8.11.1
  • Gradle caches in ~/.gradle; debug signing key in ~/.android
source ~/android-env.sh
cd /tzxoesen/codex-chat/pics-android
./gradlew assembleDebug testDebugUnitTest lintDebug

The wrapper checks the downloaded Gradle distribution against its pinned SHA-256. The preinstalled gradle command can also build the project without the wrapper downloading another copy. local.properties points at this container's SDK; on another machine, set ANDROID_HOME or create your own local.properties. No Android Studio, emulator, NDK, or system Kotlin compiler is required.

Build output: app/build/outputs/apk/debug/app-debug.apk. Test report: app/build/reports/tests/testDebugUnitTest/index.html. Lint report: app/build/reports/lint-results-debug.html.

Tests cover payload bounds, signal states, signed coordinates, intersection isolation, stale data, bounded history/logs, demo playback, clipboard export, and foreground lifecycle/log persistence. Android UI tests use Robolectric on API 35; they cannot establish real Pixel Bluetooth reception or OS permission dialog behavior. Those need testing on the physical phone.

Protocol and limitations

The parser is ported from yumu19/ble-pics-viewer, especially web/pics.mjs, under its MIT license (included in LICENSE and APK assets). This follows that repository's interpretation, not an independently verified full PICS specification.

  • Reads manufacturer payload 0x01CE; the company ID is not part of the byte offsets.
  • Type 0: intersection name. Type 1: coordinates. Type 2: up to six signals.
  • State and remaining-value nibble decoding matches upstream. Remaining values are not labeled seconds and no countdown is inferred.
  • Signals are numbered; compass directions are not inferred from slot order.
  • Metadata packets do not refresh the age of a signal reading. Signal age uses the BLE packet's monotonic timestamp and becomes stale after five seconds.
  • Up to 100 intersections are retained. History is the latest five signal packets within 30 seconds per intersection.
  • A recognized manufacturer ID and plausible payload are not authentication.
  • The app is a diagnostic viewer; use the actual street signal for crossing.

The application contains no native libraries, so it does not depend on a particular phone CPU ABI or native-library page size.