maps-and-more.md
Findings from the maps+more app
Source: de.volkswagen.mapsandmore, decompiled with jadx 1.5.6.
The car protocol lives in de.vwag.viwi.*. The app layer lives in vwg.vw.prerelease.app4entry.*.
TLS runs on wolfSSL through JNI.
Bluetooth
createRfcommSocketToServiceRecord(3728ED40-E416-11E4-80A0-0002A5D5C51B). Secure socket, bonded devices only.- 4 attempts with 2, 4, 6, 8 s back-off (
SPPConnectThread).
Tunnel (proxy/SPPConnection)
- Same framing as MIBBridge:
port u16le | type u8. DATA 0, CONNECT 1, CLOSE 2, HEARTBEAT 3. - The app sends a heartbeat (
00 00 03) every 5 s. It does not echo heartbeats. - It drops the link after 15 s without heartbeats from the car.
- The virtual port is any unique u16.
Registration (ViwiRegistrationProcess)
GET /car/info/vinon port 80 withConnection: close.POST /auth/registrationwithout a body. Expects 303 andcontent-location.- TLS 1.2
ECDHE-RSA-AES128-GCM-SHA256on port 443, thenGET <content-location>. The body isusername,password.
The app verifies the car certificate against a bundled root CA (TlsSocket.ROOT_CA, SSL_VERIFY_PEER):
CN=ViWi Certificate Authority, OU=EECF/1, O=Volkswagen GmbH Deutschland, L=Wolfsburg, ST=Niedersachsen, C=DE- Valid 2015-02-24 to 2055-02-14.
- SHA-256
E7:03:40:9C:26:FB:B7:3F:57:9C:EE:6E:0C:3E:63:D1:E5:1C:E5:29:E2:66:7A:57:F1:E6:95:CA:4B:78:F7:66
PIN (computePin), from the registration handshake key block:
digest = SHA-256(client_write_key || server_write_key) # 16 bytes each
t = little-endian uint32 of digest[0..4]
pin = t mod 1000000, left-padded to 6 digits
Session
- TLS 1.2
PSK-AES128-CBC-SHA256. Identity = username. Key = UTF-8 bytes of the password. - The car sends the VIN as the PSK identity hint.
- maps+more keeps one HTTP connection open. upApp does the same and reconnects once if the car closed it.
HTTP
- Paths have one leading and one trailing slash, for example
/chargingmanager/profiles/. - No custom headers. POST bodies are
text/plain; charset=UTF-8. - Bluetooth mode uses
127.0.0.1:4080and127.0.0.1:4443as hosts.
Charging manager writes
All writes are POSTs to the element uri. Updates send only the changed fields.
- Timer:
{"departureDate", "departureTime", "profile": "<profile id>", "cyclic", "weekdays"}. State alone:{"state": "scheduled" | "idle"}. - Profile: changed fields of
name, operations, maxCurrent, targetLevel, powerProvider, temperature, minLevel. Create:POST /chargingmanager/profiles/. Delete:DELETE <uri>. - Provider:
{"weekdays", "preferredTimeStart", "preferredTimeEnd"}. - Timer and provider times are UTC
HH:mm:ss. Only the time moves. Weekdays and dates are not shifted. climateExtSupplyin the first profile: "Allow AC and heating from battery" / "Klimatisieren mit Batteriestrom".- maxCurrent: 5, 10, 13, 16. 32 only for the e-up! 300: VIN not
WVW, or VIN[9] >=L. - Temperature: 15.5 to 30.0 °C in 0.5 steps.
- Names: must not be empty. The app has no length limit.
Other endpoints
/car/{info,batteries,ranges,engines,consumptions,distances,drivingstates,environments,gearboxes}//chargingmanager/{profiles,timers,providers,batteryCharges,batteryClimates,batteryPlugs}//media/collections/,/media/renderers/,/medialibrary/sources/,/mixer/audiosources/,/mixer/positioners- WebSocket
wss://host/over the PSK session. Subscribe with{"type":"subscribe","event":"<path>[?fields=a,b]#<sessionId>","interval":n,"updatelimit":n}. Server frames havetypesubscribe, unsubscribe or data.
Car test, 2026-10-07 (e-up! 2020, MIB2 Entry, Pixel 8a with Android 17)
- Bluetooth by service UUID, registration, PIN, certificate check and PSK session work.
- The car certificate is
CN=MIB2 Entry ViWi Server, valid 2015-02-24 to 2055-02-14. - Without ignition, all car values are empty strings and the profile and provider lists are empty. Timers are placeholders with empty fields.
- With ignition on, all data arrives.
batteryChargesalso hasmode(for exampleDC),remainingTime(minutes),targetSOC,isActive. batteryClimates.stateisabortedIgnitionOnwhile the ignition is on.- Normal profiles can contain
climateExtSupply. The options profile containscharge. - Profiles have no
powerProviderlink on this car. There is one provider per profile, without a visible link. - WebSocket: the car puts several JSON messages into one frame without a separator.
datais a JSON array, not a string. - WebSocket: subscriptions must be sent one at a time. Overlapping ones get
409 Conflict. - Element GETs are wrapped too:
{"status": "ok", "data": {...}}. POST answers{"status": "ok"}. - Writing a timer
state(scheduled/idle) works and the car sends a timer event.
Infotainment keys (/mechanicalinput/)
Subscribe to each element with updatelimit 100. With a higher limit the car merges pushed and released.
| Key | meaning |
|---|---|
| RADIO | radio |
| MEDIA | media |
| PHONE | phone |
| Soft key with flag icon | navigation |
| Soft key with gauge icon | thinkBlueTrainer |
| Soft key with car icon | car |
| Soft key with battery icon | energyflow |
| Soft key with search icon | search |
| MENU, X | no event |
The car also lists gauges, which has no key on this car.
Right Rotary Encoder (the right knob) sends ticks +1/-1 per click and pushed/released. Left Rotary Encoder (volume) was not tested.
- Off-peak times: maps+more has no off switch, only start and end. Start = end means off. The car uses
00:00:00–00:00:00UTC for unset entries. - The off-peak entry at the position of the options profile answers
403 Forbiddento POST. Only the location entries are writable. - Writing provider
weekdays, profile create and DELETE work on the car.