Skip to content
View in the app

A better way to browse. Learn more.

Firmware, Software & Manuals for BYD Owners

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

DashCast — BYD Cluster Launcher & Mirror

DashCast — BYD Cluster Launcher & Mirror

Android application for BYD vehicles with DiLink 3.0 (Android 10) to push any installed app onto the instrument cluster display, control it via a real-time touch mirror, and diagnose BYD APIs.

изображение.png

Links:

Apk's:

Version

Link

v1.3.25-beta

Details

v1.3.24-beta

Details

v1.3.23-beta

Details

v1.3.22-beta

Details

v1.3.21-beta

Details

v1.3.20-beta

Details

v1.3.19-beta

Details

v1.3.18-beta

Details

v1.3.17-beta

Details

v1.3.16-beta

Details

v1.3.15-beta

Details

v1.3.14-beta

Details

v1.3.13-beta

Details

v1.3.12-beta

Details

v1.3.11-beta

Details

v1.3.10-beta

Details

v1.3.9-beta

Details

v1.3.8-beta

Details

v1.3.7-beta

Details

v1.3.6-beta

Details

v1.3.5-beta

Details

v1.3.4

Details

v1.3.3

Details

v1.3.3-beta

Details

v1.3.2

Details

v1.3.1

Details

v1.3.0

Details

v1.2.82-beta

Details

v1.2.81-beta

Details

v1.2.80-beta

Details

v1.2.79-beta

Details

v1.2.78-beta

Details

v1.2.77-beta

Details

v1.2.76-beta

Details

v1.2.75-beta

Details

v1.2.74-beta

Details

v1.2.73-beta

Details

v1.2.72-beta

Details

v1.2.71-beta

Details

v1.2.70-beta

Details

v1.2.69-beta

Details

v1.2.68-beta

Details

v1.2.67-beta

Details

v1.2.66-beta

Details

v1.2.65-beta

Details

v1.2.64-beta

Details

v1.2.63-beta

Details

v1.2.62-beta

Details

v1.2.61-beta

Details

v1.2.60-beta

Details

v1.2.58-beta-phase-A-step1

Details

v1.2.57-beta

Details

v1.2.56-beta

Details

v1.2.55-beta

Details

v1.2.54-beta

Details

v1.2.51-beta

Details

v1.2.49-beta

Details

v1.2.48-beta

Details

v1.2.47-beta

Details

v1.2.46-beta

Details

v1.2.45-beta

Details

v1.2.44-beta

Details

v1.2.43-beta

Details

v1.2.42-beta

Details

v1.2.40-beta

Details

v1.2.39-beta

Details

v1.2.38-beta

Details

v1.2.37-beta

Details

v1.2.36-beta

Details

v1.2.35-beta

Details

v1.2.34-beta

Details

v1.2.33-beta

Details

v1.2.32-beta

Details

v1.2.31-beta

Details

v1.2.28

Details

v1.2.27

Details

v1.2.26

Details

v1.2.25

Details

v1.2.24

Details

v1.2.23

Details

v1.2.22

Details

v1.2.21

Details

v1.2.20

Details

v1.2.19

Details

v1.2.18

Details

v1.2.17

Details

v1.2.16

Details

v1.2.15

Details

v1.2.14

Details

v1.2.13

Details

v1.2.12

Details

v1.2.11

Details

v1.2.10

Details

v1.2.9

Details

v1.2.8

Details

v1.2.7

Details

v1.2.6

Details

v1.2.5

Details

v1.2.4

Details

v1.2.3

Details

v1.2.2

Details

v1.2.1

Details

v1.2.0-build186

Details

v1.2.0-build185

Details

v1.2.0-build184

Details

v1.2.0-build183

Details

v1.2.0-build182

Details

v1.1.9-build180

Details

v1.1.9-build179

Details

v1.1.9-build178

Details

v1.1.9-build177

Details

v1.1.9-build176

Details

v1.1.9-build175

Details

v1.1.9-build174

Details

v1.1.9-build173

Details

v1.1.9-build172

Details

v1.1.9-build171

Details

v1.1.9-build170

Details

v1.1.9-build169

Details

v1.1.8

Details

v1.1.7

Details

v1.1.6

Details

v1.1.5

Details

v1.1.4

Details

v1.1.3

Details

v1.1.2

Details

v1.1.1

Details

v1.1.0-beta1

Details

v1.0.1

Details

v1.0.0

Details

v0.9.94

Details

v0.9.0

Details

v0.8.7

Details

v0.8.6

Details

v0.8.5

Details

v0.8.2

Details

v0.8.1

Details

v0.8.0

Details

v0.6.8-beta

Details

0.5.1

Details

v0.5.0-beta

Details

v0.2.1

Details

v0.2.0-beta

Details

  • Replies 143
  • Views 416
  • Created
  • Last Reply

Top Posters In This Topic

Posted Images

Featured Replies

  • Author

DashCast v1.2.37-beta


v1.2.37-beta — DL5 Cluster Recon tab + Telegram one-tap export

A targeted release for the DiLink 5 tester whose Yandex Maps cluster resize broke
after v1.2.35 reverted force_resizable_activities. This release ships only
the recon tooling
needed to validate the next fix in production conditions
before wiring any live cluster operation.

Why a recon-only release

The DL5 resize regression has a clear suspected fix — the AOSP per-app compat
override am compat enable FORCE_RESIZE_APP <pkg> (change ID 174042936) —
but we need empirical confirmation that the compat framework shell surface is
actually exposed on production BYD ROMs before putting any new live code
path in front of users. To do that without burning hours of in-car tests, the
tester now has a dedicated diag tab that runs 30 non-destructive probes and
ships the full report back via Telegram in a single tap.

New "Cluster DL5" diagnostic tab

  • Renamed: the existing "Cluster" tab is now "Cluster DL3" (purely cosmetic,
    the underlying panel panel_cluster_poc is unchanged).
  • New tab "Cluster DL5" at the end of the tab strip (index 11), with a
    Material 3 card UI: header (ic_science icon + title + subtitle), platform
    pill, live counters, three action buttons (Run recon / Copy report / Send
    via Telegram), and a programmatic test list.
  • No live cluster operation in this release — every test is read-only.

30 recon tests

  • R01–R05 Platform identity: Build.MODEL, Build.PRODUCT,
    Build.FINGERPRINT, DisplayManager inventory (cluster auto-detection by name
    match on fission|xdja|cluster|virtual), ADB local uid probe, SELinux
    enforce mode, cluster apps installed scan (Yandex Maps, Google Maps, Waze,
    Sygic, TomTom).
  • R06–R12 Shell verb surface: full --help capture of am, am start,
    cmd activity, cmd activity task, wm, cmd window, cmd display — so
    we know exactly which --display, --windowingMode, resize,
    set-ignore-orientation-request flags are honoured on this ROM.
  • R13–R18 Compat framework — the critical block. am compat --help,
    am compat list first 60 entries, then targeted am compat get against
    FORCE_RESIZE_APP, 174042936 (numeric), NEVER_SANDBOX_DISPLAY_APIS,
    OVERRIDE_MIN_ASPECT_RATIO, all scoped to our own package.
  • R19–R23 Settings + dumpsys snapshots: force_resizable_activities current
    value, settings list global filtered on resizable|freeform|multi|sandbox|ignore_orientation,
    dumpsys window/display/activity keyword filters.
  • R24–R25 wm size/wm density on default display and on the detected
    cluster display.
  • R26–R28 Per-app manifest probe via pm dump <pkg> | grep -E 'resizeable|maxAspectRatio|launchMode|targetSdk' for Yandex Maps, Google
    Maps, Waze (auto-SKIP when not installed).
  • R29 Service list filter for auto_container|cluster|fission|xdja|window|activity_task.
  • R30 getprop filter for resizable|freeform|multi|sandbox|cluster|dilink|fission|xdja.

Total run: < 20 s typical (single-threaded executor, 6 s per-probe timeout).

Telegram one-tap export

New helper AppLogger.shareReportToTelegram(Context, String):

  1. Probes 5 Telegram package names in order (org.telegram.messenger,
    org.telegram.messenger.web, org.telegram.plus, nekox.messenger,
    org.thunderdog.challegram).
  2. On hit, fires a direct ACTION_SEND with setPackage(hit) + FileProvider URI
    on a byd_report_*.log containing the recon report and the in-memory
    AppLogger ringbuffer (so we get both the structured probe results and the
    raw lifecycle log).
  3. Three-level fallback: TG-found → direct intent; no-TG → generic share
    chooser; no-file → text-only chooser.

The new "Send via Telegram" button in the Cluster DL5 panel wires straight to
this helper — no manual file-picking, no submenu hunting.

Next step (v1.2.38)

Once the tester runs the recon and ships the report, we will:

  1. Confirm am compat list contains FORCE_RESIZE_APP (or 174042936)
    accessible from the app uid 2000 shell.
  2. Wire the per-app fix into ClusterService.startActivityViaShell (chain
    am compat enable 174042936 <pkg> ; am force-stop <pkg> ; am start --display N --windowingMode 5 ...).
  3. Remove the now-unneeded global force_resizable_activities toggle that
    v1.2.35 reverted, since the per-app override has no system-wide side effect.

Touched files

  • app/build.gradle — 294 → 295, 1.2.36-beta → 1.2.37-beta.
  • New dilink5/Dl5ClusterReconRunner.java (~380 LoC, reuses
    DiLink5TestRunner.TestDef/TestResult/Status/Listener types for row
    rendering reuse).
  • New res/layout/include_diag_cluster_dl5.xml (~140 LoC, Material 3 card +
    action row + programmatic test list).
  • AppLogger.java — added shareReportToTelegram (~65 LoC).
  • DiagActivity.java — new TAB_CLUSTER_DL5 = 11, new fields, new methods
    bindClusterDl5Panel / prepareClusterDl5TestRows / runClusterDl5AllTests
    / updateClusterDl5Counters / copyClusterDl5Report /
    sendClusterDl5ToTelegram (~110 LoC). Row binder reused from existing
    bindDl5Row.
  • res/layout/activity_diag.xml — new tab + new include.
  • res/values/strings.xml + res/values-en/strings.xml — 1 renamed
    (diag_tab_cluster ClusterCluster DL3) + 15 new keys.

Compatibility

  • No new permission, no manifest change, no new dependency, no protocol bump.
  • Zero behavioural regression on DL2 / DL3 / DL4 / DL5 production paths —
    the new tab is purely diagnostic; the rename Cluster → Cluster DL3 is
    cosmetic and does not affect any code path.

Channel

Pre-release on beta/1.2.0-dilink5. Not merged to main. Latest stable
remains v1.0.1 on GitHub.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.38-beta


v1.2.38-beta — UpdateChecker loop fix + Recon-v2

Pre-release. Branch: beta/1.2.0-dilink5. Two field-reported bug fixes.

🐛 Bug A — UpdateChecker infinite-prompt loop on v1.2.37-beta

Symptom (reported by multiple users): v1.2.37-beta installs correctly,
but on every next launch the OTA banner re-proposes installing
v1.2.37-beta itself. Tap "Install" → install completes → next launch →
banner again. Infinite loop.

Root cause in UpdateChecker.isNewer(latest, currentName, currentCode):
the helper stripped the -beta / -buildN suffix from latest before
calling parseVer(...), but NOT from currentName. So
parseVer("1.2.37-beta")split(".")["1","2","37-beta"]
Integer.parseInt("37-beta") throws → segment silently stays 0 →
current = [1,2,0] vs latest = [1,2,37] → loop reports 37 > 0 = true
→ "update available" forever.

Fix (1 line): apply stripSuffix(...) to currentName too, mirroring
exactly what's done for latest. Also tightened the equality fall-through
to explicitly return false when both base versions match and no -buildN
suffix is available.

Impact: every v1.2.37-beta user, on first launch with v1.2.38-beta,
sees one final correct update prompt → installs → never re-prompted for
the same version again. v1.2.36-beta and earlier are unaffected.

🛠 Bug B — Recon-v2 (8 fixes in the DL5 Cluster Recon tab)

Field log log/byd_report_20260525_105105.log from the DL5 tester
(Yandex Maps on cluster) running v1.2.37-beta recon-v1 revealed 8 bugs
in Dl5ClusterReconRunner.java. All fixed:

# Test Bug Fix
1 R01 Platform.describeMode(null) NPE pass Context through
2 R02 last-match overwrite picks id=4 break after first match
3 R02 + R25 hidden Display Id=2 not enumerated dumpsys-based fallback enumerates every Display Id=N the platform sees, including hidden ones
4 R03 required token "uid=2000" doesn't match id -u output "2000" required tokens changed to "2000" + "shell"
5 R13 am compat --help rejected on DL5 (no --help verb) now am compat enable 2>&1 | head -40 (triggers usage hint)
6 R14 am compat list needs a package argument now am compat list <ourPackage>
7 R15–R18 am compat get is NOT a valid AOSP verb on DL5 (rejected with Invalid toggle value: 'get') R15/R16: am compat list <yandexmaps/waze>; R17: am compat reset 174042936 <ourPackage> (idempotent); R18: dumpsys platform_compat | grep cross-check
8 R23 grep matches mGlobalConfig noise on every Configuration grep -v mGlobalConfig pre-filter + anchored task-stanza grep
9 R26–R28 grep matches <supports-screens> resizeable instead of activity-level resizeableActivity grep narrowed to resizeMode|resizeableActivity|maxAspectRatio|launchMode|targetSdk

Key discovery (catalog now documents it): the DL5 platform_compat
shell exposes ONLY enable | disable | reset | reset-all | list (no
get verb). Read-back is done via am compat list <pkg>.

Why hidden Display Id=2 matters: DL5 hides the physical cluster
display from DisplayManager.getDisplays() for non-system uids. Only
the containerservice-owned virtual proxies shared_fission_bg_XDJAScreenProjection_0/1
(ids 3 and 4) are exposed. The cluster framebuffer itself is id=2 and is
only visible via dumpsys headers. R02 now reports both views; R25
geometry probes every id, not just heuristic name matches.

⚠ Not in this release (deferred to v1.2.39+)

  • Live FORCE_RESIZE_APP wire-up — still gated on a clean recon-v2 run
    from at least one DL5 user confirming the change ID is enablable.
  • Isolated live-test panel (Enable / Test launch / Reset buttons).

Build

  • versionCode 296 / versionName 1.2.38-beta
  • Branch: beta/1.2.0-dilink5
  • No new permission, no manifest change, no new dependency, no protocol bump.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.39-beta


v1.2.39-beta — Hotspot icon visible by default on DL3 + no auto-projection on app launch

Pre-release. Two field-reported UX fixes.

🐛 Fix A — Hotspot icon missing on DL3 navrail

A DL3 user reported the Hotspot icon never appeared in the left navigation rail.

Root cause: refreshNavHotspot() read getBoolean(PREF_USE_OWN_SIM, false)
— the false default meant new DL3 users had to discover and tick the
"I use my own SIM card" toggle in Settings before the icon would show up.

Fix: default flipped to true. On DL3 the Hotspot navrail entry now
appears immediately on first launch. Users without a personal SIM can
still hide it via the same Settings toggle (checkbox + support text
unchanged).

⚙ Fix B — No more auto-projection on app launch

User request (verbatim): « Je ne souhaite pas que la projection s'active
automatiquement quand on lance l'application. Vu qu'on lance la projection
automatiquement quand on lance une application c'est suffisant. Il faut
juste s'assurer que le slow path et le fast path soit bien gardé. »

Since v1.2.77 (a long-standing behaviour), opening DashCast unconditionally
called activateCluster() in onStart(). This woke the cluster surface
even when the user only opened the app to browse Settings, view SysInfo,
or check for OTA updates.

Fix: the else { activateCluster(); } branch in MainActivity.onStart()
is removed. Both projection auto-start paths in the actual app-launch flow
are preserved untouched:

  • Slow pathonSendToDashboard()mClusterService == null
    mPendingAppAfterActivation = app ; activateCluster() → service-connected
    callback replays the tapped app once displayId > 0.
  • Fast pathonSendToDashboard() → service already bound,
    displayId > 0 → direct moveTaskToDisplay() / launchOnDashboard().

So tapping any app in the grid still kicks projection automatically
(exactly like before). The only thing removed is the gratuitous cluster
wake-up when DashCast is opened without launching an app.

The if (ClusterService.sIsRunning) branch is preserved: an already-running
service still gets re-bound when the Activity returns to foreground.

Behavioural contract

  • Cold open (no projection) → land on app grid, status pill "OFF", zero
    shell calls fired. Like opening any launcher.
  • Tap any app in the grid → projection auto-activates (slow path), app
    replayed once cluster is up.
  • Open DashCast while projection is already running → bind and re-attach
    listener. No regression.

Build

  • versionCode 297 / versionName 1.2.39-beta
  • Branch: beta/1.2.0-dilink5
  • Touched: app/build.gradle, MainActivity.java (2 small edits).
  • No new permission, no string change, no layout change, no dependency, no protocol bump.
  • Zero behavioural regression on DL2/DL4/DL5 — Hotspot gate is still
    platform-gated on isDiLink3 and the auto-activation removal applies
    uniformly per the user's clarification.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.40-beta


v1.2.40-beta — Critical recon bug fix: R17 was killing DashCast mid-suite

Pre-release. One critical fix.

🐛 The bug

DL5 tester running v1.2.39-beta recon-v2 reported:

« À un moment l'écran flash et ça sort de DashCast pour revenir.
Du coup le résultat est perdu et la personne ne peut pas exporter le log. »

🔍 Root cause

R17 was implemented in v1.2.38-beta as:

am compat reset 174042936 com.byd.dashcast 2>&1

intended as an idempotent no-op probe (no override set → reset is a no-op).

The flaw: on Android 11+, the platform_compat service forces a full
process restart of the target package on every compat-override mutation,
including a reset that resolves to a no-op. This is by design — compat
changes are evaluated at Application.onCreate, so the platform recycles
the process to guarantee the new value takes effect on next launch.

Net effect on DL5: R17 called am compat reset against our own package
→ platform killed DashCast → DiagActivity recreated → in-memory recon
results wiped → tester left with nothing to copy or send via Telegram.

✅ Fix

R17 is now a fully read-only probe:

dumpsys platform_compat 2>&1 | grep -B1 -A1 '174042936|FORCE_RESIZE_APP' | head -30

Surfaces the same registered metadata of change ID 174042936 (registered?
overridable? default state? min sdk?) without touching the override state
of any package → zero process kill, zero side effect.

🔒 Audit

No other test in the catalog mutates state:

  • R13 (am compat enable no args) → usage hint only
  • R14–R16 (am compat list <pkg>) → read-back
  • R18 (dumpsys platform_compat | grep) → read-only
  • R01–R12, R19–R30 → pure dumpsys / settings get / pm dump / getprop / etc.

So R17 was the ONLY suite-killing test. After this fix the suite runs
end-to-end every time and the report can be copied / shared via Telegram
normally.

Build

  • versionCode 298 / versionName 1.2.40-beta
  • Branch: beta/1.2.0-dilink5
  • Touched: app/build.gradle, Dl5ClusterReconRunner.java (R17 dispatcher + catalog description).
  • No new permission, no string change, no layout change, no dependency, no protocol bump.
  • Zero regression on any other platform — recon tab is DL5-only and the change is a pure command swap in a single test case.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.42-beta


DashCast v1.2.42-beta

New Diagnostic tab — Export APK BYD

List every installed BYD / XDJA / DiLink / cluster package and ship its APK to Telegram in two taps, with auto-cleanup. No USB cable, no host machine needed for grabbing a BYD APK off the ROM.

UI

  • New Export APK BYD tab in Diagnostic (after Cluster DL5)
  • Per-APK row mirroring the Journal look: colored bar + monospace package name + v<name> (<code>) + size + APK path + Exporter button
  • Header card with counters (« X APK détectés • Y MB sur disque ») and Rafraîchir button
  • Hint spelling out the cache-share-auto-delete contract

Pipeline

  1. Scan: PackageManager.getInstalledPackages(0) filtered on com.byd., com.xdja., com.dilink., plus tokens cluster | automotive | fission | projection | dilink
  2. Copy: reads applicationInfo.sourceDir (every /data/app/.../base.apk is mode 0644, no ADB shell needed)
  3. Share: ACTION_SEND with MIME application/vnd.android.package-archive via FileProvider, targeted on Telegram (5 package variants probed) with a chooser fallback
  4. Auto-cleanup — 3 layers, the car never keeps an APK long-term:
    • Cache wiped on onCreate (orphans from previous session)
    • Cache wiped on Rafraîchir
    • Handler.postDelayed(... 5 min) deletion after each share, with removeCallbacksAndMessages(null) on onDestroy

i18n

15 new keys × 12 locales = 180 entries. French (default) + English + ar, be, de, es, it, kk, ru, tr, uk, uz. Hand-curated translations, technical identifiers preserved, %1$s/%2$d placeholders strict.

Also shipped — v1.2.41-beta content (unreleased before)

  • DL5 Cluster recon cleanup — R14/R15/R16 rewritten with proper dumpsys platform_compat syntax (the old am compat list <pkg> was interpreted as a change ID and crashed). R17 grep switched from BRE \| to BusyBox-compatible ERE.
  • New Test Fission live battery (F01–F12, DL5 only) targeting Yandex Maps. Enables FORCE_RESIZE_APP (change ID 174042936) per-package via am compat enable, launches on the fission display with am start --display N --windowingMode 5 ..., resizes via cmd activity task resize, moves back to main display via the safe MAIN/LAUNCHER intent (F09/F10), then cleans up (am force-stop + am compat reset). DL5-only enforced; F-tier hidden on DL2/DL3/DL4. Display 0 policy: no resize / no size-mutation command ever, but moving an app TO display 0 via the launcher intent is allowed.
  • Full i18n for the Fission UI strings: 6 keys × 12 locales.

Files

  • DashCast-v1.2.42-beta-debug.apk (15 MB)

Install

adb install -r DashCast-v1.2.42-beta-debug.apk

Open Diagnostic → Export APK BYD to try the new tab.

Status

Pre-release. Latest stable remains v1.0.1.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.43-beta


v1.2.43-beta — Voice (alpha) audio-chain PoC

Diagnostic → new « Voice (alpha) » tab. Step 1/3 of the cross-DiLink voice-control roadmap. This release ships an audio-chain validator only — no ML, no wake word, no ASR. The goal is to prove that the microphone reaches the app on DL3/DL4/DL5 before adding the ML dependencies in later releases.

Roadmap (3 steps)

Version Adds APK delta
v1.2.43 Audio chain validator (AudioRecord 16 kHz mono + RMS/peak/clip + VU-meter) +~40 KB
v1.2.44 Wake word « Ok DashCast » via openWakeWord (ONNX Runtime Mobile, FOSS Apache-2.0) +~5 MB
v1.2.45 Vosk multilingual ASR with on-demand language packs + intent router +~50 MB/lang

Why this stack and not Porcupine

Porcupine requires a per-user AccessKey, periodic online validation, is limited to ~3 monthly-active-users on the free tier and ships as a closed binary blob — unsuitable for a freely-distributed offline APK. openWakeWord + Vosk + ONNX Runtime Mobile are 100% FOSS (Apache-2.0), 100% offline, no key, no telemetry, no Google Play Services required.

What's in this release

  • New « Voice (alpha) » tab in Diagnostic (index 13, after Export APK BYD)
  • Foreground service com.byd.dashcast.voice.VoiceService (channel dashcast_voice_poc, type microphone)
  • AudioRecord at 16 kHz mono 16-bit, source VOICE_RECOGNITION, frames of 800 samples (≈50 ms)
  • Live VU-meter (RMS + instantaneous peak, both 0..32767), stats line RMS X • pic Y • clip Z • frames N, run-time chrono mm:ss
  • RECORD_AUDIO permission requested at runtime, denied → toast + button reverts
  • IPC: LocalBroadcastManager (in-process only, no extra system permission)
  • i18n 100% — 19 new keys × 12 locales = 228 entries (fr/en hand-shipped, ar/be/de/es/it/kk/ru/tr/uk/uz hand-curated)

Isolation contract

All voice code lives under com.byd.dashcast.voice.* with zero imports from production code (only outside imports = AppLogger and R). Production code is zero-touchDiagActivity only receives a new tab in append, AndroidManifest only receives a new permission + service, activity_diag.xml only receives a TabItem + <include>. Killing or crashing the voice service has zero impact on the rest of the app.

Privacy

Nothing is recorded, transmitted or analysed. The audio frames are read, RMS/peak/clip are computed locally in a pure Java loop, broadcast to the UI, and dropped — no buffer is ever persisted to disk, no network call is ever made.

Install

This is a pre-release. Latest stays at v1.0.1. Install the attached APK over v1.2.42-beta to upgrade — versionCode 301 makes Android accept the update without uninstall.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.44-beta


v1.2.44-beta — Hotspot UX consistency + clients fix

Four bugs fixed, all reported from the field.

Bug 1 — Navrail inconsistency

The « Hotspot » entry only appeared on the Main activity navrail. Entering Diag, Sysinfo, Log or Settings made it disappear, breaking the user's mental model of « the navrail is always the same set of icons ».

Fix. New helper com.byd.dashcast.NavRailHotspot.apply(activity, viewId, finishOnNav) that replicates the historical gating logic (DL3 + use_own_sim pref, default true since v1.2.38). Each of the 4 secondary layouts gets a new LinearLayout wrapper (nav_hotspot_diag / _log / _sys / _set, visibility="gone" by default, flipped to VISIBLE by the helper when gating passes), and each of the 4 activities calls the helper from its existing wire<Screen>NavRail() method — one line of glue per screen.

Bug 2 — Hotspot activity has no navrail at all

Entering Hotspot dropped the user into a screen with only a back button, forcing finish-and-relaunch to reach any other screen.

Fix. activity_hotspot.xml root rewrapped from a single NestedScrollView to a horizontal LinearLayout with (a) a full navrail on the left (Apps/Settings/Diag/Sysinfo/Log clickable + Hotspot marked active with bg_nav_pill_active + primary tint), and (b) the original 2-col content wrapped in a NestedScrollView with weight=1. New wireHotspotNavRail() method in HotspotActivity wires the 5 other items.

Bug 3 — Uptime counter not refreshed in real time

The session uptime was only updated when statsTick fired (every 5 s), so the counter visibly jumped 5 seconds at a time.

Fix. New independent ticker uptimeTick posted every 1 s, updates only tv_stat_uptime from the locally-tracked upStartElapsed. No ADB call, no dumpsys, no TrafficStats read — pure SystemClock.elapsedRealtime() subtraction. Started in onResume, removed in onPause. The 5 s statsTick is unchanged and still drives Rx/Tx + clients + the session lifecycle.

Bug 4 — Connected clients not shown

The previous parseClients relied exclusively on dumpsys wifip2p. On the BYD ROM this section is empty (or formatted differently) even when devices are connected, so the user saw « 0 clients » with a phone clearly visible in TetherFi's UI.

Fix. The enumeration now combines three independent data sources in a single ADB roundtrip and takes the union of MACs discovered:

  1. dumpsys wifip2p — kept for friendly-name harvest (primary).
  2. /proc/net/arp — every entry on a p2p-* / p2p* / ap* / softap* / *-ap interface with Flags != 0x0 is treated as authoritative.
  3. ip neigh show — same gating, for kernels that surface neighbours only via the ip tool. Lines containing FAILED / INCOMPLETE are dropped.

The three sources merge into a LinkedHashMap<mac, HClient>, so dumpsys-derived friendly names win when available and the ARP/neigh fallbacks fill in the rest.

Touched files

  • app/build.gradle (301→302)
  • New: NavRailHotspot.java
  • HotspotActivity.java (+nav rail wiring, +1Hz uptime ticker, rewritten client enumeration)
  • DiagActivity.java / LogActivity.java / SysInfoActivity.java / SettingsActivity.java (+1 line each)
  • activity_diag.xml / activity_log.xml / activity_sysinfo.xml / activity_settings.xml (+1 hotspot wrapper each)
  • activity_hotspot.xml (root rewrapped + navrail prepended)

No new dependency, no new permission, no new string, no manifest change.

Compatibility

versionCode 302 — Android accepts the update without uninstall over v1.2.43. Pre-release : « Latest » stays at v1.0.1.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.45-beta


v1.2.45-beta — Compact apps panel mode

Main — new "Mode compact (liste apps étroite)" Settings toggle. Field request: free the left apps column down to a 2-icon-wide grid so the cluster preview pane on the right gets significantly more room (both width and height). Made into a Settings switch so each driver picks the layout they prefer.

What changed

  • New Settings toggle in Affichage card: "Mode compact (liste apps étroite)" — PREF_COMPACT_APPS_PANEL, default OFF (zero regression, historical 5-col layout preserved).
  • When ON:
    • Left apps column layout_weight flips from 1.40.6
    • rv_apps GridLayoutManager spanCount flips from 52 (in grid mode)
    • Filter chips row (ll_category_filters) is force-hidden (no longer fits in a 2-icon column)
  • When OFF: full historical layout restored, chips visibility honoured per their own pref.
  • Live apply: switch takes effect immediately on return from Settings (no activity restart, called from MainActivity.onResume).
  • List ↔ Grid toggle in the search bar still honours compact spanCount on re-creation.

i18n

2 new strings × 12 locales = 24 entries (settings_compact_apps_panel, settings_compact_apps_panel_support). All locales hand-curated, zero MissingTranslation lint regression.

Files touched

  • app/build.gradle (302 → 303, 1.2.44-beta → 1.2.45-beta)
  • SettingsActivity.java (+pref constant, +field, +bind/load/listener)
  • activity_settings.xml (+1 toggle row in Display card)
  • MainActivity.java (+applyCompactAppsPanelMode() ~50 LoC, +onResume call, toggleViewMode honours spanCount)
  • res/values/strings.xml + 11 other locales (+2 keys each)

No new permission, no manifest change, no new dependency, no new drawable (reuses ic_aspect_ratio).

Zero regression contract

The pref defaults to false, so any user upgrading without touching the new toggle sees the exact same layout as v1.2.44.

Install

Side-load DashCast-v1.2.45-beta-debug.apk over any previous beta (versionCode 303 ≥ all earlier betas; signed with the same BYD platform keystore).

Branch: beta/1.2.0-dilink5 (not merged to main).

Links

APK download

GitHub release page

  • Author

DashCast v1.2.46-beta


v1.2.46-beta — Diag cold-start perf : lazy test rows

Field report: the Diagnostic page felt slow to open.

Root cause

DiagActivity.onCreate() was eagerly inflating the test-row catalogs of all 6 diag panels:

Panel Rows
Beta Engine 28
DiLink 5 31
DL5 Cluster recon 42
DiLink 2 40
DiLink 4 40
Mirror 10
Total 191

That's 191 row inflations × ~6 findViewById per row on the UI thread, even though only one panel is ever visible at a time. On DL3/DL5 that's ~400–800 ms of bound UI-thread work before the first frame can be drawn. 5 panels out of 6 are pure waste — the typical user opens Diag, looks at one tab, and goes back.

Fix

Each prepare<X>TestRows() is now wrapped behind a lightweight prepare<X>TestRowsIfNeeded() guard (one boolean flag per panel) and the call moves from onCreate into showPanelForTab(position) — so the catalog is only inflated the first time that panel actually becomes visible. Once prepared, the guard is a no-op, subsequent tab switches stay free.

Same pattern already used (since v1.2.42 / v1.2.43) for refreshExportApkListIfNeeded() and onVoicePanelEntered().

Files touched

  • app/build.gradle (303 → 304, 1.2.45-beta → 1.2.46-beta)
  • DiagActivity.java (+6 boolean flags, +6 IfNeeded wrappers, removed 6 eager calls from onCreate, added 6 lazy calls in showPanelForTab)

No new permission, no manifest change, no new dependency, no new string, no new layout.

Zero regression contract

Same rows, same bindings, same Run-All / Copy / Telegram pipelines — only the inflation moment changes. The default tab logic (DL2 → DL4 → DL5 → Beta fallback) is unchanged; whichever tab opens first, its catalog is prepared synchronously inside showPanelForTab before the panel becomes visible.

Install

Side-load DashCast-v1.2.46-beta-debug.apk over any previous beta (versionCode 304 ≥ all earlier betas).

Branch: beta/1.2.0-dilink5 (not merged to main).

Links

APK download

GitHub release page

  • Author

DashCast v1.2.47-beta


DL5 Fission runner fixes after first field run (log 20260525_142241).

Why

Field test scored 10 PASS / 2 WARN / 0 FAIL but two PASS were false positives — see CHANGELOG.

Fixes (runner-only, zero UI change)

  • FissionState gains targetActivity (resolved ComponentName).
  • F02 resolves launcher activity via PackageManager.getLaunchIntentForPackage(); aborts F03..F10 if none.
  • F05/F09 use 'am start ... -n <pkg/.Activity>' and treat 'unable to resolve' as FAIL/WARN regardless of exit code.
  • F06/F10 use awk to extract the per-task displayId (no more false positives from substring sweeps).
  • F08 scopes mBounds=Rect extraction to the Yandex task block, requires both '(100, 80' and '1820' on the same line.

Branch: beta/1.2.0-dilink5. Latest stays v1.0.1.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.48-beta


DL5 Fission runner — interactive multi-display probe (v1.2.48-beta)

After the second field test (log/byd_report_20260525_144058.log, 9 PASS / 2 WARN / 1 FAIL) it became clear that dumpsys-only heuristics will keep producing false negatives on this 4-display DL5 ROM. v1.2.48 pivots to an interactive F-tier that asks the testeur, in their locale, on each candidate display in turn, whether Yandex Maps is actually visible on the cluster screen.

New F-tier flow (16 steps)

  • F01 — opens the BYD cluster projection itself (service call auto_container 2 i32 1000 i32 16 s16 "") so fission displays become routable. Sets a flag so F16 only tears down if F01 opened it.
  • F02 / F03 — Yandex installed + launcher resolved / enable FORCE_RESIZE_APP compat override (unchanged from v1.2.47).
  • F04 / F06 / F08 — force-stop Yandex then am start --display N --windowingMode 5 -n <component> for N in {2, 3, 4}. SKIPs once a previous candidate has been confirmed.
  • F05 / F07 / F09NEW localized Yes/No popup asking « Yandex Maps est-il visible sur l'écran cluster ? (test sur le display N) ». Runner blocks on a CountDownLatch (180 s safety timeout) while the AlertDialog is shown via the new Listener.onPromptYesNo callback. YES → records this display as the cluster, extracts the Yandex taskId via a properly-scoped awk pass (walks the dump, tracks current displayId=N block, only matches (mPreferredTopFocusableRootTask|mLastFocusedRootTask)=Task{…yandexmaps} inside that block — the awk fix the v1.2.47 false-negative diagnostic was missing), skips remaining candidates. NO → moves Yandex back to display 0 + force-stops it, so the next candidate launches from a clean slate. TIMEOUT → WARN. Every step logs prompt title / message / user answer in the report.
  • F10NEW cmd activity set-task-windowing-mode <taskId> 5 on the confirmed cluster display. Workaround for the display-level fullscreen lock (mDisplayWindowingMode=fullscreen) that made v1.2.47's task resize silently no-op.
  • F11 / F12 — resize task + verify new bounds (same logic as v1.2.47, now on the confirmed display + freeform-locked task).
  • F13 — move Yandex back to display 0 via resolved launcher.
  • F14 / F15 / F16 — cleanup: force-stop Yandex, reset FORCE_RESIZE_APP override, close the projection (sendInfo 18 + sendInfo 0) only if F01 opened it.

i18n

4 new keys × 12 locales = 48 entries: diag_dl5_fission_prompt_title, _msg_fmt (with %1$d for display id), _yes, _no. Hand-curated per locale; Uzbek apostrophes properly escaped.

Safety

  • Strictly scoped to the F-tier diagnostic battery on DL5.
  • Non-DL5 platforms still see one SKIPPED row.
  • R-tier (R01..R30) listener uses the new default onPromptYesNo (= NO) and never triggers a prompt because no F-step is in the R-catalog.
  • F01 / F16 are symmetric: the projection is only closed if F01 opened it in this run.
  • 180 s prompt timeout safety net.
  • No new permission, no manifest change, no new dependency.

See CHANGELOG for full diff.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.49-beta


DL5 Fission task-id extractor hardening (v1.2.49-beta)

Third field test (log/byd_report_20260525_152233.log, 11 PASS / 1 WARN / 0 FAIL / 4 SKIPPED) was the first run where the v1.2.48 interactive multi-display probe worked end-to-end: testeur said NO on display 2 (hidden), YES on display 3 → cluster confirmed. One bug remained — the awk-scoped extractYandexTaskIdOnDisplay returned -1 on this ROM (the actual dumpsys activity activities layout doesn't match the (mPreferredTopFocusableRootTask|mLastFocusedRootTask)=Task{…A=…yandexmaps} pattern), so F10 WARNed and F11/F12 SKIPPED → the resize never ran.

Fix

  • Drop per-display scoping. Since the runner force-stops Yandex before every candidate launch, exactly ONE Yandex task exists at the moment of YES. A global match for Task{…yandexmaps…} is sufficient and ROM-agnostic.
  • Two-pattern extractor. Primary: Task\{[^}]*ru\.yandex\.yandexmaps[^}]*\}#(\d+). Fallback: taskId=(\d+)[^\n]*ru\.yandex\.yandexmaps.
  • 4 attempts with 0/250/500/1000 ms backoff. Catches late-registered tasks (Yandex SplashScreen → main activity transition may not have published the task at the exact YES tap).
  • Debug capture into r.detail. Raw filtered dumpsys lines (grep -nE 'Task{|taskId=|yandexmaps' | grep -i yandex | head -20) attached to F07/F09 (and F10 if it retries). Any future regression is debuggable from the field report alone, no adb access needed.

Touched files

  • app/build.gradle — versionCode 306→307 + per-version comment.
  • dilink5/Dl5ClusterReconRunner.java — one method replaced + two call sites updated. ~80 LoC.

Safety

  • Same UX as v1.2.48 (popup flow unchanged).
  • Non-DL5 platforms see no behavioural difference.
  • R-tier listener untouched.
  • No new permission, manifest, dependency, layout, or string resource.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.51-beta


Changes since v1.2.49-beta:

  • Material 3 Dark theme upgraded to Premium Cyber Cockpit Modern EV look with electric blue, neon accents, and deeper high-contrast backgrounds.
  • Enhanced hand ergonomics and physical automotive touch targets (from 28dp completely rounded to sleek 16dp rounded corner rectangles).
  • Voice tab PoC step 2/3: wake-word placeholder using openWakeWord ONNX Runtime (Hey Jarvis, built with arm64-v8a and armeabi-v7a native optimized filters).
  • Bumped versionCode to 310 to guarantee the car OTA updater detects the update cleanly.
  • Guaranteed full backwards-compatibility with older DiLink 3 units while adopting native glass effects on DiLink 5.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.54-beta


DashCast v1.2.54-beta — cumulative release since v1.2.51

This pre-release bundles three internal builds (1.2.52, 1.2.53 and 1.2.54) into a single OTA-detectable package. Install on DL3 / DL5; DL2 testers only.


v1.2.54-beta — audit pass (build 313)

Read-only audit over 52 Java files and 108 XML resources. No P1 bug found. Four zero-risk cleanups applied; all other findings deferred until device-side validation.

Code

  • BetaProxyClient.onReceive: replace the residual pingBinder() round-trip by isBinderAlive() on the PROXY_CONNECTED broadcast path — coherent with the P2 migration already in place a few lines below.
  • TaskRemover: switch from System.out.println / printStackTrace to android.util.Log so daemon-side traces show up in logcat.

Resources

  • Drop the orphan drawable ic_screenshot.xml.
  • Drop 10 orphan string keys across all 12 locales (120 entries, all 12 strings.xml stay aligned at 469 keys each): diag_beta_report_copied, diag_dl5_cluster_toast_telegram_missing, diag_placeholder_subtitle, diag_placeholder_title, diag_tab_stress, keyboard_bridge_btn_back, keyboard_bridge_btn_close, keyboard_bridge_btn_enter, keyboard_bridge_hint, keyboard_bridge_input_hint.

Platform impact: zero — no platform-gated code modified. Beta wire protocol PROTOCOL_VERSION=2, MirrorDaemon LocalSocket 1007, SurfaceControl.Transaction instance API and ClusterInputForwarder 16-pointer pool all untouched.


v1.2.53-beta — defensive cleanup of v1.2.52 (build 312)

  • Keep v1.2.52's grace period and layout listener (both needed for the TextureView ready signal on cold boot).
  • Remove the redundant prefs-restore mLastLaunchTime arming line at MainActivity L1048 that was double-arming the launch debouncer when the preference was restored on resume.

v1.2.52-beta — MainActivity display-state race + TextureView black-preview fix (build 311)

  • Fix the race where MainActivity polled Display.STATE_ON before the projection display was actually registered with DisplayManager, leading to a stuck "Waiting for cluster…" state on DL5 cold boot.
  • Add a surfaceTexture ready listener to the preview TextureView and gate the first frame on it — fixes the all-black preview tile that some users hit on the first projection start of a session.
  • Introduce a launch-debounce grace period so the user can't double-tap the projection button and trigger two ClusterService.start() calls back-to-back.

Upgrade notes

  • No new permissions, no schema change, drop-in replacement of any 1.2.5x-beta.
  • DL2 testers: same caveats as before (no display fission; functional parity limited to projection + diag).
  • Signed with the same bydPlatform keystore — BYD system permissions remain granted on install.

versionCode 313 — the car's update checker will detect the bump and offer the install automatically.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.55-beta


DashCast v1.2.55-beta — field-validated fixes + Tier S audit pass + auto-launch restore

Beta build 315 on branch beta/1.2.0-dilink5. Not merged to main.

This release bundles the field-validated bug fixes that landed shortly after v1.2.54-beta, a full Tier S code audit (5 hot-path files — MirrorDaemon, ClusterInputForwarder, ClusterMirrorManager, ClusterService, MainActivity — totalling ~6 000 LoC), and the restoration of the auto-launch feature that had been silently broken since May 9 2026.


Bug fixes (field-validated)

  • Waze first-tap fix — singleInstance / taskAffinity apps were routed by AOSP to display 0 on cold launch despite ActivityOptions.setLaunchDisplayId. New ClusterService.enforceTaskOnDisplay() schedules a deferred IActivityTaskManager.moveTaskToDisplay, then applies the full setTaskWindowingMode(FREEFORM) + resizeTask(bounds) post-move sequence (factored as moveTaskToDisplayInternal(..., enforceOnly)), so per-app custom insets are no longer dropped when the enforcer fires at T+2.5s.
  • Mirror stayed black until fullscreen+back — the active mirror path is now tracked in ClusterMirrorManager via a mMirrorViaDaemon flag; daemon-arrival callbacks stopClusterMirror() first when the current path is direct, so attemptStartMirrorWithCurrentHolder actually re-establishes via the daemon.
  • 1.08 GB storage growth — every save / share / sniffer run was producing a fresh timestamped file under getExternalFilesDir with nothing rotating. New AppLogger.pruneOldFiles keeps the 3 most recent per prefix. Orphan logcat processes left over from setsid-detached sniffer runs are now reaped at app start via ShellGateway.execShell, gated on the absence of re_sniffer_file_path in byd_diag_prefs so an in-progress capture is never clobbered if the Activity is recreated under memory pressure.
  • "Hey Jarvis" wake-word toggle was a no-oponWakeWordToggle now auto-starts VoiceService (requesting RECORD_AUDIO if needed) so the engine actually receives audio frames.
  • Auto-launch on cluster connect — feature shipped in v0.2.3-alpha but the consumer block in onClusterDisplayConnected was unreachable because the pending-package field was never assigned. Restored in onCreate (one line). The toggle now actually fires the chosen app on the first cluster connection of each Activity instance.

Tier S audit pass — hot-path hardening

Five hot-path files audited end-to-end, batches committed independently.

  • MirrorDaemon.setupMirrordumpsys SurfaceFlinger Process leaked stderr / stdin FDs (only stdin was wrapped in try-with-resources). Wrapped output streams + explicit p.destroy() in a finally block. 1 FD per setupMirror call eliminated.
  • ClusterInputForwarder.injectTouchAtMultimTouchDownTime reset on ACTION_UP / ACTION_CANCEL was placed after the daemon-path early return;, so the reset never ran on that path. Moved into a finally block of the synchronized region. Harmless today (ACTION_DOWN resets unconditionally), but removes a latent trap for any future MOVE-without-DOWN sequence.
  • ClusterMirrorManager — dead mMirrorSurface field (assigned 4 times but never read; the Surface lifecycle is owned by MainActivity via the TextureView's SurfaceTexture).
  • ClusterService — six hardcoded notification strings (channel_name, channel_desc, initializing, active with %1$d displayId, stopped, disconnected) extracted to notif_cluster_* keys, translated in all 12 locales. Notification title changed from the legacy "BYD App" to @string/app_name ("DashCast").
  • MainActivityToast "✓" confirmation literal in the usage-stats reset path extracted to toast_usage_stats_reset × 12 locales. Verified safe: receiver/handler/surface/threads/executor/bindService lifecycle, isFinishing()/isDestroyed() guards on async callbacks, SharedPreferences.apply() everywhere, no PendingIntent, no MotionEvent leak (pre-allocated 16-pointer arrays mirror ClusterInputForwarder), one-shot reflection only.

Two latent regressions in the field-validated fix set were caught and corrected (enforceTaskOnDisplay missing the post-move resize sequence; orphan-sniffer kill being unconditional). Net diff after audit: +64 / -56 vs +192 / -2 before audit.

UI — auto-launch indicator

  • The auto-launch toggle, previously duplicated as an inline CheckBox on every list row, has been removed from the row layouts. The single source of truth is now the MaterialSwitch in the long-press action bottom sheet (dialog_app_actions.xml).
  • A small amber badge (▶ on a #FFC107 oval with white stroke) is now shown on the app icon in both list and grid modes when the app is marked as auto-launch. New drawable bg_auto_launch_badge.xml; the accessibility label reuses the existing app_state_auto string (already localised in all 12 locales).

i18n

  • Two new strings added in all 12 supported locales (fr base + en/de/es/it/ru/uk/be/kk/uz/tr/ar) for the audit batches: notif_cluster_* (6 keys × 12) and toast_usage_stats_reset (1 key × 12).

Invariants preserved

  • 16-pointer pre-allocated touch arrays (mFwdPointerIds / mFwdClusterXs / mFwdClusterYs mirroring ClusterInputForwarder).
  • MirrorDaemon PROTOCOL_VERSION = "2" unchanged.
  • SurfaceControl.Transaction instance API unchanged.
  • Preserved-by-design dead code documented in CHANGELOG v1.2.12 (key-injection path in MirrorDaemon / ClusterInputForwarder, btnActivateCluster removed in v1.2.76) left untouched.

Install

adb install -r DashCast-v1.2.55-beta-debug.apk

versionCode 315 (previous beta v1.2.54-beta = vc313).

Links

APK download

GitHub release page

  • Author

DashCast v1.2.56-beta


DL5 Fission test — user-pickable target app

Field-validated bug from the v1.2.54-beta DL5 tester run (log/byd_report_20260527_080752.log): 3 PASS / 13 SKIPPED — F02 aborted with ru.yandex.yandexmaps not installed — F03..F16 skipped even though the tester actually had 2GIS (ru.dublgis.dgismobile) and Yandex Navi (ru.yandex.yandexnavi, distinct package from Yandex Maps) installed. The Fission catalog was hard-wired to one specific package, so it had no way to use the perfectly valid alternatives that were sitting right there.

What's new

  • App picker before the destructive test. Tapping Test Fission now shows a single-choice dialog listing every launchable app on the device (alphabetical by label, DashCast excluded, rows formatted <label> — <package> so variants like Yandex Maps vs Yandex Navi are visible).
  • Two-step UX: pick app → confirm dialog now reads Target: <App Name> (<package>) then explains the destructive resize/move pipeline → tap Run.
  • Runner parameterised on the target package. All 14 hard-coded ru.yandex.yandexmaps reads inside the F-tier (F02 install check, F03/F15 am compat enable/reset, F04/F06/F08 pre-launch force-stop, F05/F07/F09 NO-branch cleanup, F14 final force-stop, move-back am start --display 0) replaced by the chosen package.
  • extractYandexTaskId is now generic — uses Pattern.quote(targetPkg) for client-side regex and the last two dot-segments as a shell-safe filter token in the dumpsys activity activities grep.
  • Catalog row titles generalised: F02-F15 no longer say "Yandex Maps" — they say "target app" so the diag report reads sensibly for any chosen package.
  • i18n (12 locales): 3 strings generalised (_hint, _confirm_msg, _prompt_msg_fmt — the YES/NO prompt now reads "Is 2GIS visible on the cluster screen? (testing display 3)"), 2 new strings (_pick_title, _pick_empty), zero MissingTranslation regression.

What's preserved (compat)

  • Legacy 1-arg runFission(Context, Listener) overload still defaults to Yandex Maps so any out-of-tree caller keeps working.
  • Test Fission button still gated on Platform.isAutoDetectedDiLink5() — DL2/DL3/DL4 unaffected.
  • Cancelling the picker dialog is a clean no-op.
  • Cleanup steps F14/F15/F16 still run unconditionally as before.

Touched files

  • app/build.gradle (315 → 316)
  • dilink5/Dl5ClusterReconRunner.javaFISSION_TARGET_PKGDEFAULT_FISSION_TARGET_PKG + sLastFissionTarget volatile + FissionState.targetPkg/targetLabel, two new runFission overloads, generalised catalog texts (~70 LoC net)
  • DiagActivity.java — new pickFissionTargetThenConfirm() + confirmFissionRun(pkg, label), 2-arg runClusterDl5FissionTests (~70 LoC net)
  • 12 × strings.xml — 3 generalised + 2 new keys

Behavioural contract (DL5 tester)

Install over v1.2.55, open Diag → Cluster DL5 → tap Test Fission → pick e.g. 2GIS — ru.dublgis.dgismobile → confirm → F01 opens projection → F02 PASS (target installed) → F03 enables FORCE_RESIZE_APP on 2GIS → F04 launches 2GIS on display 2 → "Is 2GIS visible on the cluster screen?" → NO → F06 on display 3 → YES → F10/F11/F12 actually run the freeform + resize + bounds-verify chain (no more skip on wrong package) → F13/F14/F15/F16 cleanup. Report's Target= line reflects the actual chosen package.

— Not merged to main. Pre-release on beta/1.2.0-dilink5.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.57-beta


DashCast v1.2.57-beta

Audit pass — Tier S + A + B + C complete (52/52 Java files, 113/113 res files).

This release closes the systematic per-file audit started in v1.2.56-beta. All applicative code under com.byd.dashcast has now been reviewed against the project's resource-leak / lifecycle / i18n / dead-code checklists. The fixes below are the actionable findings; the rest of the audit confirmed existing code is correct.

Fixes

Bugs

  • BootReceiver — fix double-finish() race on PendingResult. A goAsync() boot handler could call result.finish() twice when the 8 s hotspot pre-warm completed before the synchronous setup steps incremented the pending counter. The counter is now seeded at 1 (sentinel) and a final releaseOne.run() releases the sentinel — standard PendingResult coordination pattern. Eliminates the IllegalStateException: finish() called twice warnings in logcat at boot.
  • DiagActivity — populateLaunchableApps() moved off the UI thread. Querying PackageManager for every launchable activity could freeze the Diag tab for 200–500 ms on devices with many apps. The query now runs on a background thread, with UI gating on the "Run DL5 tests" button and an mDestroyed guard before runOnUiThread.
  • SettingsActivity — remove dead duplicate setChecked(). A leftover post-refactor setter that never had any effect.

Internationalisation

  • HotspotActivity — friendlyMacName() was returning a hardcoded French string ("Appareil xx:xx") on the connected-clients card. Extracted to hotspot_client_default_name and translated into the 12 supported locales (fr, en, de, es, it, ru, uk, be, kk, uz, tr, ar).
  • ClusterResizeActivity — "Aucun package" Toast extracted to resize_no_package + 12 locales.

Dead code removal

  • AdbLocalClient — removed 5 unused public methods (~373 LoC, 31 % of the file): runClusterDisplaySizeTest, captureClusterDisplay + its BitmapCallback interface, and 3 related helpers. All callsites verified absent across the codebase. Trimmed Bitmap/BitmapFactory imports.

Audit summary

Tier Files Outcome
S (hot path) 5 / 5 Done — 4 fixes
A 10 / 10 Done — 4 fixes
B 33 / 33 Done — 2 i18n fixes
C 4 / 4 Done — 0 fixes
Total 52 / 52 10 fixes applied across v1.2.55→v1.2.57

Resources: 113 / 113 reviewed — 0 dead layouts / strings / drawables / styles. i18n parity: 100 % across 12 locales × 480 keys.

Known items (deferred — documented in audit-state)

These were classified MED and not auto-applied; they require validation before landing:

  • MirrorDaemon.setupMirrorSurface read from Parcel is not release()d on the daemon side. Net effect unclear without a device-side test (dumpsys SurfaceFlinger before/after 100 mirror cycles).
  • Phase4Verbs.execShell (daemon) — timeoutMs parameter is silently ignored and stdout/stderr are read sequentially. To be hardened along the same pattern as ProxyDaemonMain.runShell (bounded waitFor + destroyForcibly + redirectErrorStream(true)).
  • MainActivity.mPendingAutoLaunchPkg — auto-launch-on-cluster preference has been a no-op since refactor 3b77081. One-line fix awaits user decision.

Compatibility

  • DiLink 3.0 (BYD Seal EU production) — primary target, fully validated.
  • DiLink 5.0 (BYD-AUTO tester) — all shared code paths preserved.
  • DiLink 2.0 (legacy tester) — all DL5/DL4 gates use Platform.isAutoDetectedDiLink2(); behaviour unchanged.

Artifact

  • DashCast-v1.2.57-beta-debug.apk (signed with bydPlatform key — BYDAUTO permissions granted).

Links

APK download

GitHub release page

  • Author

DashCast v1.2.58-beta-phase-A-step1


Pre-release for in-car testing.

Phase A step 1 — daemon auto-recovery in BetaProxyClient typed verbs.

When the proxy daemon dies (OOM kill, manual kill -9, etc.), production typed verbs now silently bootstrap a fresh one with a 10s cooldown gate (anti-storm). Behaviour when the daemon is healthy is byte-identical to v1.2.57-beta.

In-car test plan

  1. Smoke test: boot normally → enable cluster mirror, resize via SeekBar, force-stop a mirror, switch YouTube → cluster. Must behave exactly as before, no visible regression.
  2. Auto-recovery: from ADB:
    adb shell pgrep -f dashcast_proxy
    adb shell kill -9 <pid>
    
    then trigger any cluster action (resize or force-stop). Must succeed silently with a single info log line: attemptReconnect: bootstrapping daemon (cooldown gate passed) followed by daemon ready (uid=2000 pid=…).
  3. Logs to collect: logcat tag BetaProxyClient plus any WARN.

Wrapped verbs (production hot path + cluster ops)

runShell, setOverscan, getPidsByPackage, autoContainerSendInfo, forceStopPackage, releaseVirtualDisplay, launchAndForce, moveAndResize, cleanFissionStacks.

Intentionally not wrapped

ping, runPhase4Probes (health/diag, retry would mask failures), createVirtualDisplay (Surface lifecycle owned by caller).

Zero-regression guarantees

  • Daemon healthy → identical path, overhead = one volatile read.
  • Daemon dead, reconnect OK → silent recovery, single info log line.
  • Daemon dead, reconnect fails or cooldown-gated → throws BetaProxyException exactly like before → callers still fall back to legacy AdbLocalClient.executeShell*.

Branch: phase-A-proxy-recovery — not merged into main, beta/1.2.0-dilink5 untouched.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.60-beta


Pre-release for in-car testing. Not merged into main. Branch: phase-A-proxy-recovery.

This release bundles three beta increments since v1.2.57-beta. None of them changes behaviour on DL2 / DL3 / DL4 — all DL5-specific work is gated by the existing platform auto-detection.


v1.2.60-beta (vc320) — DL5 Fission test: 3 new exploration probes (F12A / F12B / F12C)

Pure additive rows inserted between F12 and F13 in the Fission live-test battery. F01..F12 + F13..F16 behave byte-identical to v1.2.59-beta — if a tester re-runs the suite the existing rows look exactly the same, the 3 new rows just give us more data. On any platform other than DL5 the 3 new rows SKIP via the existing outer DL5 gate.

The goal of the 3 probes is to find a real DL5 resize path despite the stripped cmd activity verbs that v1.2.59-beta documented.

  • F12A — verb-surface inventory. Pure read-only. Runs cmd activity (no-arg help) + cmd activity task + cmd window + a focused am help grep. Reports a one-line summary:
    am task resize=Y/N, set-task-windowing-mode=Y/N, cmd window set-display-windowing-mode=Y/N
    Authoritative inventory of what survived the BYD strip — no more guessing which subverb to try next.

  • F12B — display windowing mode → freeform, retry resize, auto-revert. Sequence:

    1. cmd window set-display-windowing-mode <confirmedClusterDisplay> freeform
    2. sleep 1 s
    3. cmd activity task resize <taskId> 100 80 1820 640
    4. sleep 2 s
    5. dumpsys mBounds for the task
    6. revert the display back to fullscreen so the cluster face is left exactly as F12 saw it.

    PASS = BREAKTHROUGH — display freeform unlocked task resize. Tests the hypothesis that the silent no-op of F11 in v1.2.59 was caused by the fission display declaring mDisplayWindowingMode=fullscreen, and that forcing the display itself into freeform is the missing piece.

  • F12C — launch-time bounds via --activity-launch-bounds. Sequence:

    1. am force-stop <targetPkg>
    2. am start --display <id> --windowingMode 5 --activity-launch-bounds 100,80,1820,640 -n <component>
    3. sleep 3 s
    4. dumpsys mBounds

    PASS = BREAKTHROUGH — launch-time bounds honoured on cluster display. Tests whether DL5 honours launch-time bounds where post-launch resize is a no-op; if so, the production resize path becomes "tear down + relaunch at new rect" rather than mutating an existing task. WARN-on-unknown option so the row's message is self-explanatory.

Safety / reversibility: F12B's only mutation (display windowing mode) is reverted in the same shell pipeline before the row returns. F12C's only mutation (re-launched activity) is cleaned up by the existing F14 force-stop two rows later.

Touched files. app/build.gradle (319 → 320, 1.2.59-beta → 1.2.60-beta), dilink5/Dl5ClusterReconRunner.java (+3 TestDef entries, +3 switch arms in runFissionOne, +3 private implementations, +1 tokenForPkg busybox-safe grep-token helper), CHANGELOG.md. No new strings — no i18n surface touched.


v1.2.59-beta (vc319) — DL5 cluster resize disabled at the UI when the ROM ships a stripped cmd activity binary

Field-validated workaround. Source: log/byd_report_20260528_081206.log (BYD DiLink 5.0 / Android 12 / build SKQ1.230128.001, user-run Fission live tests).

The report confirms the launch pipeline works end-to-end on DL5 — F01 PASS (service call auto_container 2 i32 1000 i32 16 opens the cluster projection), F04 + F05 PASS (am start --display 2 --windowingMode 5 -n ru.yandex.yandexnavi/.core.NavigatorActivity lands Yandex Navi fullscreen on the cluster face, user-confirmed visually), F13..F16 PASS (move-back-to-display-0 + sendInfo 18+0 cleanup chain works).

But the report also proves resize is a ROM-level no-op on this build:

  • F10 WARN — cmd activity set-task-windowing-modeUnknown command: + exit=255 (verb stripped from the binary).
  • F11 PASS-but-no-op — cmd activity task resize 76 100 80 1820 640 returns exit=0 with zero visible effect.
  • F12 WARN — post-resize dumpsys shows mBounds=Rect(0, 0 - 1920, 720) mWindowingMode=fullscreen mMagicWindowMode=MAGIC_MODE_FULLSCREEN (bounds unchanged).

That's the same outcome the in-app ClusterService.resizeActiveTask reflection-then-shell cascade was producing on every SeekBar drag since v1.2.26 — silently doing nothing while looking like it worked.

Fix — strictly additive, fully reversible, DL2/DL3/DL4 unchanged.

  1. Platform.isClusterTaskResizeSupported(ctx) — new lazy capability probe. On DL5 it forks one sh -c "cmd activity set-task-windowing-mode 2>&1" with a 1.5 s timeout on a daemon thread and caches the result both in a volatile field and a sticky SharedPreferences key (platform_cluster_resize_supported = "yes" | "no"). Conservative: any error or ambiguous output → "supported" (UX never downgrades on uncertain signal). On DL2/DL3/DL4 the method short-circuits to true immediately — no probe, no behavioural change on those platforms.

  2. DashCastApp.onCreate primes the probe at startup via Platform.primeClusterResizeProbe(this) so UI reads return the cached answer instantly without forking on the main thread.

  3. ClusterService.resizeActiveTask short-circuits at entry when !isClusterTaskResizeSupported(this), logs once via the new static sResizeUnsupportedLogged gate, and returns without running the IATM-then-shell cascade — saves ~30 useless verbs per SeekBar drag.

  4. MainActivity hides btn_toggle_resize + panel_resize on DL5 when the probe returned "no", so the user never sees a broken affordance. No new strings.

Reversibility. When a future BYD ROM update restores the verb, wiping the SharedPreferences key re-enables the SeekBar with zero code change.

Docs. New doc_api/DL5_CLUSTER_RESIZE_LIMITATION.md with the full F10/F11/F12 verbatim plus three RE-able avenues for an actual DL5 resize.


v1.2.58-beta (vc318) — Phase A step 1: daemon auto-recovery in BetaProxyClient typed verbs

When the proxy daemon dies (OOM kill, manual kill -9, etc.), production typed verbs now silently bootstrap a fresh daemon, gated by a 10 s cooldown (anti-storm). When the daemon is healthy, behaviour is byte-identical to v1.2.57-beta.

Wrapped verbs (production hot path + cluster ops): runShell, setOverscan, getPidsByPackage, autoContainerSendInfo, forceStopPackage, releaseVirtualDisplay, launchAndForce, moveAndResize, cleanFissionStacks.

Intentionally not wrapped: ping, runPhase4Probes (health / diag — retry would mask failures), create* (unchanged).


In-car test plan for v1.2.60-beta

  1. Smoke test (any platform). Boot normally → enable cluster mirror, force-stop a mirror, switch YouTube → cluster. Must behave exactly as v1.2.57-beta, no visible regression. Resize SeekBar: visible on DL2/DL3/DL4, hidden on DL5 (workaround from v1.2.59).

  2. DL5 Fission probe (NEW — primary objective of this release). Open Diag → run Fission live tests with any installed launchable target (2GIS, Yandex Navi, Maps…). Watch the new rows:

    • F12A always passes (it's read-only) — copy its msg line, it tells us which verbs survived.
    • F12B PASS = breakthrough on the "display freeform" hypothesis.
    • F12C PASS = breakthrough on the "launch-time bounds" hypothesis.
    • Both WARN = report stays useful, we'll design the next probe based on F12A's inventory + F12B/F12C raw output.
  3. Daemon auto-recovery sanity (carried over from v1.2.58).

    adb shell pgrep -f dashcast_proxy
    adb shell kill -9 <pid>
    

    then trigger any cluster action. Must succeed silently with a single info log line: attemptReconnect: bootstrapping daemon (cooldown gate passed) followed by daemon ready (uid=2000 pid=…).

  4. Logs to collect. Send the full Fission report via the existing "Send via Telegram" button + logcat tags BetaProxyClient, Dl5ClusterRecon, plus any WARN/ERROR.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.61-beta


Pre-release for in-car testing.

Adds three buttons in Diag → Cluster Move/Resize panel, right under the existing 🔄 Restart daemon row, so the v1.2.58-beta Phase A step 1 auto-recovery can be validated directly from the car — no ADB required.

New buttons

  • 💀 Kill daemon — SIGKILLs dashcast_proxy via dadb and shows the new isConnected state. Then trigger any normal diag action (Lister displays, Lancer, Apply…) and watch the auto-bootstrap kick in silently.
  • 🩺 Test recovery — kill + immediate typed-verb probe (getPidsByPackage). Reports oldPid → newPid, elapsed ms and PROTOCOL_VERSION. This is the golden-path validation.
  • 🌪 Anti-storm test — (kill → probe) ×3 in <1 s. Expected: cycle 1 reconnects (5–8 s), cycles 2 and 3 are cooldown-blocked (10 s gate), proving the bootstrap-cascade protection works.

Kill is always routed through AdbLocalClient (dadb), never through the wrapped BetaProxyClient.runShell, so auto-recovery can't race the kill.

In-car test plan (Telegram copy-paste below)

  1. Smoke test — boot, enable cluster mirror, resize SeekBar, force-stop a mirror, switch YouTube ↔ cluster. Must be visually identical to v1.2.57-beta.
  2. 🩺 Test recovery — should show ✅, newPid different from oldPid, elapsed ~5000–8000 ms, proto ≥ 5.
  3. 🌪 Anti-storm test — cycle 1 ✅, cycles 2 and 3 ⏸ "cooldown blocked".
  4. Optional: 💀 Kill daemon then press "📋 Lister displays" → must succeed silently after a short delay.

If anything is unexpected, share the report (📤 Partager rapport) and the logcat tag BetaProxyClient.

Branch: phase-A-proxy-recovery — still not merged into main, beta/1.2.0-dilink5 untouched.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.62-beta


Pre-release for in-car testing.

Phase A step 2 — proactive liveness ping for the proxy daemon.

A new ProxyWatchdog singleton, installed at app boot, polls BetaProxyClient.isConnected() every 30 s while the app is in the foreground. When the binder is dead it triggers a silent connect() so the next user-initiated typed verb is instant instead of paying the ~1 s bootstrap cost on its first call (as observed in v1.2.61-beta tests).

Design

  • Polling only while at least one activity is resumed (registerActivityLifecycleCallbacks, zero new deps).
  • Stops as soon as the last activity pauses → zero background drain.
  • Gated on BetaConfig.isProxyDaemonEnabled → users who never opted in pay strictly zero overhead.
  • Reconnect path reuses the same 10 s cooldown gate inside connect() → bootstrap cascades remain impossible.
  • Tick body is try/catch wrapped on a dedicated HandlerThread → can never crash the app.

Cost when daemon is healthy

One volatile read + one isBinderAlive() (~5 µs) every 30 s. Invisible.

In-car test plan

  1. Smoke test — boot normally, exercise cluster mirror / resize / force-stop. Must be visually identical to v1.2.61-beta.
  2. Watchdog status — Diag → Cluster POC panel → new button 🐕 Watchdog status. Should show installed=true foreground≥1 proxyEnabled=true connected=true pid=….
  3. Proactive recovery — same panel:
    • tap 💀 Kill daemon (pid disappears),
    • wait 30–40 s without touching the app,
    • retap 🐕 Watchdog statuspid must have changed (new daemon, brought up by the watchdog, not by you).
  4. Optional: redo 🩺 Test recovery after step 3 — should now report elapsed ≈ 0 ms since the daemon was already up.

Logcat tag: ProxyWatchdog (one info line on install, one info line per proactive reconnect).

Branch: phase-A-proxy-recovery — still not merged into main, beta/1.2.0-dilink5 untouched.

Links

APK download

GitHub release page

  • Author

DashCast v1.2.63-beta


Phase A step 3 — PID-file + flock + trigger-file rebroadcast

Builds on step 1 (auto-recovery) + step 2 (foreground watchdog). The
daemon now writes its PID to a known path and watches a trigger file;
the bootstrap script's fast path can ask a surviving daemon to
re-emit its binder in milliseconds, instead of paying the ~1 s
app_process restart cost after every app restart.

Daemon (ProxyDaemonMain)

  • Writes its PID to /data/local/tmp/dashcast_proxy.pid at startup.
  • Watches /data/local/tmp/dashcast_proxy.trigger via FileObserver
    and re-emits ACTION_PROXY_CONNECTED on any touch.
  • Best-effort shutdown hook cleans PID + trigger files.
  • Extracted emitBroadcast() so the trigger path and the cold-boot
    path go through the exact same code.
  • PROTOCOL_VERSION 6 → 7 (purely additive, old clients still work).

App (BetaProxyClient.BOOTSTRAP_CMD)

  • Atomic via flock -n on /data/local/tmp/dashcast_proxy.lock
    concurrent bootstrap invocations now serialize at the shell level
    (belt-and-suspenders on top of the 10 s Java cooldown from step 1).
  • PID-file fast path: if a daemon is already alive and its
    /proc/<pid>/comm is dashcast_proxy, the script just touches
    the trigger file and exits with REBROADCAST <pid>. Skips the
    entire app_process + systemMain + broadcast chain.
  • Silent fallback to the legacy full bootstrap when the PID file is
    missing/stale or flock is unavailable — zero regression.

Diag (Cluster POC panel)

  • New « 📡 Test rebroadcast (fast-path) » button: reproduces the
    fast-path shell branch and reports the round-trip time. Expect
    < 200 ms with REBROADCAST <pid> in the output.

In-car test plan

  1. Smoke — boot, cluster mirror, resize, force-stop unchanged vs v1.2.62.
  2. Watchdog status — installed + connected + non-zero PID.
  3. NEW — Test rebroadcast — tap the button, expect and < 200 ms.
  4. Verify PID fileadb shell cat /data/local/tmp/dashcast_proxy.pid
    must match the PID shown by Watchdog status.
  5. Cross-restart fast path — kill the app (swipe from recents), reopen,
    open Diag and tap any wrapped verb (Recovery test, etc.). Reconnect
    should be noticeably faster than v1.2.62 (no app_process respawn).

Links

APK download

GitHub release page

  • Author

DashCast v1.2.64-beta


Export daemon log (Telegram) — in-car post-mortem

Adds a « 📥 Export daemon log (Telegram) » button in Diag → Cluster POC.

  • Reads /data/local/tmp/dashcast_proxy.log via AdbLocalClient
    (uid=2000 shell owns the file → works even when the daemon is dead).
  • Prepends a state header: PID / lock / trigger file status + alive
    dashcast_proxy processes.
  • Caps the log tail at 200 KB (Telegram-friendly).
  • Writes to app cache + shares via the FileProvider chooser.

No more adb pull needed for in-car incident reports.

Includes all v1.2.63-beta changes (Phase A step 3: PID-file + flock + trigger-file rebroadcast).

Links

APK download

GitHub release page

  • Author

DashCast v1.2.65-beta


🚑 HOTFIX — v1.2.63 / v1.2.64 had a critical bootstrap regression

Symptoms in v1.2.63 / v1.2.64: projection refusing to start, tablet
freezes, every bootstrap call returns ALREADY_BOOTSTRAPPING even on
cold boot, followed by no live binder after 15000ms.

Root cause: the flock-based bootstrap script opened FD 9 to take an
advisory lock, then spawned the daemon via setsid sh -c "..." &
without closing FD 9 in the child. The daemon inherited FD 9 and held
the lock for its entire lifetime. Every subsequent bootstrap call then
hit flock -n 9 → fail → ALREADY_BOOTSTRAPPING, never reaching the
PID-file fast path. Auto-recovery broken, rebroadcast unreachable,
15 s broadcast timeouts on every reconnect, UI freezes when fired on
the main thread.

Fixes

  1. PID-file fast path moved BEFORE the flock. It's pure read +
    trigger-file touch, idempotent under concurrency — never needed
    the lock. This unblocks recovery for users with a v1.2.63 / v1.2.64
    daemon currently stuck in the field (the fast-path bypasses the
    lock those daemons still hold until next reboot/kill).
  2. 9>&- added to the setsid spawn so the daemon explicitly
    closes FD 9 before exec app_process64. Future bootstraps see a
    free lock; the flock guard works as designed (concurrent
    cold-start storm protection only).

Zero new code paths — same script, just reordered + one FD close.

Recommended install path

  • Install v1.2.65 directly on top of v1.2.64 (the old daemon stays
    usable via the fast-path).
  • If problems persist: reboot the tablet to kill the stuck old
    daemons (v1.2.63 / v1.2.64).

Links

APK download

GitHub release page

  • Author

DashCast v1.2.66-beta


🚑 HOTFIX #2 — fast-path was reading the wrong /proc field

Symptoms in v1.2.65: same as v1.2.63/64 — projection refuses to
start, freezes, every bootstrap returns ALREADY_BOOTSTRAPPING.

Root cause: v1.2.65's fast-path tested
cat /proc/$PID/comm = dashcast_proxy to validate that the PID file
pointed to our daemon. But /proc/PID/comm is the thread name —
Android's app_process --nice-name sets argv[0] (visible in
cmdline and ps) but the JVM keeps the main thread's comm as
main. The fast-path therefore never matched even with a healthy
daemon alive. Combined with the v1.2.63/64 daemon still holding FD 9
(unkilled in the field), every bootstrap fell through to flock
ALREADY_BOOTSTRAPPING → 15 s broadcast timeouts → fallbacks
everywhere → freezes.

Fix

Replace the comm check with

grep -aq dashcast_proxy /proc/$P/cmdline
  • -a forces text mode (cmdline is NUL-separated)
  • -q silent, returns 0/1
  • matches the same field used by the existing ps | grep stale-kill
    heuristic, so the two identity checks are now consistent.

Applied identically to the Diag « Test rebroadcast » button.

Recommended install path

  • Install v1.2.66 on top of v1.2.65 (no need to uninstall).
  • If projection stays broken after install, a single tablet reboot
    is enough (kills the stuck v1.2.63/64/65 daemon) → afterwards
    v1.2.66 should work normally and the PID-file fast-path should kick
    in for every subsequent bootstrap.

Links

APK download

GitHub release page

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.