443 lines
24 KiB
Markdown
443 lines
24 KiB
Markdown
# Pi Car Companion — Product and Delivery Plan
|
||
|
||
## 1. Purpose
|
||
|
||
Pi Car Companion is a local-first companion computer for an MG4. A Raspberry Pi 5 runs services, diagnostics, automations, and a touch-friendly dashboard; the car's existing Android infotainment system remains the display and input device.
|
||
|
||
The project supplements the vehicle. It must never replace or modify the OEM launcher, firmware, vehicle controls, CAN bus, safety systems, or any driving-critical function.
|
||
|
||
The intended daily flow is simple:
|
||
|
||
1. The Pi boots independently and starts the companion service.
|
||
2. The MG4 Android app opens the Pi-hosted dashboard at `http://mg4pi.local:8787`.
|
||
3. The user sees real system state and can run a small set of audited, server-defined actions.
|
||
4. If the native app is unavailable, the same dashboard works in a browser or as a PWA.
|
||
|
||
Core operation must work without cloud access. Optional remote access is a later, explicitly configured feature.
|
||
|
||
## 2. Product principles
|
||
|
||
- **Local first:** the Pi and dashboard remain useful on an isolated LAN.
|
||
- **Safe by construction:** privileged actions are allowlisted, validated, confirmed where necessary, and audited.
|
||
- **Truthful UI:** unavailable information is shown as unavailable with a reason; no fake telemetry, devices, logs, or success states.
|
||
- **Headless Pi:** routine use and administration require no dedicated Pi display.
|
||
- **Touch first:** the primary target is a roughly 1920×720 landscape infotainment screen with large controls and no hover-only interactions.
|
||
- **Recoverable:** service failures, network loss, partial jobs, and interrupted updates have explicit recovery paths.
|
||
- **Least privilege:** the service runs as a dedicated non-root user and receives only narrowly scoped privileges.
|
||
- **Progressive scope:** establish a secure end-to-end vertical slice before adding integrations or convenience features.
|
||
|
||
## 3. Scope
|
||
|
||
### Version 1
|
||
|
||
- First-run administrator creation, login, logout, and session management
|
||
- Real Raspberry Pi health and network status
|
||
- Live connection and job progress updates
|
||
- Server-defined jobs with timeouts, validation, logs, and audit history
|
||
- Initial jobs:
|
||
- refresh system health
|
||
- run network diagnostics
|
||
- generate a sanitized support bundle
|
||
- Touch-focused web dashboard
|
||
- Native Android WebView shell with editable trusted dashboard URL and reconnect controls
|
||
- Read-only ADB connection health and device/display identity for one explicitly configured head unit
|
||
- Raspberry Pi bootstrap, systemd service, health check, update, and uninstall scripts
|
||
- Automated tests, production builds, and operator documentation
|
||
|
||
### Version 1.1
|
||
|
||
- Controlled artifact upload, listing, download, and deletion
|
||
- SHA-256 metadata and storage quota reporting
|
||
- Wi-Fi status/reconnect where supported by the host
|
||
- Dual-radio car hotspot with USB-upstream preference, 60-second built-in fallback, and automatic offline recovery
|
||
- Log rotation/pruning job
|
||
- Companion service restart job
|
||
- Reboot and shutdown actions with strong confirmation
|
||
- Audit filtering and diagnostic export improvements
|
||
- Live hotspot client, DNS-query metadata, and active-flow diagnostics
|
||
- Optional PWA installation support
|
||
|
||
### Later, only after real-world validation
|
||
|
||
- Tailscale-assisted remote access
|
||
- Additional local services and automations
|
||
- Media-adjacent utilities
|
||
- Read-only vehicle integrations, only with a separate threat model and explicit hardware testing
|
||
|
||
### Explicitly out of scope
|
||
|
||
- Arbitrary shell, terminal, SSH, ADB, Docker socket, or unrestricted file-browser access through the dashboard
|
||
- Public-internet exposure by default
|
||
- Hardcoded or default credentials
|
||
- Rooting, flashing, or modifying MG4 infotainment software
|
||
- Sending vehicle-control commands or interacting with safety-critical systems
|
||
- Claiming hardware behavior that has not been tested on the actual Pi/MG4 installation
|
||
|
||
## 4. Proposed architecture
|
||
|
||
```text
|
||
MG4 Android app / browser
|
||
|
|
||
| trusted local HTTP(S) origin
|
||
v
|
||
Fastify API + static React app <----> SQLite
|
||
|
|
||
+---- system status collectors
|
||
+---- allowlisted read-only ADB collectors
|
||
+---- allowlisted job handlers
|
||
+---- controlled artifact storage
|
||
+---- audit and diagnostic services
|
||
```
|
||
|
||
### Repository layout
|
||
|
||
```text
|
||
pi-car-companion/
|
||
├── server/ # Fastify API, auth, jobs, persistence, collectors
|
||
├── web/ # React touch dashboard and PWA assets
|
||
├── android-companion/ # Kotlin Android WebView application
|
||
├── config/ # Safe defaults and example configuration
|
||
├── docs/ # Operations, security, networking, validation
|
||
├── scripts/ # Pi bootstrap/install/update/uninstall utilities
|
||
├── systemd/ # Service and optional watchdog units
|
||
├── tests/ # Cross-package/integration tests and fixtures
|
||
├── .env.example
|
||
├── docker-compose.dev.yml # Development dependencies only, if needed
|
||
├── package.json # Workspace commands
|
||
└── README.md
|
||
```
|
||
|
||
### Technology baseline
|
||
|
||
- Raspberry Pi OS Lite / Debian arm64 on Raspberry Pi 5
|
||
- Node.js 22 and strict TypeScript
|
||
- Fastify, Zod, SQLite, and Argon2id
|
||
- React, Vite, and a PWA-capable frontend
|
||
- Server-Sent Events by default for one-way live status; use WebSockets only if bidirectional live messaging becomes necessary
|
||
- Native Kotlin Android app, minimum SDK 28, targeting a current stable Android SDK
|
||
- systemd for production process supervision
|
||
|
||
Use a JavaScript workspace setup that keeps server and web commands consistent. Do not introduce containers into the production Pi path unless a measured need emerges.
|
||
|
||
## 5. System design
|
||
|
||
### Authentication and first run
|
||
|
||
- There is no default account.
|
||
- Setup is available only while the user table is empty; the first successfully created user becomes administrator.
|
||
- Setup and account creation must be transaction-safe so concurrent requests cannot create multiple initial administrators.
|
||
- Passwords are hashed with Argon2id using parameters documented and tested on Pi hardware.
|
||
- Sessions use opaque, revocable identifiers in HttpOnly cookies.
|
||
- Cookies use `SameSite=Strict` where compatible, a narrow path, and `Secure` whenever HTTPS is enabled.
|
||
- Every state-changing browser request requires CSRF protection and origin validation.
|
||
- Login and setup endpoints are rate-limited without leaking account existence.
|
||
|
||
### Server and API
|
||
|
||
- Bind to loopback by default. LAN binding is an explicit configuration change documented during installation.
|
||
- Provide `GET /healthz` for process health and a separate authenticated system-status endpoint.
|
||
- Validate request params, query strings, bodies, and relevant configuration with Zod.
|
||
- Use structured logs with a central redaction policy.
|
||
- Return stable machine-readable error codes alongside safe user-facing messages.
|
||
- Keep collection failures isolated: one unavailable sensor must not fail the entire status response.
|
||
|
||
### System status
|
||
|
||
Collect actual values for:
|
||
|
||
- hostname, OS/version, uptime, and current timestamp
|
||
- CPU model, load average, and temperature when exposed by the OS
|
||
- RAM and disk usage
|
||
- network interfaces and addresses
|
||
- Wi-Fi connection and signal when supported
|
||
- companion service health
|
||
- time of the last successful collection
|
||
|
||
Each field that can fail should carry an availability state and safe reason. Never substitute generated values.
|
||
|
||
### Controlled jobs
|
||
|
||
Jobs are registered in code through a typed manifest. The client can discover and invoke only registered jobs; it never supplies an executable or command line.
|
||
|
||
Every job definition includes:
|
||
|
||
- stable ID, title, description, and risk level
|
||
- typed input schema and allowed values
|
||
- explicit timeout and concurrency policy
|
||
- confirmation policy
|
||
- server-side handler
|
||
- log sanitization rules
|
||
|
||
Every run records:
|
||
|
||
- unique run ID and job ID
|
||
- authenticated actor
|
||
- validated, safely serializable inputs
|
||
- queued, started, and completed timestamps
|
||
- progress and terminal state
|
||
- duration, result, and stable error code
|
||
- bounded, sanitized stdout/stderr or handler output
|
||
|
||
Jobs must handle timeout, cancellation caused by service shutdown, duplicate submission, and stale in-progress records after restart. Destructive jobs require a short-lived, action-bound confirmation rather than a reusable client-side checkbox.
|
||
|
||
### Artifacts
|
||
|
||
Artifacts live under one configured data directory; API callers never provide filesystem paths.
|
||
|
||
- Generate storage names server-side and retain a sanitized display name separately.
|
||
- Reject traversal, separators, reserved names, oversize files, and executable content by default.
|
||
- Enforce limits while streaming, not only from request headers.
|
||
- Record size, detected type, SHA-256, uploader, and upload time.
|
||
- Require authentication for list/download and confirmation plus audit logging for deletion.
|
||
- Define retention and quota behavior before enabling uploads in production.
|
||
|
||
### Audit and diagnostics
|
||
|
||
- Audit authentication, settings changes, job lifecycle events, artifact changes, and privileged actions.
|
||
- Audit records are append-oriented and distinct from verbose application logs.
|
||
- Redact passwords, cookies, CSRF values, tokens, authorization headers, environment secrets, and sensitive query parameters at ingestion and export.
|
||
- Support bundles contain an explicit manifest and only allowlisted diagnostic files/data.
|
||
- The UI provides filters, job output viewing, and a plaintext “copy diagnostics” view.
|
||
- Retention is configurable and pruning itself is audited.
|
||
|
||
### Android companion
|
||
|
||
- First launch asks for a dashboard URL, defaulting to `http://mg4pi.local:8787`; the saved value remains editable.
|
||
- Only the configured origin is trusted. External navigation is blocked or opened only after explicit confirmation.
|
||
- Cleartext traffic is permitted only through a narrow network-security configuration suitable for the configured local origin; production HTTPS remains supported.
|
||
- JavaScript, cookies, and DOM storage are enabled only as required for the trusted dashboard.
|
||
- Use `WebViewClient` and `WebChromeClient` with native loading, offline, certificate, and connection-error states.
|
||
- Provide a persistent native reconnect/refresh action.
|
||
- Preserve Android soft-keyboard behavior and support file chooser/download flows without broad storage permissions.
|
||
- Optimize for landscape and offer immersive mode without making navigation recovery difficult.
|
||
|
||
### MG4 infotainment and ADB boundary
|
||
|
||
The hardware baseline is sourced from the captured MG4 Luxury 2022 profile at
|
||
`https://git.molberg.cloud/alex/mg4-luxury-2022-info` (capture dated 2026-07-30;
|
||
reference commit `162a6636df89e1df2bba7ffc14550a37b3f9e9d5`). Treat it as a device-specific
|
||
reference, not proof for other model years, trims, regions, or software builds.
|
||
|
||
- The tested head unit is Android 9/API 28 on arm64, with ADB enabled but no root,
|
||
no `su`, and SELinux enforcing. The companion must not depend on root or system
|
||
partition changes.
|
||
- Display 0 is 1920×720 at 160 dpi, but the usable app area is 1778×640 because of
|
||
an 80 px top status bar and 142 px left navigation bar. Use Android window insets;
|
||
do not model the navigation bar as a bottom inset.
|
||
- The physical pixels are non-square. Emulator checks cannot validate the apparent
|
||
shape of circles, gauges, artwork, or logos; those require the real display.
|
||
- Car UX restrictions are enforced while moving. Version 1 is parked-use software
|
||
unless a later native activity is explicitly designed, declared, and validated as
|
||
distraction optimized.
|
||
- The Pi is the only intended ADB host. The bridge targets either one configured
|
||
serial or exactly one automatically discovered device, refuses ambiguous multi-device
|
||
connections, uses argument-array process execution, enforces timeouts and output bounds,
|
||
expose unavailable/unauthorized/offline states, and register every operation in a
|
||
typed server-side allowlist. No API accepts a shell string, arbitrary ADB arguments,
|
||
a package/component supplied by a client, or an unrestricted file path.
|
||
- Begin with read-only device identity, connection health, display, package/version,
|
||
and carefully selected `dumpsys` collectors. Any app launch, input injection, file
|
||
transfer, or settings change is a separate state-changing capability requiring an
|
||
explicit threat model, parked-state policy, confirmation policy, and audit trail.
|
||
- Do not use ADB or the exported SAIC vehicle service to actuate vehicle hardware in
|
||
this project. Vehicle telemetry and vendor APIs remain a later, read-only research
|
||
track until permissions, semantics, privacy, and parked-vehicle tests are documented.
|
||
|
||
### Web experience
|
||
|
||
The web app must remain usable for desktop/mobile setup, but the primary runtime layout targets 1920×720 landscape.
|
||
|
||
- Minimum 48dp touch targets; prefer 56–72dp for primary actions.
|
||
- Dark, high-contrast UI with large readable type and restrained use of cards.
|
||
- Persistent connection state and obvious reconnect action.
|
||
- Keyboard-accessible controls and visible focus states.
|
||
- No hover-only behavior.
|
||
- Dialogs, logs, menus, and settings panes must fit and scroll at 720px height.
|
||
- Dangerous actions are visually distinct without relying on color alone.
|
||
|
||
Primary areas:
|
||
|
||
1. **Home:** connection, health, time, network, warnings, recent runs, and safe quick actions.
|
||
2. **Jobs:** server-discovered actions, validated inputs, confirmations, progress, history, and logs.
|
||
3. **Artifacts:** upload, usage, metadata, download, and confirmed deletion.
|
||
4. **Audit:** filterable event history, job output, and support-bundle export.
|
||
5. **Settings:** access mode, mDNS, optional Tailscale state, retention, sessions, and danger zone.
|
||
|
||
## 6. Delivery roadmap
|
||
|
||
The repository contains a working MVP and tracks remaining delivery work here. Status markers are: `[ ]` pending, `[~]` in progress, `[x]` complete, `[!]` blocked.
|
||
|
||
### Milestone 0 — Decisions and scaffold
|
||
|
||
- [ ] Record architecture decisions for workspace tooling, SQLite library/migrations, sessions, CSRF, and live events.
|
||
- [x] Create the monorepo layout and shared TypeScript/tooling configuration.
|
||
- [x] Add repeatable `dev`, `lint`, `typecheck`, `test`, and `build` workspace commands.
|
||
- [x] Establish environment parsing, structured logging/redaction, migrations, and test database helpers.
|
||
- [ ] Add CI for all host-runnable quality gates.
|
||
|
||
**Exit:** a clean checkout installs reproducibly and all empty/scaffolded packages build and test through root commands.
|
||
|
||
### Milestone 1 — Secure authenticated vertical slice
|
||
|
||
- [~] Implement SQLite migrations for users, sessions, settings, audit events, and job runs. User, session, audit, and job-run tables are complete; settings remain pending.
|
||
- [x] Implement atomic first-admin setup, login, logout, session expiry/revocation, CSRF, and throttling.
|
||
- [x] Build setup/login screens and authenticated application shell.
|
||
- [x] Implement `GET /healthz` and authenticated system status with per-field availability.
|
||
- [x] Show real connection and health state in the dashboard with loading, error, empty, and reconnect behavior.
|
||
- [x] Stream status updates with reconnect/backoff semantics.
|
||
|
||
**Exit:** a new local installation can create one admin, authenticate securely, and view truthful host status end to end.
|
||
|
||
### Milestone 2 — Jobs, audit, and diagnostics
|
||
|
||
- [x] Implement the typed job registry, runner, timeouts, progress events, persistence, and restart recovery.
|
||
- [x] Add refresh-health, network-diagnostics, and sanitized-support-bundle jobs.
|
||
- [ ] Implement action-bound confirmation infrastructure for higher-risk future jobs.
|
||
- [ ] Build the jobs/history UI and audit timeline.
|
||
- [~] Add bounded output, central redaction, support-bundle manifest, and retention behavior. Output bounding, central redaction, and the support-bundle manifest are complete; retention remains pending.
|
||
|
||
**Exit:** the three initial jobs run only through the allowlist, expose live progress, persist safe results, and create attributable audit events.
|
||
|
||
### Milestone 3 — ADB transport and Android client
|
||
|
||
- [x] Implement one-device ADB configuration, connection/authorization state, timeouts, output bounds, and test fixtures.
|
||
- [x] Add allowlisted, read-only device identity and display collectors with per-field availability.
|
||
- [ ] Create the Kotlin project with minimum SDK 28 and landscape support.
|
||
- [ ] Implement URL setup/storage, trusted-origin navigation, and narrow cleartext policy.
|
||
- [ ] Implement WebView session behavior, soft keyboard, refresh, loading, and offline/error screens.
|
||
- [ ] Implement safe file chooser/download behavior where supported.
|
||
- [ ] Produce a debug APK in CI or a documented compatible Android build environment.
|
||
|
||
**Exit:** the Pi reports truthful ADB/head-unit connection state through read-only allowlisted collectors, and the app builds and can load/authenticate against the local dashboard in an emulator or test device; actual MG4 behavior remains explicitly unverified until hardware testing.
|
||
|
||
### Milestone 4 — Pi deployment
|
||
|
||
- [~] Add idempotent bootstrap, install-service, healthcheck, update, and uninstall scripts. Installation, health verification, and atomic updates are complete; uninstall remains pending.
|
||
- [x] Create a dedicated non-root user, `/var/lib/pi-car-companion` data directory, and `/etc/pi-car-companion` configuration directory.
|
||
- [~] Add hardened systemd unit(s), restart policy, graceful shutdown, and optional watchdog. Service hardening and restart behavior are complete; an optional watchdog remains pending.
|
||
- [~] Document LAN opt-in, firewall expectations, `mg4pi.local` through Avahi/mDNS, backups, updates, rollback, and recovery. LAN access, mDNS service discovery, updates, and release safety are documented; backup and recovery procedures remain pending.
|
||
- [ ] Test a clean install and upgrade on Debian arm64 or Raspberry Pi OS.
|
||
|
||
**Exit:** a fresh supported Pi image can be installed, rebooted, updated, and removed using documented commands without leaving an insecure default service.
|
||
|
||
### Milestone 5 — Artifacts and operational actions
|
||
|
||
- [ ] Implement controlled uploads/downloads/deletion, hashing, quotas, and metadata UI.
|
||
- [~] Add Wi-Fi refresh/reconnect with capability detection. A disabled-by-default dual-radio NetworkManager controller, 60-second recovery watchdog, fixed dashboard actions, hotspot credential configuration, and status UI are implemented; destructive failover and unplug/replug behavior still require staged Pi and MG4 validation.
|
||
- [ ] Add log pruning and companion-service restart jobs.
|
||
- [ ] Add reboot and shutdown with explicit action-bound confirmation and narrowly scoped privilege configuration.
|
||
- [ ] Exercise storage exhaustion, job timeout, service restart, and network-loss recovery.
|
||
|
||
**Exit:** operational features remain path-safe and allowlisted, destructive actions are hard to trigger accidentally, and failure modes are documented and tested.
|
||
|
||
### Milestone 6 — Vehicle validation and release
|
||
|
||
- [ ] Validate Pi boot, power-loss behavior, thermals, storage durability, Wi-Fi, and mDNS in the intended installation.
|
||
- [ ] Validate the Android app on the MG4: resolution, touch sizing, keyboard, scrolling, downloads, reconnect, and immersive-mode recovery.
|
||
- [ ] Run a parked-vehicle distraction/usability review; remove or gate flows unsuitable while driving.
|
||
- [ ] Complete security review, dependency audit, backup/restore drill, and release checklist.
|
||
- [ ] Clearly record tested hardware/software versions and all remaining limitations.
|
||
|
||
**Exit:** all release gates pass on the actual target hardware, documentation matches observed behavior, and no hardware-dependent claim exceeds the evidence.
|
||
|
||
## 7. Verification strategy
|
||
|
||
### Automated tests
|
||
|
||
- Authentication: first-admin race, password verification, session expiry/revocation, setup lockout, rate limiting
|
||
- Request security: CSRF rejection, origin checks, schema validation, authorization boundaries
|
||
- Jobs: unknown-job rejection, input validation, confirmation enforcement, timeout, redaction, persistence, restart recovery
|
||
- Artifacts: traversal and filename attacks, streaming size limits, executable rejection, SHA-256, authorization, deletion audit
|
||
- Status: parser/collector fixtures, unavailable fields, partial collection failures
|
||
- UI: setup/login, loading/error/reconnect, live updates, empty states, confirmation flows, and scroll behavior at target viewport sizes
|
||
- Android: URL validation, origin policy, navigation decisions, and lifecycle state where practical
|
||
- Deployment: shell linting plus idempotency checks in a disposable supported environment
|
||
|
||
### Required quality gates
|
||
|
||
Before merging a milestone:
|
||
|
||
```text
|
||
lint
|
||
typecheck
|
||
unit and integration tests
|
||
web production build
|
||
server production build
|
||
Android build (from Milestone 3 onward)
|
||
```
|
||
|
||
Security-sensitive changes also require focused negative tests. Commands and their actual results belong in release notes or CI; documentation must not claim a command passed unless it was run.
|
||
|
||
### Manual target checks
|
||
|
||
- 1920×720 layout at normal and enlarged font scale
|
||
- touch targets, focus order, soft keyboard, and scroll containment
|
||
- Pi temperature/status availability across supported kernels
|
||
- Wi-Fi disconnected/reconnected states
|
||
- service crash/restart and stale job recovery
|
||
- browser/app behavior during Pi reboot and network loss
|
||
- support-bundle inspection for secret leakage
|
||
- abrupt power loss and subsequent database integrity
|
||
|
||
## 8. Security invariants
|
||
|
||
These are release blockers, not optional enhancements:
|
||
|
||
- No default credentials or post-setup first-run endpoint
|
||
- No arbitrary command or arbitrary filesystem path accepted from a client
|
||
- No privileged action outside the server allowlist
|
||
- No destructive action without strong, action-specific confirmation and audit
|
||
- No secret-bearing data in logs, audit payloads, live events, or support bundles
|
||
- No LAN/public binding silently enabled by application defaults
|
||
- No broad root service; privileged host operations use narrowly scoped mechanisms
|
||
- No unauthenticated artifact or diagnostic access
|
||
- No unsupported claim of HTTPS security for a plain local HTTP deployment
|
||
- No vehicle-control or safety-system integration in this project scope
|
||
|
||
## 9. Configuration and operations
|
||
|
||
Configuration should be explicit, validated at startup, and documented in `.env.example`. At minimum it covers:
|
||
|
||
- listen host and port
|
||
- canonical dashboard origin
|
||
- database, artifact, and support-bundle locations
|
||
- session lifetime and secure-cookie mode
|
||
- upload limits and allowed content policy
|
||
- audit, job-output, and artifact retention
|
||
- log level and redaction-safe diagnostics mode
|
||
- optional mDNS and trusted-proxy behavior
|
||
|
||
Production secrets are generated during installation and stored outside the repository with restrictive permissions. Database and artifact backups must be consistent, restorable, and versioned with the application schema.
|
||
|
||
## 10. Definition of done for Version 1
|
||
|
||
Version 1 is complete only when:
|
||
|
||
- a clean Raspberry Pi installation can be completed from the documentation;
|
||
- the service runs unprivileged under systemd and survives reboot/restart;
|
||
- the first admin can be created exactly once and can log in/out securely;
|
||
- real status is visible without fabricated fallback data;
|
||
- the three initial jobs are allowlisted, validated, bounded, streamed, persisted, and audited;
|
||
- a sanitized support bundle can be generated and inspected;
|
||
- configured ADB device/display identity is read through bounded, read-only collectors with honest offline and unauthorized states;
|
||
- the web UI is usable at 1920×720 and handles connection failure honestly;
|
||
- the Android app builds and loads only its configured trusted origin;
|
||
- lint, typecheck, automated tests, server/web builds, and Android build pass;
|
||
- security and operations documentation is current;
|
||
- Pi/MG4-specific validation results are separated from emulator or desktop results.
|
||
|
||
## 11. Open decisions
|
||
|
||
Resolve these through short architecture decision records before their milestone begins:
|
||
|
||
1. Package manager/workspace tooling and lockfile policy
|
||
2. SQLite driver, migration mechanism, and backup approach
|
||
3. Session store and CSRF implementation
|
||
4. Server-Sent Events reconnect/replay contract
|
||
5. Safe privilege boundary for service restart, reboot, shutdown, and Wi-Fi operations
|
||
6. Android handling for local HTTP versus an optional locally trusted HTTPS setup
|
||
7. Pi power supply, graceful shutdown strategy, and storage medium for in-vehicle use
|
||
8. Whether the infotainment app can be installed and persist sessions under the MG4's Android restrictions
|
||
|
||
Until measured on target hardware, these remain decisions or risks—not assumptions disguised as completed functionality.
|