RE paperConfirmed

Reverse engineering paper #8 — what selects the cluster's navigation view

Simple, Small and Full. One vendor display mode picks which the instrument shows, it lives in a system setting, and it outlives the app that set it.

BYD LabVerified 8 min read

Settings.Global moet_extension_navi_display_mode Simple · mode 2 the instrument draws it — no surface Small · mode 3 a 640 × 440 card Full · mode 4 the full 1920 × 440 band closed · mode 1 stock cluster — the only way back

Our own app. Sealion 7 Pilot is developed by BYD Lab. It is held to the same standards as every other app on this site — read with that in mind.

Tested on

Vehicle
BYD Sealion 7 · Morocco
Head-unit version
51.1.4.2606220.1
Android
11
Verified

Once an app can talk to the instrument cluster at all (paper #7), the next question is what the cluster will actually draw. There are three answers, and the app is not the one choosing between them.

Question#

The Sealion 7’s cluster can show navigation three different ways — what are they, and how does the car decide which one is showing?

Environment#

Decompiled read-only with jadx 1.5.5 from ClusterCommService.apk (com.ts.appservice.cluster), ts-framework.jar and ts-androidauto-adapter.jar, all pulled off the unit. The mode naming comes from BYD’s domestic navigation classes — api.structs.com.neusoft.byd.ClusterViewMode, Byd8155fSystem, BydAutoNaviDevice — which are not installed on this export unit. The selection branch comes from NavigationProxy, in this unit’s own CarPlay proxy (com.ts.carplay.app). Probed on the car over adb with settings get global, dumpsys window windows and the app’s own on-device ring log, with the modes driven from our own app. Nothing here writes a vehicle property.

Observation#

The cluster’s navigation area has three shapes: a turn card drawn by the instrument itself, in its own style, with no map; a map card in the small navi area; a full-bleed map. The fourth state is the stock cluster, with no navigation at all.

Underneath, all four are a single integer. DataType 30 (NAVI_SEND_DISPLAY_MODE) carries one Bundle key, mode, and ClusterCommService writes its value straight into Settings.Global moet_extension_navi_display_mode. Any app on the unit can read that value; writing it goes through the service, which holds WRITE_SETTINGS.

Three naming systems describe the same four states, and none of them agrees with the others.

On the cluster Neusoft ClusterViewMode Display mode (DataType 30) Presentation state (DataType 31) Android surface
Simple TURN_BY_TURN_MODE (3) 2 7 none — the cluster’s own widget
Small SMALL_SCREEN_MODE (1) 3 5 Presentation on display 1, map card in the small navi area
Full LARGE_SCREEN_MODE (2) 4 6 Presentation on display 1, full-bleed map
(closed) CLOSE_SCREEN_MODE (4) 1 1 none — stock cluster

No arithmetic relates the enum ordinals to the display modes. The mapping is a table, read out of Byd8155fSystem’s insNavDisplayMode → ClusterViewMode conversion.

Evidence#

The second command is the one that opens a surface. DataType 31 (NAVI_SEND_PRESENTATION_STATE) is a {msgType: 3, subtype: 15, mapType: 2, state} bundle forwarded to QNX, where mapType 2 is the IVI/Android map source. Its state values are BydAutoNaviDevice’s MAP_STATE_* constants:

Log
1  NAVI_CLOSE
2  SMALL  close      5  SMALL  open
3  FULL   close      6  FULL   open
4  SIMPLE close      7  SIMPLE open

On the export unit that gives one open order for Simple, and it is the order our own app sends:

Log
sendCommand(16, {DataType: 33, mapType: 2})                                     request map focus
sendCommand(16, {DataType: 35, naviState: 1})                                   navi state START
sendCommand(16, {DataType: 30, mode: 2})                                        display mode = Simple
sendCommand(16, {DataType: 31, msgType: 3, subtype: 15, mapType: 2, state: 7})  presentation state

Interpretation#

The mode is not app state. It is car state — one integer the instrument and the head unit keep in sync, with no session, no handle and no owner.

That is the answer to “how does the car decide”. It decides by whatever was last written to moet_extension_navi_display_mode, from either end. The app writes it on DataType 30. The driver writes it by picking a navigation view from the cluster menu, which reaches the head unit as subtype 1 and lands in the same setting. On reconnect the service asks the instrument what the mode currently is and takes that answer. An app that wants to know what the cluster is showing does not ask a service — it reads a setting.

The consequence that bites is the missing reset. Nothing in the chain tracks the client — paper #7 takes that apart — so nothing clears the value when the process that set it disappears, which is how we found the unit sitting at 4 with nothing running. A cast has to override a stale mode rather than assume the cluster is at 1, and a clean release has to hand the cluster back by sending mode 1 explicitly.

One correction is worth stating, because we had it wrong first. Simple is not a map presentation at “sample state 7”. Simple is TURN_BY_TURN: a data-only feed to the cluster’s own gauge, with no Android surface. State 7 exists, and it is the state the app sends in Simple, but it is not a map mode — the states are a presentation channel, and the display mode is what decides whether there is anything to present.

Result#

Confirmed: the cluster’s three navigation views — Simple, Small and Full — are selected by a single vendor display mode, DataType 30 values 2, 3 and 4, mirrored into Settings.Global moet_extension_navi_display_mode in both directions and never reset when a navigation app dies. DataType 31 opens the surface for the two map modes; DataType 33 and 34 gate all of it behind map focus, mirrored the same way into moet_extension_navi_focus_map.

Three things follow for an app on this unit:

  • Read moet_extension_navi_display_mode before casting. It is the system’s current selection, it needs no permission to read, and it may well be stale.
  • Releasing means sending display mode 1: dismissing the Presentation and exiting leaves the navi area configured for a projection that is gone.
  • Simple needs no surface and no renderer, but it does need a feed — and that feed is a different endpoint entirely (paper #9).

Limitations#

One car, one market, one head-unit build, parked.

The device confirmation is for Simple. Display modes 3 and 4 are the values this unit carries for our small and big casts, but the walk that selects Simple → Small → Full in one pass and reads 2 / 3 / 4 back at each step has not been done.

The naming — the ClusterViewMode ordinals and the MAP_STATE_* constants — comes from BYD’s domestic navigation classes, which are not installed on this export unit. The export behaviour we measured is consistent with them, which is not the same as reading them off this car. The selection branch is read from NavigationProxy in this unit’s CarPlay proxy: code that ships here, but not code we watched run.

Two things in our own record are explicitly not confirmed on the car, and neither is presented here as observed:

  • The ContentObserver path during guidance. The service mirrors an instrument-initiated mode change into the setting, and our app registers an observer that is meant to follow a real selection (2 / 3 / 4) with a live switch. The device check for that — set the cluster from the instrument menu mid-route and watch the app follow — is pending; the head unit was offline when it was written.
  • Whether QNX composites an Android surface at all in display mode 2. The domestic analysis says TURN_BY_TURN has no surface. Whether the instrument would nevertheless composite a display-1 surface in that mode, and where, is a hypothesis we have not tested.

The driver-initiated change itself — cluster menu, subtype 1, the setting moves — is read from the service’s code. We have not sat in the car, picked a view from the instrument menu and watched the value change.

One drive saw an ADAS display fault after a mode change, cleared by a system reboot. Seen once, cause not established; nothing in the open or close sequence had changed in that build.

Reproduction#

With adb reachable on the car — the unit’s LAN address is written <car-ip> throughout:

Shell
adb connect <car-ip>:5555
adb shell settings get global moet_extension_navi_display_mode
adb shell settings get global moet_extension_navi_focus_map

Both are readable by any process, so this works with nothing installed. 1 or an unset value means no selection; 2, 3 and 4 are Simple, Small and Full. Take the first reading on a car nobody has cast to yet — that is the one that shows whether the value is stale.

To move it, an app has to bind the cluster service and send DataType 30; the protocol, the permissions and the bind are paper #7. Send the Simple open order above, read both settings back, then check the cluster display:

Shell
adb shell dumpsys window windows

In Simple there must be no Presentation of yours on display 1; in Small and Full there must be one. That check is what tells the three modes apart from outside the app. When you are done, send display mode 1.

ClusterCoreService also exposes a command-injection hook through its own dump (dumpsys activity service … <DataType> <msgType> <subtype> [k v]…), but it is gated to non-user builds; we drove every mode from our own app instead.