Skip to content

Commit fa011b8

Browse files
committed
Fix Android 16 companion startup
1 parent bc26e6e commit fa011b8

15 files changed

Lines changed: 78 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22

33
All notable project changes are documented here.
44

5+
## [0.2.3] - 2026-08-07
6+
7+
Android 16 foreground-service startup hotfix.
8+
9+
### Fixed
10+
11+
- Restored the normal `CHANGE_NETWORK_STATE` permission required by Android as a `connectedDevice` foreground-service prerequisite before USB permission is available.
12+
- Added manifest regression coverage so the companion cannot again ship with a cold-start crash caused by the missing prerequisite.
13+
514
## [0.2.2] - 2026-08-06
615

716
Repository-opening hardening release.
817

918
### Changed
1019

1120
- AOA manufacturer is now `Zhilin` and the accessory URI points to the canonical GitHub repository.
12-
- Removed unused network-state permission, both unused FileProviders, and their path resources.
21+
- Removed both unused FileProviders and their path resources. The network-state permission removed in this release was restored in 0.2.3 after real Android 16 verification proved it is a foreground-service prerequisite.
1322
- Added source-available noncommercial licensing, privacy/security guidance, contribution policy, and dependency update automation.
1423
- Backed up the release keystore and signing properties in a master-password-protected Bitwarden Secure Note through CLI.
1524
- Anonymized legacy project references in OpenSpec and repaired duplicate task numbering without changing acceptance status.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Display Bridge turns Rokid Glasses into a low-latency, video-only Android displa
44

55
> **Independent project:** Display Bridge is not affiliated with, endorsed by, or an official product of Rokid Corporation. Rokid and related product names are trademarks of their respective owners.
66
7-
> **MVP status:** `v0.2.2` is the stable, release-signed build. The core External Display and Screen Mirror paths work on the target Pixel/RG_glasses pair, while several long-run and physical-input acceptance checks remain documented as pending.
7+
> **MVP status:** `v0.2.3` is the current release-signed MVP and supersedes the Android 16 startup-broken `v0.2.2`. The core External Display and Screen Mirror paths work on the target Pixel/RG_glasses pair, while several long-run and physical-input acceptance checks remain documented as pending.
88
99
## Features
1010

apps/android-companion/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ android {
1010
applicationId = "com.azhilin.rokid.displaybridge.companion"
1111
minSdk = 28
1212
targetSdk = 36
13-
versionCode = 4
14-
versionName = "0.2.2"
13+
versionCode = 5
14+
versionName = "0.2.3"
1515
}
1616

1717
buildFeatures {

apps/android-companion/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
44
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
55
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
6+
<!-- Android requires one connected-device prerequisite before USB permission exists. -->
7+
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
68
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
79

810
<application

apps/android-companion/src/test/java/com/azhilin/rokid/displaybridge/companion/CompanionUnitTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,18 @@ class CompanionUnitTest {
147147
assertEquals(bars.bottom, dashboard.root.paddingBottom)
148148
}
149149

150-
@Test fun `bridge package and foreground service declarations are independent`() {
150+
@Test fun `bridge package and foreground service declarations include startup prerequisites`() {
151151
val context = ApplicationProvider.getApplicationContext<android.content.Context>()
152152
assertEquals("com.azhilin.rokid.displaybridge.companion", context.packageName)
153153
assertEquals("Display Bridge", context.getString(R.string.app_name))
154154
@Suppress("DEPRECATION")
155155
val info = context.packageManager.getPackageInfo(context.packageName,
156-
PackageManager.GET_SERVICES)
156+
PackageManager.GET_SERVICES or PackageManager.GET_PERMISSIONS)
157157
val service = requireNotNull(info.services).single { it.name.endsWith("BridgeService") }
158158
assertTrue(service.name.startsWith("com.azhilin.rokid.displaybridge.companion"))
159159
assertTrue(service.foregroundServiceType and ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE != 0)
160160
assertTrue(service.foregroundServiceType and ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION != 0)
161+
assertTrue(requireNotNull(info.requestedPermissions).contains("android.permission.CHANGE_NETWORK_STATE"))
161162
}
162163

163164
@Test fun `AOA identity is stable and application neutral`() {

apps/glasses/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ android {
1010
applicationId = "com.azhilin.rokid.displaybridge.glasses"
1111
minSdk = 28
1212
targetSdk = 28
13-
versionCode = 4
14-
versionName = "0.2.2"
13+
versionCode = 5
14+
versionName = "0.2.3"
1515
}
1616

1717
compileOptions {

docs/release-notes-v0.2.2.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Display Bridge v0.2.2
22

3+
> Superseded by v0.2.3: removing `CHANGE_NETWORK_STATE` caused Android 16 to reject the pre-permission `connectedDevice` foreground-service startup.
4+
35
This release prepares the MVP repository for later public access while preserving the release-signing certificate introduced in v0.2.1.
46

57
## Changes

docs/release-notes-v0.2.3.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Display Bridge v0.2.3
2+
3+
Android 16 foreground-service startup hotfix.
4+
5+
## Fixed
6+
7+
- Restored the normal `CHANGE_NETWORK_STATE` permission used by Android as a prerequisite for starting a `connectedDevice` foreground service before USB device or accessory permission exists.
8+
- Added manifest regression coverage for the prerequisite.
9+
- Verified on Pixel 10 user 0 that the release-signed companion cold-starts without the v0.2.2 fatal `SecurityException` and remains in the foreground.
10+
- Installed and hash-verified the matching release-signed glasses APK on the target RG_glasses device.
11+
12+
The application does not use this permission to change routing, Wi-Fi, Bluetooth, or the user's audio route.
13+
14+
## Artifacts
15+
16+
| Artifact | SHA-256 |
17+
|---|---|
18+
| `display-bridge-companion-v0.2.3-release.apk` | `426E74465CA79651C320A91162B1390BC66742FA23D8CC56E9A4B2B829F712E5` |
19+
| `display-bridge-glasses-v0.2.3-release.apk` | `305D3CC92AC8585A26D715961C68E39F2C2160AA9E19A7F63E67475982750C0A` |
20+
21+
Both APKs use the existing Display Bridge release certificate. Updating from v0.2.2 works in place.

docs/short-term-acceptance-plan.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ Goal: close the 20 remaining hardware/evidence-gated OpenSpec tasks without mixi
66

77
1. Run `adb devices -l`, verify the exact phone owner profile and glasses serial, and record package versions before changes.
88
2. If Android reports a signature mismatch, remove only the two Display Bridge debug packages after explicit approval; never remove unrelated accessory applications.
9-
3. Install both release-signed v0.2.2 APKs together because the AOA manufacturer changed to `Zhilin`.
9+
3. Install both release-signed v0.2.3 APKs together because the AOA manufacturer changed to `Zhilin` and 0.2.3 repairs Android 16 foreground-service startup.
1010
4. Verify package coexistence and that only the Display Bridge glasses app matches the new accessory identity.
1111
5. With glasses ADB unavailable, open the companion, attach USB, verify automatic permission/handshake/External Display startup, then detach and confirm idempotent cleanup plus HOME.
1212
6. Reconnect once and confirm no ambiguous chooser or duplicate concurrent permission request.
1313

1414
Expected closures: `build-display-bridge` 4.11, 6.4, 8.4; `harden-low-latency-live-video` 4.3.
1515

16+
Result (2026-08-07): complete. Both v0.2.3 APKs were installed on the exact devices; automatic accessory dispatch, the two distinct Android USB permission stages, detach-to-HOME, and clean reconnect were verified without removing or disabling unrelated packages.
17+
1618
## Session 2 — Core video modes and switching (45–60 minutes)
1719

1820
1. External Display: use one known presentation-capable independent application, confirm presentation display discovery, non-empty encoded/sent/decoded counters, visible glasses video, phone-side controls, and phone/Bluetooth audio.

docs/verification.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Verification record
22

3-
Release candidate: `v0.2.2` stable MVP
4-
Last updated: 2026-08-06 (Europe/Moscow)
3+
Release candidate: `v0.2.3` Android 16 startup hotfix
4+
Last updated: 2026-08-07 (Europe/Moscow)
55

66
## Automated release gate
77

@@ -27,8 +27,8 @@ Compiler output contains only known Android deprecation warnings for compatibili
2727

2828
| Artifact | Package | Version | SHA-256 |
2929
|---|---|---|---|
30-
| `display-bridge-companion-v0.2.2-release.apk` | `com.azhilin.rokid.displaybridge.companion` | `0.2.2` (`4`) | `BE44DE9152361C248EFDEA52BCFDD0353941F38A08393F04B63960A2F234D1A4` |
31-
| `display-bridge-glasses-v0.2.2-release.apk` | `com.azhilin.rokid.displaybridge.glasses` | `0.2.2` (`4`) | `A4D4B7904F27289A8C4DE529884A0A69EEE91FC76592C6E2B7FFD1F63A531C08` |
30+
| `display-bridge-companion-v0.2.3-release.apk` | `com.azhilin.rokid.displaybridge.companion` | `0.2.3` (`5`) | `426E74465CA79651C320A91162B1390BC66742FA23D8CC56E9A4B2B829F712E5` |
31+
| `display-bridge-glasses-v0.2.3-release.apk` | `com.azhilin.rokid.displaybridge.glasses` | `0.2.3` (`5`) | `305D3CC92AC8585A26D715961C68E39F2C2160AA9E19A7F63E67475982750C0A` |
3232

3333
Both APKs use APK Signature Scheme v2 and the same dedicated release certificate:
3434

@@ -39,15 +39,21 @@ The private key and credentials are kept outside Git. They are not included in r
3939

4040
The PKCS12 keystore and ignored signing-properties file are backed up in the master-password-protected Bitwarden Secure Note `Rokid Display Bridge Release Signing`. Both attachments and their metadata were verified through Bitwarden CLI, the vault was synchronized, and the temporary CLI session file was deleted.
4141

42-
Packaged manifests were also checked to confirm that the unused `CHANGE_NETWORK_STATE` permission and FileProviders are absent. The glasses accessory filter contains manufacturer `Zhilin`, model `Rokid Display Bridge`, and version `1.0`; the companion advertises `https://github.com/azhilin246/RokidDisplayBridge` as its AOA URI.
42+
Packaged manifests were checked to confirm that FileProviders remain absent and that the companion retains `CHANGE_NETWORK_STATE`. Android 16 accepts this normal permission as the connected-device foreground-service prerequisite before USB permission exists; the application does not use it to mutate network routing. The glasses accessory filter contains manufacturer `Zhilin`, model `Rokid Display Bridge`, and version `1.0`; the companion advertises `https://github.com/azhilin246/RokidDisplayBridge` as its AOA URI.
4343

4444
## Device evidence
4545

46-
Development and iterative testing used a Pixel 10 owner profile and RG_glasses with a physical 480×640 display. The user confirmed that the application-neutral External Display MVP renders real content on the glasses. Companion and glasses packages have also been installed and cold-started independently without a matching fatal `AndroidRuntime` failure.
46+
Development and iterative testing used a Pixel 10 owner profile and RG_glasses with a physical 480×640 display. The user confirmed that the application-neutral External Display MVP renders real content on the glasses.
47+
48+
The release-signed v0.2.2 companion reproduced an Android 16 `SecurityException` while starting `BridgeService` as a `connectedDevice` foreground service before USB permission. Release-signed v0.2.3 was installed in Pixel user 0, cold-started with the same companion-visibility intent, and remained alive as a foreground service with type `0x10`; the cleared crash buffer remained empty. The matching v0.2.3 glasses APK was then installed in place on exact serial `1901092553801899`. Package Manager reported version `0.2.3` (`5`) and release signature `[ad9a3b73]`; the pulled installed base APK matched the release artifact at SHA-256 `305D3CC92AC8585A26D715961C68E39F2C2160AA9E19A7F63E67475982750C0A`.
49+
50+
Session 1 then proved system accessory dispatch and detach behavior without glasses ADB. Android requested permission once for the normal `18d1:4ee7` device (`DEVICE_TO_AOA`) and once for the re-enumerated `18d1:2d01` device (`AOA_DATA`), with distinct device ids and no concurrent duplicate request per stage. AOA/DBP1 opened, External Display started automatically, and decoder feedback returned with a zero sender queue. The user confirmed that removing the active cable closed Display Bridge on the glasses and returned to HOME. USB host history and a fresh session showed automatic reconnect, a recreated 480×640 `PRESENTATION | OWN_CONTENT_ONLY` External Display, and resumed decoder feedback without a stale-session or fatal error.
51+
52+
Session 1 is complete. Both release-signed v0.2.3 APKs are installed on their exact target devices, system accessory dispatch opens only Display Bridge without an ambiguous chooser, and no unrelated package was uninstalled or disabled during install, detach, or reconnect verification. The two visible USB permission dialogs belong to the normal-device and re-enumerated AOA-device stages rather than duplicate requests for one Android `UsbDevice`.
4753

4854
This evidence is intentionally summarized without publishing personal ADB serials, process IDs, local paths, or unrelated installed package names.
4955

50-
The `v0.2.2` release certificate was verified automatically, but the release-signed APKs were not installed over the existing debug builds because Android requires a one-time uninstall when changing signing certificates.
56+
Changing from the old debug certificate required a one-time removal of only the Display Bridge packages. Subsequent v0.2.2 to v0.2.3 companion update succeeded in place with the dedicated release certificate.
5157

5258
## Pending acceptance
5359

0 commit comments

Comments
 (0)