nisargjhaveri / WirelessAndroidAutoDongle

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

Restart gadget on TCP failure (disconnect) or hostapd disconnect #26

Closed hkfuertes closed 3 months ago

hkfuertes commented 7 months ago

Hello Again, Just proposing an enhancement. Sometimes that I take the car for short rides... and I hop on and hop off rather quickly, my car infosystem does not turn off completely and therefore the PI is still on when I come back, but my Wifi is disconnected, so I need to unplug and replug the pi to re-connect.

Suggestion: a) Catch any TCP exception and trigger a gadget restart (efectively restarting everything), well or in fact doing nothing so that the next loop of the infinite loop comes... b) Externalize gadget restart functionality into a binary and trigger it via hostapd_cli (https://superuser.com/questions/1071354/hostapd-execute-a-command-when-there-is-new-connection-established) c) ... any other ideas ...?

I will try to play with it this weekend to see if I can propose a PR. Thank you again for this amazing project!

hkfuertes commented 7 months ago

I think I have an outline, I will test this this evening... https://github.com/hkfuertes/AAWirelessDongle/pull/1

I have not done any C++ ever before, thats why I prefered to show you the 'draft' in my own repo, if this makes any sense (or if it works this evening) I will do a proper PR on this repo.

I would love to just use should_exit here, but the signature for signal should be void [](int) and its complaining when I want to share a variable between scopes... So I had to use a global variable... (which I dislike)

    std::signal(SIGTERM, [](int signum){
        running.store(false);
    });

My ideas are: a) Capture SIGTERM so that when received, the proxy ends and the next loop (while true) comes, effectively restarting everything. Then the postup in network/intefaces will launch hostapd_cli with an script that will send SIGTERM on client disconnect from wifi. b) Don't do anything on C++ side, just call init.d's restart usb gadget via hostapd_cli(like before), and let aawgd handle the usb exception, as @nisargjhaveri you said that that part was well controlled.

I will test this this evening or during the weekend, but any idea or refactoring will be welcome :)

Thank you all!

nisargjhaveri commented 7 months ago

Sorry for delay in reply. I see that the latest change you have is essentially restarting aawgd on hostapd disconnect. We'd need some more changes, especially retrying connection if you're out of range initially for it to work better for your scenario as well. Though, since this is tied to hostapd, it won't handle other TCP failures, and might end up disconnecting the AA session even if some other client disconnects (for example while debugging).

Another way to go about this might be to handle TCP errors directly in aawg. Unfortunately, TCP doesn't do error well. But we can have something like 30 second timeout. If we don't receive anything from TCP for 30 seconds, we reset the gadget and retry. We'd still need the changes to retry initial connection and bunch of other changes. Though I believe something like this would be in a better direction and would help other scenarios as well overall.

I have very initial changes for this direction in interruptible_tcp branch. Needs some more work, though, it'll enable us to cancel TCP read/write better and allow timeouts etc. in future.

hkfuertes commented 7 months ago

Sorry for my late response, please dismiss my pr, I don't know how to follow... (how do you test this? do you develop directly on the pi?)

Anyway, I tested the just-restart-gadget-on-hostapd-change idea but it did not work, as for some reason it restarted upon connection, not on disconnection... so I tried (with no luck) another aproach, that maybe you can explore.

  1. Set the hostapd_cli -a <script> daemon, but instead of restarting usb gadget service, it will signal aawgd (ie: kill -15 (cat var/run/aawg.pid))
  2. Trap the signal in the proxy thread to change the should_exit variable...

The main drawback that I see, is that should_exit would need to be global... or we could refactor the proxy into a class, so it can be a class variable (although I don't know how to do this in c++)

I have very initial changes for this direction in interruptible_tcp branch. Needs some more work, though, it'll enable us to cancel TCP read/write better and allow timeouts etc. in future.

I saw your branch, and I saw the retrials, but its not doing anything yet, right? I mean reacting to those retrials... Good one!... does c++ have exceptions? isn't the socket throwing any, I guess that if you disconnect from wifi, the socket would die, right? (it makes sense to me, hahahaha)

nisargjhaveri commented 7 months ago

The main drawback that I see, is that should_exit would need to be global... or we could refactor the proxy into a class, so it can be a class variable (although I don't know how to do this in c++)

should_exit can be easily made member of the AAWProxy class in proxyHandler. It might help avoid global and still allow us to change the value from external callback.

I saw your branch, and I saw the retrials, but its not doing anything yet, right? I mean reacting to those retrials... Good one!... does c++ have exceptions? isn't the socket throwing any, I guess that if you disconnect from wifi, the socket would die, right? (it makes sense to me, hahahaha)

When I tested, if the wifi is disconnected, the socket just hangs there indefinitely without any errors as the TCP was not properly closed. The solution there would be to timeout if we don't read anything for let say 30 seconds and reset connection (this is not yet part of the branch).

Ioniq3 commented 5 months ago

I made one solution but it depends on external program control that i used with the gpio for some extras, so it doesn't have direct merge with the main of the proyect.

In proxyHandler.cpp must be added a second set (after the first) of configuration for the socket; when pass 30 seconds without connection it reset and the code can return to the main while to start again.

struct timeval tv = {
    .tv_sec = 30
};

if (setsockopt(server_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) { Logger::instance()->info("setsockopt failed timeout set: %s\n", strerror(errno)); return std::nullopt; }

admiralspeedy commented 4 months ago

I've been trying different things this weekend and simply setting a timeout on the client socket is enough to fix half the problem because your code already breaks out of the forwarding loop if the read length is less than 0.

I added this to handleClient in proxyHandler, right after the server socket is closed:

    struct timeval tv = {
        .tv_sec = 10
    };

    if (setsockopt(m_tcp_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
        Logger::instance()->info("setsockopt failed: %s\n", strerror(errno));
        return;
    }

In Ioniq3's pull request, I didn't understand why he was setting a timeout on the server socket, since it gets closed after the client connects and also his loop in handleClient does nothing. I'm pretty sure for that loop to do anything, the dongle would have to lose connection to the phone precisely as the phone had just connected, which is not really possible.

Setting the timeout for the client socket causes the forwarding loop to break after 10 seconds (or whatever you choose as a timeout, I have tried all the way down to 3 seconds without a problem) of the phone being disconnected from the dongle's network and the main aawgd loop starts over and it reconnects perfectly every time for me.

A couple issues that still exist though are that this does not handle lost connections from the head unit because I have no idea how to check if a connection to the head unit has been lost (the /dev/usb_accessory path always exists and even if the USB connection is lost, read does not fail).

A USB connection loss is possible in three scenarios that I know of:

  1. You shut the car off long enough for the head unit to power down, but the USB ports in the vehicle remain powered long enough for you to restart the car and have the head unit come back on. This happens in my car because Ford decided to sometimes arbitrarily keep the USB ports powered on for ~10 minutes after shutting the car off, and sometimes I shut it off, run into the house to grab something, come back and the dongle hasn't actually powered down before I restart the car.
  2. You have a bad/damaged USB cable that loses the data connection temporarily but continues to supply power to the dongle.
  3. You go into settings on your head unit and disable Android Auto, and then re-enable it while using the dongle. Not sure why anyone would do this, but it's the only other scenario I can think of where the dongle could lose connection but remain powered.

In all three scenarios, the dongle gets stuck in a state where it thinks it's still connected to Android Auto on the head unit and the only way to fix it is to reboot the dongle.

Last thing of note is that the timeout I added to client socket does not work properly with the new dongle mode connection strategy. I suspect it is because when the main loop loops again after the timeout, the BLE advertisement doesn't happen again since it only happens during the BT device init call, which is outside of the loop. I have moved that into the loop to see what happens and I am about to go out and test it in a few minutes (I can't test dongle mode on my PC, it refuses to work with the head unit emulator). The only thing I don't really understand is the stopAdvertisement function is never called as far as I can tell, so I'm not sure why we would have to start advertising again?

admiralspeedy commented 4 months ago

I was able to get Dongle Mode working with the head unit emulator.

I moved the timeout to after the USB device is connected in proxyHandler since sometimes the TCP socket would sometimes timeout while waiting for a USB device, so we can avoid that by setting it after.

I still can't figure out why though the timeout doesn't work properly with Dongle Mode. I get the following error when the loop starts over after the timeout:

Jan  1 00:01:12 buildroot user.info aawgd[217]: Forwarding stopped
Jan  1 00:01:12 buildroot daemon.err bluetoothd[130]: src/adv_monitor.c:btd_adv_monitor_power_down() Unexpected NULL btd_adv_monitor_manager object upon power down
Jan  1 00:01:12 buildroot daemon.err bluetoothd[130]: src/profile.c:ext_io_disconnected() Unable to get io data for AA Wireless: getpeername: Transport endpoint is not connected (107)
Jan  1 00:01:12 buildroot daemon.err bluetoothd[130]: src/advertising.c:add_client_complete() Failed to add advertisement: Rejected (0x0b)
Jan  1 00:01:12 buildroot user.info aawgd[217]: Bluetooth adapter was powered off
Jan  1 00:01:12 buildroot user.info aawgd[217]: USB Manager: Disabled all USB gadgets
Jan  1 00:01:13 buildroot kern.info kernel: [   72.998686] android_work: sent uevent USB_STATE=DISCONNECTED
Jan  1 00:01:13 buildroot kern.info kernel: [   73.001581] android_work: did not send uevent (0 0 00000000)

There are some Bluetooth errors in there, They only happen in Dongle Mode and after the timeout the phone will not connect to the dongle via Bluetooth again until I reboot the dongle (if I try to connect manually my phone gives me a "Failed to communicate" error after a few seconds of trying).

If I switch back to Phone First the timeout works fine but Dongle Mode is more reliable in my car so I want to use it preferably. To circumvent this issue for the moment, I just forced the dongle to reboot after the timeout which is obviously not the most graceful way to do it, but it works.

nisargjhaveri commented 4 months ago

I was able to get Dongle Mode working with the head unit emulator.

Curious, how did you get it working? This would be really useful.

The only thing I don't really understand is the stopAdvertisement function is never called as far as I can tell, so I'm not sure why we would have to start advertising again?

Currently we're not stopping the advertisement, the function is never called as you noticed.

We turn off the bluetooth completely for a couple of seconds before restarting the connection. Not sure I'd this is really needed in dongle mode, we might be able to change this in dongle mode. In any case, if we do turn off, maybe we can try stopping advertisement and starring again in case that is the issue.

A couple issues that still exist though are that this does not handle lost connections from the head unit because I have no idea how to check if a connection to the head unit has been lost (the /dev/usb_accessory path always exists and even if the USB connection is lost, read does not fail).

Yes, this is the tricky part. The usb accessory driver we're using is from very old, almost unmaintained android source code. It does not support poll/select/async io to effectively timeout or cancel. I was surprised that tcp timeout alone is working for you, because I suspected that even if tcp times out, the loop still wouldn't break as it'll keep waiting on usb read.

Interesting progress though!

admiralspeedy commented 4 months ago

Curious, how did you get it working? This would be really useful.

Well, I primarily use Windows 11 and I use WSL2 to work on this and other projects and for whatever reason I can't get the emulator to work over USB with my phone. I have installed the WinUSB driver with Zadig for my Pixel 7 and once the emulator detects the phone, it switches to accessory mode and the connection drops out to an unknown device and refuses to connect (I also tried installing the WinUSB driver to the unknown device but it still doesn't work).

As you know, for dongle mode to work you have to have made a connection to the head unit one time so it shows up in your previously connected cars before dongle mode works, so what I did was I used the dongle with a build in Phone First mode, connected to the emulator so that the emulator (listed as "Google") was added to my list of previously connected cars, then I disconnected the dongle, swapped the SD card for one with a build in Dongle mode, reconnected the dongle, forgot the old dongle Bluetooth device on my phone, connected to the new dongle Bluetooth device, and then started the emulator and it works now because the "Google" head unit has previously been associated.

If you can just get it to work via USB with your phone directly once, dongle mode should work as normal.

Currently we're not stopping the advertisement, the function is never called as you noticed.

We turn off the bluetooth completely for a couple of seconds before restarting the connection. Not sure I'd this is really needed in dongle mode, we might be able to change this in dongle mode. In any case, if we do turn off, maybe we can try stopping advertisement and starring again in case that is the issue.

I am going to try this tonight.

I was surprised that tcp timeout alone is working for you, because I suspected that even if tcp times out, the loop still wouldn't break as it'll keep waiting on usb read.

Well, the USB and TCP reading are done on separate threads so when TCP times out, the read fails (len == -1) which breaks out of the loop and sets should_exit to true. Since should_exit is set to true, the USB loop in the other thread stops as well, the thread join calls both return and the forwarding function finishes, which means the proxy thread dies and the main loop starts over.

admiralspeedy commented 4 months ago

I made startAdvertising and stopAdvertising public, moved the startAdvertising call out of the Bluetooth handler's init call and into the main loop. I also added a call to stopAdvertising before the powerOff call to see if it would correct the errors I posted above but it doesn't, the same thing happens so I suspect there is an error in the advertising code somewhere.

I am also facing some issues with inconsistency using Dongle mode. Sometimes the dongle gets stuck in a weird state where my phone repeatedly connects and disconnects from the dongle's network over and over until I reboot the dongle.

I think Dongle mode needs more work and truthfully I don't even understand how it works. Based on the description in the pull request it waits for the car/head unit BT connection and dongle's BT connection, but unless I'm completely blind I do not see any code that waits for the cars BT connections (unless this is inherent in the dongle implementation and handled by AA and the car) and I also don't understand what impact it has on call audio routing (sounds exactly the same to me).

I'm going to go back to Phone First mode for a bit and see if the timeout was enough to fix my issues with that because I'm also now experiencing issues with using Google Assistant where it almost never fully understands what I've said while driving and it only happened since switching to Dongle mode (it almost seems as if it's not using the car's mic but rather the phone's mic in my pocket for some reason).

nisargjhaveri commented 4 months ago

Based on the description in the pull request it waits for the car/head unit BT connection and dongle's BT connection, but unless I'm completely blind I do not see any code that waits for the cars BT connections (unless this is inherent in the dongle implementation and handled by AA and the car)

Yes, this is natively handled by the AA in the phone. It connecta to the dongle via TCP only when it also detects the headunit bluetooth connected.

... I also don't understand what impact it has on call audio routing (sounds exactly the same to me).

This part is slightly more nuanced. Earlier we had a dummy handset profile to trick the phone. When we connect, the phone would have two hsp profiles. One from the headunit and the dummy one from the dongle. If phone routes call to the dongle, it doesn't work. This is the reason we turned off bluetooth just after connection. But even then it might be confusing some phones. With dongle mode, we don't need the dummy profile, which should make the call routing to headunit somewhat more reliable.

I'm going to go back to Phone First mode for a bit and see if the timeout was enough to fix my issues with that

That is certainly a good start.

I'm also now experiencing issues with using Google Assistant where it almost never fully understands what I've said while driving and it only happened since switching to Dongle mode (it almost seems as if it's not using the car's mic but rather the phone's mic in my pocket for some reason).

This is interesting. Does it only happen in dongle mode? Let us know if you find out more about this.

admiralspeedy commented 4 months ago

This is interesting. Does it only happen in dongle mode? Let us know if you find out more about this.

Nope, it happens with regardless of the mode used. Basically Assistant is almost unusable for me now.

I thought at first it was just Assistant being Assistant and maybe Google shifted resources over to the steaming pile of garbage that is Gemini, but yesterday I was trying to send a 12 word text to someone and it refused to accept more than like 5-6 words of what I said. I tried yelling, I tried taking my phone out of my pocket and speaking into it (thinking maybe it wasn't routing the mic properly and using the phone, which would be muffled in my pocket) and eventually I got mad and disconnected the dongle and plugged my phone in and it works perfectly when plugged in.

When I got home I tried both wireless and wired again and wired works fine every time, wireless does not. I have also noticed the last couple weeks that when using the dongle, Assistant is way slower. I trigger it with a button on my steering wheel and when using the dongle sometimes it opens and sits frozen (the little colored lines that show noise don't move at all) and I have to tap the screen to get rid of it and try it again because it won't actually listen to my response. This only happens on wireless with the dongle.

I don't really understand how the dongle could possibly cause problems with Assistant, unless it is somehow mangling voice data being sent from the car to the phone, but I'm not sure how or why it would be doing that. I am going to revert the Dongle mode pull request just to see if somehow that is causing it even though I have it set to Phone first mode anyways.

admiralspeedy commented 4 months ago

Two updates today:

Assistant issue:

I've been trying a variety of things in regards to the Assistant issue and I think I can tentatively say I have solved it. I spent a long time searching online and it actually seems like this issue is not exclusive to this project and it happens on most retail adapters you can buy (like AAWireless) for some people.

I tried increasing the buffer size thinking maybe it was clipping voice data or something, but it didn't help. I tried switching to my Pi 4 since it has 5 GHz Wi-Fi and I saw someone online mention it only happened to them when using their dongle in 2.4 GHz mode, but it did the same thing. I then stumbled on a Reddit comment from someone that said to clear the cache on the Google app. I did that and it seems to be working totally fine now. Not sure how or why that makes any sense at all, but I just sat in my car for 10 minutes giving Assistant various commands and long messages to send via text and it never missed a word after clearing cache. I will continue testing it out this week on my way to and from work to confirm.

Detecting USB disconnection:

I have devised a method to detect USB disconnection, but before I submit a PR I figured I would explain first and see what you though since it's a little bit janky.

I discovered that when USB is disconnected, the read call in the USB to TCP thread never returns, it perpetually gets stuck. I tried using select on it with a timeout but it doesn't work for some reason.

What I ended up doing was using the chrono library to store a time right before the forwarding loop begins (only on the USB thread) and then right before the reading begins:

const auto forward_start = std::chrono::high_resolution_clock::now();

while (!should_exit) {
     if (direction == ProxyDirection::USB_to_TCP) {
          read_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - forward_start).count();
          }

         // Read ...

When spawning the USB and TCP forwarding threads in handleClient I also spawn a third monitor thread:

    Logger::instance()->info("Frowarding data between TCP and USB\n");
    std::atomic<bool> should_exit = false;
    std::atomic<long long> read_start = 0;
    std::thread usb_tcp(&AAWProxy::forward, this, ProxyDirection::USB_to_TCP, std::ref(should_exit), std::ref(read_start));
    std::thread tcp_usb(&AAWProxy::forward, this, ProxyDirection::TCP_to_USB, std::ref(should_exit), std::ref(read_start));
    std::thread monitor(&AAWProxy::monitor, this, std::ref(read_start));

The monitor thread simply runs a loop that stores the previous read start time and compares it to the current read start time. If the read start time hasn't changed, it increments a counter and then sleeps for 1 second and checks again. If the read start time hasn't changed after 10 loops we know that the USB reading thread is stuck:

void AAWProxy::monitor(std::atomic<long long>& read_start) {
    int count = 0;
    long long prev_start = 0;

    while (true) {
        if (prev_start != read_start) {
            count = 0;
            prev_start = read_start;
        } else {
            count++;
        }

        if (count >= 10) {
            break;
        }

        sleep(1);
    }

    system("reboot");
}

The biggest issue is that as of right now to get out of the stuck state we have to reboot the dongle since setting "should_exit" doesn't work because the USB reading thread is stuck and never gets the point where it can break the loop, so it never joins the main thread.

The only way I can see avoiding the reboot is to kill the USB thread by force, but I'm not sure if that's a good idea.

admiralspeedy commented 4 months ago

Clearing the Google app cache was not a fix, it was just a fluke but I may have a new lead.

It seems to work better if I disconnect from the car's Bluetooth, however AA requires a BT connection to the car and it reconnects itself automatically if you disconnect. Even if you turn BT off, it turns it back on to connect.

I did find another post Reddit talking about the AAWireless dongle you can buy having this issue and they said what fixed it for them was changing some setting in the AAWireless app to make it stay connected to Bluetooth and set it to be used for calls in Bluetooth settings.

I can probably change the code to maintain a BT connection but Idk how we would to call routing thing?

nisargjhaveri commented 4 months ago

I discovered that when USB is disconnected, the read call in the USB to TCP thread never returns, it perpetually gets stuck. I tried using select on it with a timeout but it doesn't work for some reason.

Right. This is because the /dev/usb_accessory file does not support poll/select, which makes it very difficult to detect or exit the usb read. I can see two options:

  1. Fix the f_accessory driver to support poll on /dev/usb_accessory.
  2. Interrupt the thread on setting should_exit to true. An interrupt would make the blocking read call return, and we can proceed to join.

When spawning the USB and TCP forwarding threads in handleClient I also spawn a third monitor thread

If USB stops responding, wouldn't TCP also stop responding and eventually timeout? Why do we need another thread?

admiralspeedy commented 4 months ago

If USB stops responding, wouldn't TCP also stop responding and eventually timeout? Why do we need another thread?

Yes, TCP does timeout but both threads have to finish and join for the main loop to start over and the USB thread never joins even if should_exit is set to true because the thread gets stuck on the read call forever, so it never breaks out of the forwarding loop to let the thread exit and join.

That means the handleClient function gets stuck waiting for the USB thread to join forever, which is why I spawned a third monitoring thread.

If the driver can be fixed to support poll that would be the best option but I wouldn't even know where to begin on that.

nisargjhaveri commented 4 months ago

I've attempted to implement the interrupt solution in #90, but I haven't been fully able to validate if it does interrupt stuck usb read call or not. It does interrupt TCP read call, and also the timeout works well.

Along with eef68904fe0540cdc176d1de452d36d0a89053d9 and 5051975c50ad9456c87866cde38f21b23d469af4, this fix should help a lot with TCP disconnection scenarios. Let me know if it helps?

admiralspeedy commented 4 months ago

Will try this week. Are these changes compatible with dongle mode?

admiralspeedy commented 4 months ago

Just tried it out and TCP timeout works fine, but it still does not timeout properly if a connection to the head unit is lost.

I checked the logs and there is nothing of note, it basically just never times out.

admiralspeedy commented 4 months ago

I've been testing it for a while tonight with the head unit emulator on my PC and from what I can tell, TCP does not timeout when USB connection is lost so we still remain stuck when the head unit is lost.

I'm like 99.9% certain TCP was timing out before when the USB connection was lost, but I cannot find any change that would make that no longer happen.

nisargjhaveri commented 4 months ago

@admiralspeedy I've made more changes to the PR. Now it kills the waiting USB thread correctly, in case there are no pings.

I've been testing it for a while tonight with the head unit emulator on my PC and from what I can tell, TCP does not timeout when USB connection is lost so we still remain stuck when the head unit is lost.

Can you explain this scenario better and how to reproduce this with Desktop Head Unit?

admiralspeedy commented 4 months ago

Can you explain this scenario better and how to reproduce this with Desktop Head Unit?

Plug the dongle into your PC, wait for your phone to connect to it and trigger the USB connection, start DHU and let it connect.

Once the connection has been established and AA is running, just close the DHU window. Closing the window severs the USB side exactly how it would if the dongle lost connection to the head unit in your car but remained powered (faulty USB cable, intentionally or accidentally closed/disabled AA from the head unit, shut off car but the USB remains powered).

Once you do this, the dongle just gets stuck in a limbo state because as we already know the USB read never finishes or times out (since the driver doesn't implement poll and we can't use select) and now TCP never times out, the read for it seems to get stuck as well, so stopForwarding never gets called and neither thread gets killed.

If you SSH into the dongle and check the logs, you will see nothing abnormal, no errors and no call to stopForwarding.

I don't see any changes that would have made this happen suddenly, but I'm 99% certain that before these new changes that TCP was timing out when the USB was disconnected, which if true should mean the code would work since it would call stopForwarding and kill the USB thread. The timeout for TCP works fine the other way, if you disconnect the phone from the dongle's wireless network it triggers stopForwarding fine after 10 seconds.

admiralspeedy commented 4 months ago

I think I was just wrong in the past. I tried some older builds and TCP never timed out when USB was disconnected and thinking about it now, there is no reason it should since the connection is still open with the phone and can be read from and written to.

So basically, the only solution I can see is a third thread to monitor for USB disconnection (which I have implemented myself to test) or we need to implement polling into the USB driver.

I did start looking into implementing polling, it doesn't look too difficult but it still has me a bit confused.

nisargjhaveri commented 3 months ago

For me, closing DHU window causes an proper USB read error on Pi and everything stops and reconnect cleanly. And I don't have a way to test this on the headunit I have :/

there is no reason it should since the connection is still open with the phone and can be read from and written to.

I'd assume that if USB is not sending anything, the TCP would also not send anything eventually and it should timeout? If TCP sends something, USB write should fail, no? Or is it stuck on both USB read and USB write?

nisargjhaveri commented 3 months ago

In any case, if the TCP disconnection is better with #90, we can go ahead with that change and work on the usb part on top of it.

admiralspeedy commented 3 months ago

Just tried it in my car and it works perfectly (I caused the USB disconnect by going into my head unit settings and disabling Android Auto) and it timed out and reconnected fine.

I have absolutely no clue why DHU is suddenly not triggering a timeout for me when I close it. It definitely did before and obviously still does for you. I was able to trigger a timeout by disabling the device in Device Manager and re-enabling it after 10 seconds. Maybe a Windows update changed something with USB handling that broke it (I'm running the beta channel of Windows 11).