In src/droidplug/java/.../Peripheral.java, the command callbacks for readDescriptor, writeDescriptor, requestMtu, and readRemoteRssi don't override onConnectionStateChange. The base CommandCallback.onConnectionStateChange throws UnexpectedCallbackException, and that throw happens outside asyncWithFuture, escaping to the Binder thread from inside the synchronized block in the GATT callback.
Failure scenario: the peer disconnects during read_descriptor() / read_rssi() / MTU request. The future never completes, executingCommand stays true, and every later command on that peripheral (connect, disconnect, read, …) hangs forever. adapter.onConnectionStateChanged is never reached, so DeviceDisconnected is never emitted either.
Fix direction: handle disconnect in those callbacks the same way the read/write/discover callbacks do (fail the future with not-connected and run the next command), and make the base class path not throw out of the GATT callback.
In
src/droidplug/java/.../Peripheral.java, the command callbacks forreadDescriptor,writeDescriptor,requestMtu, andreadRemoteRssidon't overrideonConnectionStateChange. The baseCommandCallback.onConnectionStateChangethrowsUnexpectedCallbackException, and that throw happens outsideasyncWithFuture, escaping to the Binder thread from inside thesynchronizedblock in the GATT callback.Failure scenario: the peer disconnects during
read_descriptor()/read_rssi()/ MTU request. The future never completes,executingCommandstays true, and every later command on that peripheral (connect, disconnect, read, …) hangs forever.adapter.onConnectionStateChangedis never reached, soDeviceDisconnectedis never emitted either.Fix direction: handle disconnect in those callbacks the same way the read/write/discover callbacks do (fail the future with not-connected and run the next command), and make the base class path not throw out of the GATT callback.