nisargjhaveri / WirelessAndroidAutoDongle

Use Wireless Android Auto with a car that supports only wired Android Auto using a Raspberry Pi.
MIT License
664 stars 73 forks source link

Compatibility with VAG head units #2

Closed BluemediaGER closed 8 months ago

BluemediaGER commented 1 year ago

Hi!

First of all, thanks for the cool project. I stumbled upon it after testing some commercial Android Auto wireless dongles, which all had some issues. Unfortunately, because these things are black boxes, it's difficult to debug them.

I have built the image for the Raspberry Pi 4, but can't get it to run properly. I have tested both with the head unit in my Skoda Fabia III Combi (LG MIB2 Entry device) and with the official Google Desktop Head Unit emulator. Sadly, in both cases it didn't work as expected.

The head unit in my car recognizes the default USB gadget and shows it as connected. When a device is connected, the head unit displays a selection window for Android Auto and MirrorLink, as it supports both protocols. As soon as I select Android Auto, it tries to connect but fails. The head unit then resets the USB connection and displays the selection menu again. This loop can be repeated several times.

The Google Desktop Head Unit simply doesn't do anything. When I start the tool and then connect the Raspberry Pi, nothing happens. It is recognized (number of devices increases from 7 to 8), but not started:

root@zeus:~/Android/Sdk/extras/google/auto$ ./desktop-head-unit -u -i touch
Android Auto - Desktop Head Unit
  Build: 2022-03-30-438482292
  Version: 2.0-linux

ALSA lib pcm.c:2666:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2666:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2666:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
[W]: No configuration specified - using default values.
[E]: Could not load configuration from '~/.android/headunit.ini'.
BoringSSL is the SSL implementation used in the receiver-lib.
Starting link. Requested protocol version: 1.7
[I]: Searching for compatible USB device...
[I]: Found 7 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 7 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 7 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 7 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 8 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 8 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 8 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 8 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 8 USB devices.
[E]: Couldn't find/access compatible USB device.
[E]: Failed to start Google Automotive Link.

My OnePlus 7 Pro on the other hand, is recognized without problems, restarted in accessory mode, and then works as expected:

root@zeus:~/Android/Sdk/extras/google/auto$ ./desktop-head-unit -u -i touch
Android Auto - Desktop Head Unit
  Build: 2022-03-30-438482292
  Version: 2.0-linux

ALSA lib pcm.c:2666:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2666:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2666:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
[W]: No configuration specified - using default values.
[E]: Could not load configuration from '~/.android/headunit.ini'.
BoringSSL is the SSL implementation used in the receiver-lib.
Starting link. Requested protocol version: 1.7
[I]: Searching for compatible USB device...
[I]: Found 7 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 7 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 7 USB devices.
[I]: No device found ready yet, will retry shortly...
[I]: Found 8 USB devices.
[I]: Device 'OnePlus GM1911' (vid=22d9, pid=2764) supports AOAPv2, starting accessory mode...
[I]: No device found ready yet, will retry shortly...
[I]: Found 8 USB devices.
[I]: Found device 'OnePlus GM1911' in accessory mode (vid=18d1, pid=2d00).
[I]: Found accessory: ifnum: 0, rd_ep: 129, wr_ep: 1
[I]: Attaching to USB device...
[I]: Attached!
> Phone reported protocol version 1.7
ssl state=TLS client read_server_hello -1
ssl state=TLS client process_change_cipher_spec -1
ssl state=SSL negotiation finished successfully 1
SSL version=TLSv1.2 Cipher name=ECDHE-RSA-AES128-GCM-SHA256
Verify returned: ok

I did a bit of debugging and it looks like in both cases there is simply no accessory start request arriving at the default USB gadget.

In further testing, I modified /etc/init.d/S92usb_gadget to directly start the accessory gadget instead of the default gadget. Additionally I modified the /root/start_accessory.sh so that aawgd is started directly when a uevent with USB_STATE=CONFIGURED arrives. With this configuration I at least managed to get the desktop head unit working. But the head unit in my car does not recognize the device at all.

Are there any tips I could use to debug further? My understanding is that the Pi must respond to USB packets according to AOA v1.0. However, I have not found any corresponding code on the default gadget that can do this. Is it possible that this is the problem?

BluemediaGER commented 1 year ago

Okay, never mind. The desktop head unit emulator does work. It just had a problem writing to the USB device the whole time. Will try to do some more research regarding the car head unit tomorrow.

nisargjhaveri commented 1 year ago

My understanding is that the Pi must respond to USB packets according to AOA v1.0. However, I have not found any corresponding code on the default gadget that can do this. Is it possible that this is the problem?

This is being handled by the kernel patch which also adds the accessory gadget driver and handles the control requests as required. See aa_wireless_dongle/patches/linux/0001-Backport-and-apply-patches-for-Android-Accessory-mod.patch

BluemediaGER commented 1 year ago

I've spent the last few days digging into why this isn't working with my head unit. In the end, it comes down to a few points:

  1. My head unit seems to expect a MTP device. If the connected device isn't one, switching to accessory mode doesn't work.

  2. Apparently, it's a problem to use the GadgetFS vendor and product IDs. Even with the emulation of an MTP device, switching to accessory mode does not work properly. If I take the IDs and strings from my OnePlus 7 Pro instead, it works fine.

  3. aawgd is too slow. After the head unit has requested the switch to accessory mode, there is a timeout (around 6 seconds) within which communication with Android Auto must be established. Otherwise, the USB connection is reset. It simply takes too long to establish a Bluetooth connection with the smartphone and start Android Auto.

I forked the repository and fixed all these issues. With that, it now works relatively reliably for me. Getting the timing right is the hardest part; I had to build some sketchy workarounds for that, which may break some other devices. If you're interested in adopting some of the changes I've made, I'd be happy to open a pull request.

Maybe doing more work in the aawgd daemon would help with the timing problems (like switching between modes and enabling the default gadget). But my C++ knowledge is too bad in order to implement that properly :D

nisargjhaveri commented 1 year ago

Thanks a lot for the effort and sharing! :)

MTP, yes, I had read somewhere that this might happen, but wasn't a problem with the headunits I tested. But we can change the default USB config to MTP, if this is the case.

Apparently, it's a problem to use the GadgetFS vendor and product IDs. Even with the emulation of an MTP device, switching to accessory mode does not work properly. If I take the IDs and strings from my OnePlus 7 Pro instead, it works fine.

This is very interesting. Does the headunit have whitelisted/blacklisted vendor and product IDs? By any chance was the "0" serial number a problem?

aawgd is too slow. After the head unit has requested the switch to accessory mode, there is a timeout (around 6 seconds) within which communication with Android Auto must be established. Otherwise, the USB connection is reset. It simply takes too long to establish a Bluetooth connection with the smartphone and start Android Auto.

Right, this is a tricky one to fix. Sure we can start USB after bluetooth connection, but even then we can't be sure that Wifi would connect within the timeout. Starting USB after both bluetooth and wifi is connected might work, but that would mean that even if we plug in the dongle to something other than a car, we'd still advertise and connect to a phone.

Other workaround could be, instead of waiting for the bluetooth and wifi connections, we start the AA communication and then start forwarding the connection when phone is available. This would be more work, and I was avoiding this till now to not mess with the actual communication between the headunit and the phone.

Maybe doing more work in the aawgd daemon would help with the timing problems (like switching between modes and enabling the default gadget).

Yes, this is a long pending improvement in my mind. We can definitely do this, might also help with retrying connections in case of failures and other things in future.

Happy to take in PRs for all of these improvements. Though as you said, some parts may be tricky and we might need to come up with the right solutions. :)

BluemediaGER commented 1 year ago

This is very interesting. Does the headunit have whitelisted/blacklisted vendor and product IDs? By any chance was the "0" serial number a problem?

That's actually a good question. I didn't explicitly test it with a different serial number but simply copied the details from my phone. I'll test this again when I have time.

Right, this is a tricky one to fix. Sure we can start USB after bluetooth connection, but even then we can't be sure that Wifi would connect within the timeout. Starting USB after both bluetooth and wifi is connected might work, but that would mean that even if we plug in the dongle to something other than a car, we'd still advertise and connect to a phone.

I hacked aawgd so that the default gadget is enabled as soon as the proxy has accepted a client (see here). This indeed causes the connection to the phone to be established even if no car is connected, but that is acceptable in my case.

Happy to take in PRs for all of these improvements. Though as you said, some parts may be tricky and we might need to come up with the right solutions. :)

I filed #3 and #4. All the other changes are too hacky for me to merge them here :D

Thanks again for the cool project. Since I've fixed all the issues for my particular case and the project now works that way for me, we're welcome to close this issue if you'd like. Otherwise, we can leave it open in case someone else wants to contribute to it :)

rareshornet commented 11 months ago

Hi guys! Amazing work, thanks for enabling my car to do wireless AA!

For my car/phone combo(suzuki scross 2022+pixel 6) definitely the MTP implementation works quite reliably using a pi zero w. I do have a weird issue, if I delete/forget the bluetooth AA device from my pixel 6 then I can never reconnect it unless I reimage the sdcard. The error I get on my phone at least is that the pin number is incorrect although no pin prompt shows up. Not a huge issue as I don't have to forget the AA device anyway but if you want to take a look let me know if I can help in any way, collect logs etc. I did test with the original release build and also from a self build of @BluemediaGER master repo.

Cheers guys!

nisargjhaveri commented 11 months ago

I do have a weird issue, if I delete/forget the bluetooth AA device from my pixel 6 then I can never reconnect it unless I reimage the sdcard. The error I get on my phone at least is that the pin number is incorrect although no pin prompt shows up. Not a huge issue as I don't have to forget the AA device anyway but if you want to take a look let me know if I can help in any way, collect logs etc. I did test with the original release build and also from a self build of @BluemediaGER master repo.

Could you please create another issue for this? I also faced this while testing, and would like if we can find a fix!

rareshornet commented 11 months ago

Ah looks like you beat me up to it. Thanks for fixing that.

nisargjhaveri commented 11 months ago

Maybe doing more work in the aawgd daemon would help with the timing problems (like switching between modes and enabling the default gadget).

I've moved enabling the gadget to aawgd in de8f94961e1595fe223bc62a1aa18275f234937d. This should make it much easier and cleaner to switch between modes after accepting the tcp client by moving UsbManager::instance().enableDefaultAndWaitForAccessroy(); call. We can maybe do this behind a config as well (we need some way to easily set the config first, but we can use env variables if nothing else to start with.)

BluemediaGER commented 11 months ago

I've moved enabling the gadget to aawgd in https://github.com/nisargjhaveri/AAWirelessDongle/commit/de8f94961e1595fe223bc62a1aa18275f234937d. This should make it much easier and cleaner to switch between modes after accepting the tcp client by moving UsbManager::instance().enableDefaultAndWaitForAccessroy(); call.

Nice; that's a good step forward. Thanks for your work!


I just performed some more testing with my head unit and want to share the results:

Timing is unfortunately still a problem. Even with the changes from https://github.com/nisargjhaveri/AAWirelessDongle/commit/de8f94961e1595fe223bc62a1aa18275f234937d, it's like two seconds too slow.

Other workaround could be, instead of waiting for the bluetooth and wifi connections, we start the AA communication and then start forwarding the connection when phone is available. This would be more work, and I was avoiding this till now to not mess with the actual communication between the headunit and the phone.

I think this will be the only option that reliably resolves these timing issues. I don't know the details about the AA protocol, but may it be possible to just perform the handshake and send some dummy data to the head unit while waiting for the phone to connect? That would at least avoid the need for a full-blown protocol implementation.

Apparently, it's a problem to use the GadgetFS vendor and product IDs. Even with the emulation of an MTP device, switching to accessory mode does not work properly. If I take the IDs and strings from my OnePlus 7 Pro instead, it works fine.

This is very interesting. Does the headunit have whitelisted/blacklisted vendor and product IDs? By any chance was the "0" serial number a problem?

I did test that today, and I can confidently say that it wasn't the serial number. Even with a non-zero serial number and the MTP change, switching to accessory mode doesn't work. I also observed that some of the cheap commercial dongles get recognized as Xiaomi phones. I still don't know if it's a white or blacklist (my guess would be a blacklist), but faking IDs seems necessary for some head units.

nisargjhaveri commented 11 months ago

Timing is unfortunately still a problem. Even with the changes from https://github.com/nisargjhaveri/AAWirelessDongle/commit/de8f94961e1595fe223bc62a1aa18275f234937d, it's like two seconds too slow.

Just so that I understand correctly, timing is still a problem with your fork as well, or just with the master with the new changes?

I think this will be the only option that reliably resolves these timing issues. I don't know the details about the AA protocol, but may it be possible to just perform the handshake and send some dummy data to the head unit while waiting for the phone to connect? That would at least avoid the need for a full-blown protocol implementation.

We could try this out. Needs a little play around, but I believe the first message in the communication is essentially exchanging version info, and we might be able to fake it.

We could also once try to delay enabling the accessory gadget after getting the accessory start request. On Android, there seems to be a mechanism to choose apps if multiple apps can handle a particular accessory, which can take time as it requires user action. Not sure but maybe the head unit might have larger timeout there?

BluemediaGER commented 11 months ago

Just so that I understand correctly, timing is still a problem with your fork as well, or just with the master with the new changes?

With both versions. My fork works often, but not always. The main problem with it is that the USB port in my car gets power as soon as I unlock the doors. If I don't turn on the head unit fast enough (for example, because I load something into the trunk first), I have to restart the Pi manually because the auto-established connection has already timed out. It's not a solution, but more of a hacky workaround to make it work at all.

We could also once try to delay enabling the accessory gadget after getting the accessory start request. On Android, there seems to be a mechanism to choose apps if multiple apps can handle a particular accessory, which can take time as it requires user action. Not sure but maybe the head unit might have larger timeout there?

This is an interesting idea that I will definitely test. I'm not getting my hopes up too high, though. It seems to me that the timeout is for the time between the request for accessory mode and the point where AA communication is established.

nu00 commented 11 months ago

what if, when the rpi boot , it starts android auto showing a black screen (or a message saying something like "Waiting for phone"), and then wait for the phone and when it's ready start showing the real android auto on the phone? I think that this way probably it can also fix #7. I take this idea from an Airplay Dongle I had, probably it's for the same issue (although it's a different protocol...)

BluemediaGER commented 11 months ago

That would be the perfect solution, but it requires a full protocol implementation and is much more complex, than simply proxying the connection to the phone. The Pi would also have to do more processing, since it needs to re-encrypt the traffic (Android Auto uses TLS).

nisargjhaveri commented 11 months ago

With both versions. My fork works often, but not always. The main problem with it is that the USB port in my car gets power as soon as I unlock the doors. If I don't turn on the head unit fast enough (for example, because I load something into the trunk first), I have to restart the Pi manually because the auto-established connection has already timed out. It's not a solution, but more of a hacky workaround to make it work at all.

What if we trigger the bluetooth connection on first accessory start request, and then restart the gadget when when the phone is available? Does that work by any change?

nu00 commented 11 months ago

That would be the perfect solution, but it requires a full protocol implementation and is much more complex, than simply proxying the connection to the phone. The Pi would also have to do more processing, since it needs to re-encrypt the traffic (Android Auto uses TLS).

Maybe we can take some "inspiration" from here... https://github.com/adrianalin/meta-aacs or here https://github.com/tomasz-grobelny/AACS

gamelaster commented 11 months ago

Doing "proxy" is not that hard, the hard part are certificates. Headunits check if certificate sent by phone is signed by Google. We can dump such certificate from APK, but always it will have only few months lifetime, so every few months, it will be required to upload new dumped certificate. Also, right now, dumping the cert from APK is possible (it's not easiest way, but not great, not terrible), but Google can anytime change this and this will be not possible / very hard to do.

The idea about gateway making directly the connection to Headunit and then waiting for phone is real (if we have certs mentioned upper), Headunit don't need much info about phone, and phone tells when really start Android Auto (but Headunit can request it), so we can wait until real phone will connect to gateway.

Although, this way introduces to many challenges, and by my opinion, the current method is the best one.

BluemediaGER commented 11 months ago

What if we trigger the bluetooth connection on first accessory start request, and then restart the gadget when when the phone is available? Does that work by any change?

That may work, but probably not in my case.

My head unit shows a selection menu if you connect a device to it. It allows you to select between Android Auto and MirrorLink (this video shows how that looks). The problem is that the accessory start request is only sent after the Android Auto button has been pressed. The head unit remembers if the connected device was last used with Android Auto and will start it automatically the next time. But if the connection fails for some reason (e.g. because of a timeout), it will not start AA automatically until you have manually selected it again.

There must be some mechanism to force-start Android Auto on the head unit, however. I observed that with the cheap dongles from Amazon, which somehow started Android Auto on their own every time I connected them. But my problem with these was that the whole head unit crashed every time, about 3 seconds after AA was started. That was the main reason for me to start fiddling around with your project :)

gamelaster commented 11 months ago

There must be some mechanism to force-start Android Auto on the head unit, however.

As far as my knowledge reaches, there is no way for phone to force AA session. Only thing which might be the "force" is that the phone is already in accessory mode. To confirm this theory, please connect cheap dongle from Amazon to PC and send the lsusb here.

(From older comment):

After the head unit has requested the switch to accessory mode, there is a timeout (around 6 seconds) within which communication with Android Auto must be established

This is strange, this is what headunits should not do...

About the handshake, by my opinion, this can be best in terms of timing: 1) Phone connects to BT 2) RPi starts gadget mode, and waits for HU to tell it to switch to accessory mode 3) When accessory is started, RPi will send via RFCOMM info about where to connect etc. 4) Done!

Only issue here is that it might take some time until the phone will really connect to wifi and try to connect to server. This can be fixed by sending RFCOMM command to phone when the RPi starts classic device gadget mode, and if phone will connect sooner than accessory mode is ready (doubt), it will cache the packet which phone/headunit wanted to send, and then it will send it when both parties are ready.

BluemediaGER commented 11 months ago

As far as my knowledge reaches, there is no way for phone to force AA session. Only thing which might be the "force" is that the phone is already in accessory mode. To confirm this theory, please connect cheap dongle from Amazon to PC and send the lsusb here.

Unfortunately, I have already returned them, as they did not work for me. However, I did take a look at their behavior beforehand and they do not start directly in accessory mode. As mentioned in a previous comment, they first appear as a Xiaomi phone in MTP mode. I also tried to Man-in-the-middle the entire USB communication with Wireshark, but unfortunately I couldn't really get that to work due to the lack of proper tooling.

This is strange, this is what headunits should not do...

But it does, unfortunately. I would simply say that Volkswagen doesn't have the best software quality if even a connected USB device is able to crash the entire infontainment system lol

About the handshake, by my opinion, this can be best in terms of timing:

  1. Phone connects to BT
  2. RPi starts gadget mode, and waits for HU to tell it to switch to accessory mode
  3. When accessory is started, RPi will send via RFCOMM info about where to connect etc.
  4. Done!

I'll try to hack that together. Currently it is only about 1 to 1.5 seconds too slow.

gamelaster commented 11 months ago

But it does, unfortunately. I would simply say that Volkswagen doesn't have the best software quality if even a connected USB device is able to crash the entire infontainment system lol

Google have strict AA certification procedure, so I am surprised that this passed it :D

BluemediaGER commented 11 months ago

We could also once try to delay enabling the accessory gadget after getting the accessory start request. On Android, there seems to be a mechanism to choose apps if multiple apps can handle a particular accessory, which can take time as it requires user action. Not sure but maybe the head unit might have larger timeout there?

@nisargjhaveri That idea is on point! I moved the UsbManager::instance().switchToAccessoryGadget() call from the uevent handler to the proxy handlers handleClient() function. The head unit waits a long time (more than 30 seconds) for the "phone" to switch to accessory mode. As a result, it now works 100% reliable.

Currently the default gadget stays active until the accesory gadget is started, because the switchToAccessoryGadget() call also handles the deactivation of the default gadget. This is probably not ideal yet, but works anyway.

Edit: I'm also using your device_id branch. It works exactly the same as with my own USB IDs.

nisargjhaveri commented 11 months ago

Good to know that this worked! Though, not sure if this will this will work for everyone. Let me try once and we can see how we can merge this in main, now that we do the switching part in aawgd itself.

nisargjhaveri commented 10 months ago

@BluemediaGER if you're still up for some more experimentation, check out the builds from #25. It might help with the connection timeout issue as well, without delaying the switchToAccessoryGadget call.

BluemediaGER commented 10 months ago

@nisargjhaveri Thanks for the ping, that looks awesome! I will definitely test that when I have time.

cosminmocan commented 9 months ago

Hello, and thank you for the awesome project, tried it for a present, for one of my friends, it didn't worked almost at all unfortunately :( My friends car is a Skoda octavia 3, has the big discovery pro(mib2.5, no knob) , my s23 ultra, worked on and off, his s22 didnt worked at all, I tried build 25 as specified above, and the the main build, 25 had a bigger success rate, which was not really reliable. Afterwards i tried on my fathers car(passat b8) with a discovery media(mib 2) that one did not work at all, tried my phone, and a oneplus 7t, tried on all usb ports, and without anything else plugged inside.

The device I used is a raspberry pi zero 2w Also, the device works flawlessly when it use it with the android auto emulator My question is, how should I debug it, as the project is great, and would you guys be interested in starting a little wiki, of working cars + phones combo ? I'm guessing we could maybe find how to make it work better, in a faster way

Regards, happy new year, and all the respect to this awesome awesome project

nisargjhaveri commented 9 months ago

@cosminmocan, you can ssh into the Pi after it tries to connect to the car and get some logs out. You can start with /var/log/messages.

The default wifi password is "ConnectAAWirelessDongle" as defined in hostapd.conf. You can ssh using root/password for now, defined in relevant defconfigs, e.g. raspberrypi0w_defconfig.

cosminmocan commented 9 months ago

Great, will be doing that as soon as i get a hold of another vag car ! Thanks again man, truly outstanding work you have done here

cosminmocan commented 8 months ago

Just wanted to let everyone know, the optimistic_phone_connection branch worked for all the vag vehicles that I tested which are:

An outstanding piece of work this whole project! EDIT: the hardware that i used is a raspi zero 2w and I have noticed that the cable is also very important in the stability of the system, the cable that worked for my is the one from my bluetooth headset (link)

nisargjhaveri commented 8 months ago

35 has been merged. The latest builds should work on more head units. Thanks everyone for the contributions.