iDevicesInc / SweetBlue

BLE on Android, the easy way. THIS IS NOW DEPRECATED. Please visit website for info on new versions.
https://sweetblue.io
GNU General Public License v3.0
357 stars 57 forks source link

NullPointerException | P_AndroidBleServer.java line 38 #394

Open johnpaulmckean opened 7 years ago

johnpaulmckean commented 7 years ago

Getting this crash now and then while testing.

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.android.bluetooth.gatt.ServiceDeclaration.addCharacteristic(java.util.UUID, int, int)' on a null object reference
       at android.os.Parcel.readException(Parcel.java:1689)
       at android.os.Parcel.readException(Parcel.java:1636)
       at android.bluetooth.IBluetoothGatt$Stub$Proxy.addCharacteristic(IBluetoothGatt.java:1281)
       at android.bluetooth.BluetoothGattServer.addService(BluetoothGattServer.java:590)
       at com.idevicesinc.sweetblue.P_AndroidBleServer.addService(P_AndroidBleServer.java:38)
       at com.idevicesinc.sweetblue.P_Task_AddService.execute(P_Task_AddService.java:48)
       at com.idevicesinc.sweetblue.PA_Task.execute_wrapper(PA_Task.java:274)
       at com.idevicesinc.sweetblue.PA_Task.tryExecuting(PA_Task.java:330)
       at com.idevicesinc.sweetblue.P_TaskQueue.dequeue(P_TaskQueue.java:286)
       at com.idevicesinc.sweetblue.P_TaskQueue.endCurrentTask(P_TaskQueue.java:324)
       at com.idevicesinc.sweetblue.P_TaskQueue.tryEndingTask_updateThread(P_TaskQueue.java:421)
       at com.idevicesinc.sweetblue.P_TaskQueue.access$002(P_TaskQueue.java:12)
       at com.idevicesinc.sweetblue.P_TaskQueue$4.run(P_TaskQueue.java:412)
       at com.idevicesinc.sweetblue.P_PostManager.runOrPostToUpdateThread(P_PostManager.java:87)
       at com.idevicesinc.sweetblue.P_TaskQueue.tryEndingTask(P_TaskQueue.java:408)
       at com.idevicesinc.sweetblue.PA_Task.succeed(PA_Task.java:198)
       at com.idevicesinc.sweetblue.P_Task_AddService.succeed(P_Task_AddService.java:99)
       at com.idevicesinc.sweetblue.P_Task_AddService.onServiceAdded(P_Task_AddService.java:132)
       at com.idevicesinc.sweetblue.P_BleServer_Listeners.onServiceAdded_updateThread(P_BleServer_Listeners.java:284)
       at com.idevicesinc.sweetblue.P_BleServer_Listeners.access$000(P_BleServer_Listeners.java:21)
       at com.idevicesinc.sweetblue.P_BleServer_Listeners$3.run(P_BleServer_Listeners.java:273)
       at android.os.Handler.handleCallback(Handler.java:751)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6247)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:872)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:762)
ryanhubbell commented 7 years ago

Can you give a little more information on how you are getting this to happen?

Is it only on a certain device?

How many services are you adding?

Are you using the GattDatabase class to setup the gatt db (services/chars/descs)?

ryanhubbell commented 7 years ago

Bump for response

johnpaulmckean commented 7 years ago

Sorry for the late response. This is happening when:

Could you provide documentation explaining the use of GattDatabase?

This is how I currently add a char to the BLE server: BleCharacteristicProperty[] properties = new BleCharacteristicProperty[]{BleCharacteristicProperty.WRITE, BleCharacteristicProperty.READ}; BleCharacteristicPermission[] permissions = new BleCharacteristicPermission[]{BleCharacteristicPermission.WRITE, BleCharacteristicPermission.READ}; BleCharacteristic bleCharacteristic = new BleCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID, properties, permissions); BleService bleService = new BleService(LINKLOSS_SERVICE_UUID, bleCharacteristic); bleServer.addService(bleService);

ryanhubbell commented 7 years ago

Other than javadoc, there's no documentation for the GattDatabase class right now. However, it's pretty easy to pick up on. It uses the builder pattern to easily chain methods together. For your instance above it would be like this:

byte[] value = new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
GattDatabase db = new GattDatabase().addService(LINKLOSS_SERVICE_UUID)
                .addCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID).setValue(value)
                .setProperties().readWrite()
                .setPermissions().readWrite().completeService();

It's good practice to set an initial value as well, so the system has an idea of the size of the characteristic.

The nice thing about using the GattDatabase class is, you can continue to add more services, and just pass in the one database instance into the BleManager.getServer() method, and it will auto-add all services within the db.

Now when you say you have multiple devices connected, are you talking about bluetooth peripherals, or android devices?

johnpaulmckean commented 6 years ago

I'm not seeing this issue anymore. Maybe was a one-off with my dev builds.

johnpaulmckean commented 6 years ago

@ryanidev I can confirm that it's still happening on the most recent build. Only on my devices running Android v7.0

Any thoughts?

P.S. I am using the method you suggested and I'm connecting to peripherals.

ryanhubbell commented 6 years ago

Ok, so you're having the server connect to peripherals, rather than the other way around?

If you could try to get a way to reproduce it, I would appreciate it. When I spun up a test project to see if I could replicate it, I could not.

johnpaulmckean commented 6 years ago

I am connecting normally through BleDevice.connect(). I also add services to the server (my use has two way communication).

I'll try to nail it down to a reproducible bit of code.

johnpaulmckean commented 6 years ago

I'm unable to reproduce it consistently. This is what I know:

Is there something my team can do to further help figure this out? I don't feel good about moving towards production without having this nailed down.

ryanhubbell commented 6 years ago

It's a tough one to nail down, especially if I can't reproduce it. The exception is actually happening in android code. There's already a null guard before adding the service.

I could wrap the addService method in a try/catch, but that's just a band aid, and doesn't actually fix the underlying issue.

How often do you see it happen when you are trying to reproduce it?

Also, are you using an AddServiceListener?

johnpaulmckean commented 6 years ago

I totally understand. My team will have it happen very sporadicly. About once per day (when running v7.0). We haven't used an AddServiceListener since moving to: BleManager.getServer(GetGattDatabase)

What do you think the possible negative side effects of wrapping it would be? Is it possible that this is an expected (edge case) scenario on Android v7.0? I've done a lot of reading and haven't found much on BLE issues with adding a service on v7.0.

ryanhubbell commented 6 years ago

It's not that there are negative side effects, per se, but if I wrap it in a try/catch, then it will fail the AddService task. If you use an AddServiceListener, then you will get a callback when this happens.

I suppose I could add a retry there, but I have a feeling it won't make a difference.

Of course, there's still the chance that something we're doing in SweetBlue is causing this, but for us to be able to fix that, we have to be able to reproduce it.

I'll try to hammer on it some more to see if I can get it to happen. Are you only adding one service?

johnpaulmckean commented 6 years ago

I can try logging failed add events into Fabric. Would that even help? We're adding two services. Here's a code snippet:

    new GattDatabase()
            .addService(SERVICE_UUID)
            .addCharacteristic(CHARACTERISTIC_UUID)
            .setProperties().write_no_response()
            .setPermissions().write()
            .completeService()

            .addService(OTHER_SERVICE_UUID)
            .addCharacteristic(CHARACTERISTIC_UUID)
            .setProperties().readWrite()
            .setPermissions().write().read()
            .completeService();
ryanhubbell commented 6 years ago

No, the event probably wouldn't help, other than knowing it's happening.

I'll setup my test to use the same db as you (well, minus the UUIDs, but that wouldn't cause the issue).

ryanhubbell commented 6 years ago

Can you also post what options you're setting in BleManagerConfig, if any?

johnpaulmckean commented 6 years ago

Pretty simple:

BleManagerConfig config = new BleManagerConfig();
config.revertToClassicDiscoveryIfNeeded = true;
mBleManager = BleManager.get(context, config);
johnpaulmckean commented 6 years ago

@ryanidev Do you have any updates for me on this issue? I'm beginning to wonder if there may be some check I can run before trying to add a service...

ryanhubbell commented 6 years ago

Unfortunately, no. I haven't been able to reproduce the issue. I wonder if it's just adding them too fast for the stack to handle. Try adding a delay between tasks to test this theory:

BleManagerConfig config = new BleManagerConfig(); config.delayBetweenTasks = Interval.secs(.5);