ginkage / GamePad

Android TV-like Bluetooth gamepad implementation
Apache License 2.0
11 stars 7 forks source link

Possible SDK bug with BluetoothProfile.ServiceListener : onServiceConnected? #3

Closed Merthan closed 3 years ago

Merthan commented 3 years ago

Writing here because you seem knowledgable on this topic and are also working at Google / know the SDK?

I'm a student and currently working on something similar and am also using BluetoothAdapter.getProfileProxy() with a HID device.

Seems like multiple people are all getting a similar bug, which is onServiceConnected never being called on their device. Now I tested my implementation and it works for most devices, but some people are reporting that onServiceConnected never gets called on theirs. One example of this: https://stackoverflow.com/q/59960201/7968986 . I haven't recognized a pattern on the devices where it doesn't work but all are SDK 28+ and some even reported that it was called at some point but then never again. (Even trying things like uninstalling etc.)

I've spent multiple weeks on trying to resolve this, so I would really appreciate some insight. I am pretty sure that the code you have here has the same problem (although you don't need to fix it of course, just a general answer on this topic somewhere on the internet would be nice)

If you currently don't have the time to look into this, please also just write that. That's ok too.

Relevant parts in your code:

@MainThread
void registerServiceListener(Context context, ServiceStateListener listener) {
    context = checkNotNull(context).getApplicationContext();
    serviceStateListener = checkNotNull(listener);
    bluetoothAdapter.getProfileProxy(context, new ServiceListener(), BluetoothProfile.HID_DEVICE);
}

        private final class ServiceListener implements BluetoothProfile.ServiceListener {
        @Override
        @MainThread
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            service = (BluetoothHidDevice) proxy;
            if (serviceStateListener != null) {
                serviceStateListener.onServiceStateChanged(service);
            } else {
                bluetoothAdapter.closeProfileProxy(BluetoothProfile.HID_DEVICE, proxy);
            }
        }

        @Override
        @MainThread
        public void onServiceDisconnected(int profile) {
            service = null;
            if (serviceStateListener != null) {
                serviceStateListener.onServiceStateChanged(null);
            }
        }
ginkage commented 3 years ago

Interesting, I don't think I've ever stumbled upon this one. In the code linked at StackOverflow, one thing that strikes me odd is that Bluetooth is being enabled without waiting for an ActivityResult, which might lead to race condition issues, and in general one thing to look out for is to not forget calling closeProfileProxy when you're done, as otherwise the service may be busy with another app. Also, though onServiceConnected is not called, what about onServiceDisconnected? Could it be that the proxy closes itself prematurely?

Merthan commented 3 years ago

Thanks for your response!

I'm already waiting for some Logcats from devices where it doesn't work to see if those have more info.

In my case the same thing happens when Bluetooth is already enabled, so I'm pretty sure it's not because of that

Most people that reported that likely don't have any other apps using this profile running, but I'll make sure that that's the case. Even so, on my device I still get onServiceConnected even with other apps currently having an active HID_DEVICE connection (connection then fails at another point that I haven't seen occur on others devices)

closeProfileProxy is always called apart from force stopping perhaps (in onDestroy)

Is it possible that force stopping with an active proxy is the reason for this? Basically leaving the BluetoothAdapter in a wrong state?

One thing I did differently to your code is that I used a Kotlin member variable val serviceListener = object: BluetoothProfile.ServiceListener{...}

So within getProfileProxy(applicationContext,serviceListener,BluetoothProfile.HID_DEVICE) instead of new ServiceListener() as you have

Very unlikely that this is the reason why some users are getting that error though, pretty sure the call also only get's made once so there should be no difference at all (otherwise I always use the same instance).

ginkage commented 3 years ago

So, first off, getProfileProxy() will always return true, when context and listener aren't null, and profile is one of the supported values. What the method does is, it just creates a BluetoothProfile object and calls a BluetoothProfileConnector<IBluetoothHidDevice>::connect(...) method (which effectively registers a new service state listener), then returns "true" immediately. In that sense, the return value doesn't provide any useful information, so you'll need to look closer into the logcat output (without filtering to just your app, which is the default behavior in Android Studio). If you can reproduce this issue, feel free to send the logcat to me so that I could take a closer look.

Merthan commented 3 years ago

Yep, that's what I have been telling the affected. Haven't seen a false return anywhere yet as you mentioned. I'll post a logcat here as soon as I get it

Just hope that the important logcat info doesnt require a debug build (or only logs that stuff for debug builds) but that seems unlikely as it's probably logged "outside" of the app anyways.

Merthan commented 3 years ago

Hey, so I didn't get a full Logcat but what I got should be sufficient:

It's the following:

could not bind to bluetooth service with intent { act=android.bluetooth.ibluetoothhiddevice }

Which then also lead me to this:

https://stackoverflow.com/questions/53555092/how-can-i-use-the-bluetooth-hid-device-profile-in-android-pie Where the same Log message was mentioned -

Which then also lead me back to your project :D

https://stackoverflow.com/a/57137850/7968986

One of the people that commented also opened an issue

https://issuetracker.google.com/issues/125169815?pli=1

But that one didn't get much attention in the 2 years :/ Some people took it upon themselves to create something for root/xposed https://github.com/LoreBadTime/Bluetooth-HID-Enabler

So... could you tell me what I can do? I assume the answer is nothing except for maybe waiting.

I'm pretty sure there is no way to filter devices that don't support the profile on the PlayStore either, so I guess I'll have to do that manually for each bug report that I get? (Meaning I can only disable/hide a BluetoothHID app for a device after a user already told me it doesn't work?)

Is there anything you can do in your position or is this issue affecting "too few" people to get noticed by Google (again)?

Seems like some other Bluetooth "issues" in recent Android releases are less important and were still fixed. HID apps have lots of potential, just currently few apps due to the unreliability/complexity of the SDK.

grafik

*Actually controller support is probably more important, but not by much in my opinion. After all you could make Android support all other devices (receivers, that is) by faking the respective controllers with HID (if that app would exist, sure those gaming phones with Shoulder Buttons would benefit from that)

Anyways, would still love to hear your take on this.

Without finding your project I probably would have given up on my app too, didn't take code directly but debugging / noticing what I did wrong would have been near impossible without your work.

ginkage commented 3 years ago

Ah, that... Long story short, every phone manufacturer is at their own discretion whether to support certain profiles or not. In this specific example, it comes down to flipping a single boolean value here when building a system image, and I'm afraid I have exactly zero power over their decision to enable it (note that it's even "on" by default!). You can bug them with issue reports, but so far, as someone has already mentioned on the issue you've linked, it's mostly like

Yep, sadly OnePlus and Nokia just said "Blah, whoever would need that thing anyway" From my experience, it's really common for Chinese manufacturers in every aspect

I've been barking up that tree for quite some time (I was actually one of the folks who brought this Bluetooth profile to Android in the first place), but had no luck so far. I'm afraid that the only working solution here is to buy a phone from a different manufacturer (as in "vote with your wallet"). Sorry about that.

Merthan commented 3 years ago

The one and only, so it would've been double impossible without you.

Released 2 apps and am going to release a 3rd one using the profile soon. I'm pretty sure you'll use it as long as you don't create your own competitor sometime in the future :D
(I probably spent 200-300+ hours getting it working, hope the next person trying that spends as much time too)

Both are Presenters with pretty much all features one could possibly think of (At least that's all of the feedback I got, except for devices not working)

If you want to take a look, it's these 2:

https://play.google.com/store/apps/details?id=com.me.btp.free https://play.google.com/store/apps/details?id=com.me.btp

So who knows, in a few months when you hear your coworkers say

Hey did you know that you can use Wear/Android to control presentations and get custom alerts to stay on time?

You can tell them to look in the credits, I'll put your name there too (already have a section for testers/creative feedback anyways)

If I would have used your code directly I would have put a default Apache 2.0 notice there instead

I assume no one else will see this chat for some time anyways, so here is a promo code for the pro version if you want to take a look: XXXXX

Neither feature mouse/keyboard functionality though, so it's not supposed to replace your apps

The Wear version will be even better, good UI on Wear will take me ages again but if you tried it I'm sure you'll prefer that over... well all other things you could use to present. Wear will also get some extra features that you'll see once it's released

And... please don't build a competitor... whoever might be reading this :D

ginkage commented 3 years ago

Thank you for your kind words, and thank you for the recognition. Apps like yours are the very reason I was driving this project in the first place. And don't worry about "competing", here at Google we're not allowed to release apps that could undermine other devs' efforts (unlike a certain fruit company), which is also why WearMouse is only available for Wear and doesn't get new features anymore (I'm also not allowed to make it a paid app, or make any money with it), and GamePad wasn't even ever released in Play Store: it was only ever meant to be a semi-official sample code, and inspiration to other developers. That's also the reason why I can't do anything about license, though that may be probably worked around by converting the code to Kotlin (a few clicks in Android Studio). ;) I've installed your app (feel free to remove the promo code by editing your comment if you want), and adding it to my collection of apps using this profile and making the world a bit better (one other nice creative example worth checking out is WearAuthn). Godspeed!

Merthan commented 3 years ago

Thanks a lot :D Hey, just by making a "small" change like this (HID Devices) you might have a huge impact on electronic waste being generated in the future. Wouldn't even have thought of a possibility like WearAuthn (thanks for making me notice that). Who knows, in a couple years it might become popular to just use a phone/watch instead of buying some 5-30€/$ gadget and throwing it away soon. It's not even about replacing them with something equal, it's about being able to create something... better. Having a whole smartphone/OS behind that just enables so many more possibilities...

E.g. don't know the kind of (probably) circuit design that would've gone into automatically detecting Mac or Windows devices and switching a signal for entering a presentation accordingly. (Honestly haven't seen a hardware presenter that even does that)

With Android you can simply either create simple Settings for that, or do something like deviceName.contains("mac") (simplified) and then automatically switch the controls according to that.

In general most use cases have a better (at least possible) counterpart with a Android HID device. No matter the engineering that would've gone into a Hardware device. Similar to NFC payments instead of card payments, only way more possibilities.

Who knows, I might see the Wear version be used at IO at some point or have you as a coworker in a bunch of years :D If you should have any requests at all regarding this or the future Wear version, feel free to tell me

If you use LinkedIn semi-regularly you can add me https://www.linkedin.com/in/merthan/

But yeah, thanks a lot for all of your responses!

ginkage commented 3 years ago

I'm with you all the way there. :)

One thing I used to imagine while working on WearMouse (some 4-5 years ago) was to be able to simply point at the lights/TV/etc. and turn them on and off with a flick of your fingers. Interestingly, there's now a technology that could potentially allow you to do just that, called UWB. Hopefully, we'll eventually be able to leverage that one to surprise the world with even more unexpected magical experiences. Stay tuned!

Merthan commented 3 years ago

UWB looks great, the main reason I started with all of this (even before having a WearOS watch or having heard of WearMouse / BluetoothHID) was that I thought it would be possible to control a presentation using just the "swipe" arm gesture you would use to "tell other people to advance the slide" The idea for a phone version with volume buttons came later. Let's hope Wear will get more popular in the future so I'm not one of the only ones using that app by then :D

Swipe will be hard to get just right though for sure...

In your case apart from flicking your finger you could also show prompts on the Smartwatch whenever the user points at something (with Gyroscope too). Stuff like Stop music / Turn on that specific light(or TV). Sounds like something Google could do a Proof of concept for :D