Android SDK: Troubleshooting
Each entry starts from what your app sees, then gives the cause and what to do. The Integration guide describes the normal flow. To compare your app's behaviour with a known-good one, run the same steps in the demo app from the SDK package: its event list shows every SDK callback with its error code.
The terminal is never found while the screen is off
Symptom: scanAndConnectToPeripheral() ends in BleError.TerminalNotFound (code 7) every time it is called while the screen is off, for example from a service or a notification action with the phone in a pocket, although the same terminal is found in a few seconds with the screen on.
Cause: from Android 8.1, Android delivers no results to a Bluetooth scan without a filter while the screen is off, and the SDK scans without a filter so that it finds the terminal by its name.
What to do:
- Start the connection from a visible screen with the screen on, for example when the user opens the vehicle screen or taps a button.
- If the screen turns off during a scan started with the screen on, that scan receives nothing more and ends in
TerminalNotFound. CallscanAndConnectToPeripheral()again.
The terminal is not found although it is next to the phone
Symptom: BleError.TerminalNotFound (code 7) with the app on screen and the vehicle in range.
Cause and what to do:
- Bluetooth is off. With Bluetooth off the scan cannot start, and the SDK reports
TerminalNotFoundonce the timeout has passed, notBluetoothDisabled. CheckBluetoothAdapter.isEnabledbefore connecting and ask the user to turn Bluetooth on (see Location services and Bluetooth). - The name does not match. The name passed to
BleService.getTerminal()is compared exactly with the name the terminal advertises: it is case-sensitive and is not trimmed. Pass it exactly as advertised. - The app is in the background or the screen is off. See the entry above.
- The timeout is too short for the scan to see the terminal. Keep the default of 10 000 ms or more.
From 3.0.4, TerminalNotFound is also reported when the terminal was found but did not accept the Bluetooth connection within the timeout. In that case onTerminalDisconnected comes first.
A GET_LOCK_STATE result arrives that the app never sent
Symptom: right after onTerminalConnected, onTerminalCommandResult(BleAction.GET_LOCK_STATE) is called, although your app did not send GET_LOCK_STATE. In the demo app it shows as Read lock state done, lock state ... right after Connected to ....
Cause: once the terminal has accepted the key, the SDK reads the lock state itself so that BleTerminal.getLockState() has a value. If the terminal answers that the state is unknown, the SDK asks again, up to 3 more times, before it reports the result. Until this result arrives, getLockState() returns LockState.UNKNOWN.
What to do: treat every onTerminalCommandResult(GET_LOCK_STATE) as "the lock state was refreshed" and read getLockState() there. Do not pair results with the actions your app sent one for one. This query also counts towards the 3 commands in flight.
onTerminalDisconnected is called after disconnect(), even when nothing was connected
Symptom: calling disconnect() triggers onTerminalDisconnected, also when no terminal was connected or while the SDK was still scanning.
Cause: this is by design. disconnect() stops any scan in progress, closes the connection if there is one, and always reports onTerminalDisconnected. From 3.0.4 it is reported once per call. With 3.0.3 it was reported twice for one disconnect(), once immediately and once when the Bluetooth stack confirmed the disconnection.
What to do: make your onTerminalDisconnected handler safe to run when your app is already in the disconnected state. If you need to tell your own disconnection from a dropped connection, record that your app called disconnect() before calling it.
CtgError.TooManyRequest when sending actions
Symptom: onError with BleError.CtgError code 28 (CtgError.TooManyRequest) after sendAction().
Cause: at most 3 commands can wait for an answer at the same time. The SDK rejects a fourth one without sending it to the terminal. The SDK's own lock-state read right after connecting counts as one of the 3, so a burst of 3 actions sent from onTerminalConnected can already hit the limit. LOCK and UNLOCK hold their place until their result arrives, which includes the lock-state read the SDK makes after them.
What to do: queue actions in your app and send the next one when a result or an error has arrived. From 3.0.4, up to 3 commands sent at once are all delivered. With 3.0.3, a command sent while another was waiting for its answer could be lost without any callback, so send one command at a time and wait for its result.
Connecting takes several seconds
Symptom: a few seconds pass between scanAndConnectToPeripheral() and onTerminalConnected.
Cause: a connection is a scan, a Bluetooth connection, a handshake and an authentication with the terminal. With the terminal in range this takes 3 to 5 seconds from 3.0.4, and about 7 to 8 seconds with 3.0.3.
What to do: show the user that the connection is in progress, and do not call scanAndConnectToPeripheral() again while you wait: a second call during the scan is ignored. Leave the timeout at 10 000 ms or more; it is also how long each command waits for its answer.
No callback at all after scanAndConnectToPeripheral()
Symptom: neither onTerminalConnected nor onError arrives.
Cause:
- A scan was already running: the SDK ignores a second
scanAndConnectToPeripheral()while it scans, without a callback. The first call still reports its own result. - Android refused to start the scan. The SDK then stops without reporting an error. Logcat shows
Scan failed because of ...under theBleCentraltag. - The listener was replaced (see below).
What to do: call scanAndConnectToPeripheral() once per attempt and wait for its result. Keep a watchdog of your own, a few times the timeout long; when it expires, call disconnect() and try again.
CtgError.InvalidKey when connecting
Symptom: the connection attempt ends in BleError.CtgError code 21 (CtgError.InvalidKey). The terminal then closes the connection, so onTerminalDisconnected can follow.
Cause: the key saved on the phone does not match the one in the terminal:
- It is the key of another vehicle. The SDK stores one key per app, not one per terminal, so saving the key of another vehicle replaced it.
- The key was rotated with
PUT /mikey/{registration}and the new key has reached the terminal.
What to do: fetch the vehicle's key from the Fleet API, save it with saveAuthKey(), call disconnect() and connect again. Before connecting to a vehicle, save that vehicle's key.
BleError.KeyInvalid or BleError.KeyNotFound when connecting
Symptom: scanAndConnectToPeripheral() reports BleError.KeyInvalid (code 6) or BleError.KeyNotFound (code 5) straight away, without scanning.
Cause:
KeyInvalid: the stored key is not 64 hexadecimal characters.saveAuthKey()does not check the format, so a malformed key is saved successfully and only rejected when connecting.KeyNotFound: no key is stored: none was saved,removeAuthKey()deleted it, or your app's data was cleared. The key is stored in your app's default Shared Preferences, encrypted with a key kept in the phone's Android Keystore. So it also reads as missing when your app's data was restored from a backup onto another phone, which does not have that Keystore key, and when your app goes back to an SDK version older than the one that saved it (see V3.0.5).
What to do: check that the key from the Fleet API's bluetooth_key field is 64 hexadecimal characters before calling saveAuthKey(), as the demo app does, and save it before connecting.
BleError.LocationUnauthorized although the location permission is granted
Symptom: connecting fails at once with BleError.LocationUnauthorized (code 9).
Cause: the SDK also reports this error when location services are off: it needs the GPS or the network location provider to be enabled.
What to do: ask the user to turn location on, for example by opening Settings.ACTION_LOCATION_SOURCE_SETTINGS, then connect again.
BleError.BluetoothUnauthorized on Android 12 and higher
Symptom: connecting fails at once with BleError.BluetoothUnauthorized (code 1).
Cause: on Android 12 (API 31) and higher, the SDK needs both BLUETOOTH_SCAN and BLUETOOTH_CONNECT to be granted. From 3.0.4 it reports this error up front when only one of them is granted.
What to do: request both at run time (see Runtime permissions).
CtgError.Timeout after an action
Symptom: BleError.CtgError code 22 (CtgError.Timeout) after sendAction().
Cause: the terminal did not answer within the timeout given to scanAndConnectToPeripheral(). The connection stays open.
What to do: check the signal strength reported by onSignalStrength, then send the action again. If commands keep timing out, call disconnect() and connect again.
getBleConnectionState() returns DISCONNECTED while the app is connecting
Symptom: during a connection attempt, getBleConnectionState() returns DISCONNECTED rather than CONNECTING.
Cause: the SDK reports CONNECTED only once the terminal has accepted the key. During the scan, the Bluetooth connection and the authentication it returns DISCONNECTED.
What to do: keep your own "connecting" state from the call to scanAndConnectToPeripheral() until onTerminalConnected or onError, as the demo app does.
Callbacks stop arriving, or arrive in another screen
Symptom: a screen no longer receives the SDK callbacks, or another part of the app receives them.
Cause: BleService.getTerminal() returns the same BleTerminal for the same name, and a terminal has a single listener: each assignment to bleListener replaces the previous one. Calling BleService.configure() again also creates new BleTerminal objects, so a listener attached to an earlier one no longer receives anything.
What to do: call configure() once, keep one owner for the terminal's listener, and assign it again when that screen comes back to the foreground.