0

I am using Android API 27 and Kotlin and wish to override a BLE callback that is hidden via @hide. This question follows on from a previous question of mine. The callback in question is onConnectionUpdated and is defined within BluetoothGatt.java. The callback function is also defined within BluetoothGattCallback.java, so I'm not even sure which version I need to override.

I understand that I need to use reflection to access hidden member functions of a class, but I can't figure out how to actually access the function and override it.

Here's what I am attempting, to no avail:

val kclass = Class.forName("android.bluetooth.BluetoothGatt")
val kmethod = kclass.getMethod("onConnectionUpdated")

override fun kmethod(gatt: BluetoothGatt, interval: Int, latency: Int, timeout: Int, status: Int) {
    /* do stuff */
}

The error in this instance is: kmethod overrides nothing.

I understand that I can also use Kotlin's reflection library, but again, I'm not sure how to.

Thanks.

amitchone
  • 1,630
  • 3
  • 21
  • 45
  • It's not defined in BluetoothGatt, that class is just implementing an internal callback that then invokes external callbacks. – TheWanderer Oct 11 '18 at 14:31
  • Possible duplicate of [Alternatives to java.lang.reflect.Proxy for creating proxies of abstract classes (rather than interfaces)](https://stackoverflow.com/questions/3291637/alternatives-to-java-lang-reflect-proxy-for-creating-proxies-of-abstract-classes) – TheWanderer Oct 11 '18 at 14:35
  • The possible duplicate doesn't help me: not because it doesn't apply in this situation but because I don't understand it and therefore cannot apply it to my issue. Forgive my naivety, but I have minimal experience with Android. – amitchone Oct 11 '18 at 14:39
  • 1
    Implement Javassist in your project (https://mvnrepository.com/artifact/org.javassist/javassist/3.15.0-GA), then copy the code in the accepted answer and replace Dog with BluetoothGattCallback. Remove the methods called on Dog and instead set that object as your callback. In the `invoke()` method you can filter the called method and deal with everything there. – TheWanderer Oct 11 '18 at 14:43
  • I wish I understood this. I've got javassist within my project through Gradle and I've copied code around to no avail. I've tried putting the code from the other answer into a separate Java file and still no luck! Much more comfortable with embedded C++! Thanks anyway! – amitchone Oct 11 '18 at 15:03
  • Actually just having the method (inside `BluetoothGattCallback`) without the `override` keyword should be enough to override it. If it's never called then maybe because the hidden method is never called. The `overrides` keyword is just syntactic sugar and has no meaning in java. What counts is the existence of a method with the right signature – zapl Oct 11 '18 at 18:47
  • @zapl I do have the method within my `BluetoothGattCallback` object and have tried simply declaring as `fun onConnectionUpdated(etc...)` to no avail. The function (not my overridden version, but the actual implementation) *does* get called as it prints to the debug console. – amitchone Oct 11 '18 at 19:08
  • It should work: https://gist.github.com/zapl/a41a6fa59ed6a3c1de9967412f69d329 - there is nothing (besides the `final` keyword which can be gotten rid of with `open`) that's different from regularly overriding a method – zapl Oct 11 '18 at 19:43
  • @zapl I'll have to give it a try tomorrow. So you suggest something like `open fun onConnectionUpdated(...)`? Cheers. – amitchone Oct 11 '18 at 19:50
  • shouldn't make a difference since `final` is not part of the method signature (https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2). It should already work like you had it. – zapl Oct 11 '18 at 19:53
  • If you don't think it'll make a difference then it's probably not worth trying. As I've mentioned I have already tried simply declaring `fun onConnectionUpdated()` within the `BluetoothGattCallback` object and it's not triggering, back to square one then! – amitchone Oct 11 '18 at 19:55
  • you can only check whether you had a typo in your method, in which case it counts as a new unrelated method. You can also check whether that method exists on your device by using reflection like in the example code – zapl Oct 11 '18 at 20:01
  • @zapl There was definitely no typo so I'll have to use reflection. Cheers anyway! – amitchone Oct 12 '18 at 09:25
  • @AdamMitchell reflection with javaassist shouldn't change the outcome, it merely creates the class you're writing dynamically. The only advantage is that you can call `super.onConnectionUpdated` because the compiler won't let you do that unless it knows that method exists. Otherwise, there is nothing in the compiler output that actually marks a method as overriding: https://stackoverflow.com/a/8343863/995891 neither for javac nor for the kotlin compiler. – zapl Oct 12 '18 at 09:40
  • Furthermore, https://stackoverflow.com/q/9844320/995891 indicates that simply having a method with the right signature works on android so your issue is maybe not that you're overriding incorrectly but that the framework for some reason doesn't call the method your override. – zapl Oct 12 '18 at 09:50
  • @zapl If that's the case then something else is wrong then. The function definitely *is* called as, when running in debug mode from Android Studio, you can see the default implementation of the callback print to the debug log. My re-declared function never triggers. – amitchone Oct 12 '18 at 11:56
  • Where does that log come from? https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/bluetooth/BluetoothGattCallback.java#194 is empty, only https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/bluetooth/BluetoothGatt.java#670 logs as far as I can see and there is at least that `if (!address.equals(mDevice.getAddress()))` part that could skip the callback to your callback (the one from `mCallback`) – zapl Oct 12 '18 at 18:58

0 Answers0