- Java 100%
| app | ||
| gradle/wrapper | ||
| .gitattributes | ||
| .gitignore | ||
| build.gradle | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| README.md | ||
| settings.gradle | ||
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.
- Tap Demo at home. It feeds synthetic packets through the real parser and dashboard, cycling red / green / flashing green. No permissions are needed.
- Tap Stop, then Scan BLE. Grant Nearby devices and precise location while using the app. Enable Bluetooth if prompted, then tap Scan BLE again.
- 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.
- 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.