Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pair - wait for disconnect; Flash - restart after bonding #73

Merged
merged 1 commit into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ public void run() {
popupPairingFailed();
}
break;
case AlreadyPaired:
// micro:bit seems to need reset even if already paired
case Paired:
handlePairingSuccessful();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ protected void onActivityResultPairing(int requestCode, int resultCode, Intent d
case REQUEST_CODE_PAIR_BEFORE_FLASH_ALREADY_RESET:
if (resultCode == RESULT_OK) {
// micro:bit should still be in Bluetooth mode
flashingChecks( true);
flashingChecks( false);
} else {
onFlashComplete();
}
Expand Down Expand Up @@ -2094,7 +2094,6 @@ public void onClick(View v) {
}
}, popupClickFlashComplete);
} else if(intent.getAction().equals(PartialFlashingService.BROADCAST_ERROR)) {
MBApp.getAppState().eventPairSendError();
final int errorCode = intent.getIntExtra(PartialFlashingService.EXTRA_DATA, 0);
String error_message = getString(R.string.connection_failed);

Expand All @@ -2108,9 +2107,16 @@ public void onClick(View v) {
case PartialFlashingService.ERROR_DFU_MODE:
error_message = getString(R.string.reset_to_dfu_mode_failed);
break;
case PartialFlashingService.ERROR_BROKEN:
error_message = getString(R.string.connection_broken);
break;
case PartialFlashingService.ERROR_BONDED:
restartPurpose();
return;
}

logi("PFResultReceiver.onReceive() :: " + error_message + " code " + errorCode);
MBApp.getAppState().eventPairSendError();

PopUp.show( error_message + "\n\n" + getString(R.string.retry),
getString(R.string.flashing_failed_title),
Expand Down Expand Up @@ -2400,6 +2406,10 @@ public void onClick(View v) {
error_message = getString(R.string.dfu_error_CODE, error_message);
break;
default:
if ( errorCode == DfuBaseService.ERROR_DEVICE_NOT_BONDED) {
restartPurpose();
return;
}
if ( errorCode == 0) {
error_message = getString(R.string.not_found);
} else {
Expand Down
103 changes: 88 additions & 15 deletions app/src/main/java/com/samsung/microbit/utils/BLEPair.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,16 @@ public class BLEPair implements BluetoothAdapter.LeScanCallback {
// Android 13 - I have seen 4 status 133s before a good connect and pair
private int pairChecks = 0;
private int pairTries = 0;
private Boolean scanning = false;
private Boolean pairing = false;
private boolean scanning = false;
private boolean pairing = false;
private boolean waitingForDisconnect = false;
private boolean wasNotBonded = false;

public enum enumResult {
None,
Found,
Connected,
AlreadyPaired,
Paired,
TimeoutScan,
TimeoutConnect,
Expand Down Expand Up @@ -165,16 +168,49 @@ private void signalProgress( enumResult state) {
}

@SuppressLint("MissingPermission")
private void signalResultPairedIfBondedAndHardwareVersionDiscovered() {
if (resultDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
// Wait for service discovery to set resultHardwareVersion
logi("Bonded resultHardwareVersion " + resultHardwareVersion);
if ( resultHardwareVersion > 0) {
signalResult(enumResult.Paired);
}
private boolean bondedAndHardwareVersionDiscovered() {
logi("bondedAndHardwareVersionDiscovered state " + resultDevice.getBondState() + " resultHardwareVersion " + resultHardwareVersion);
if ( resultDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
wasNotBonded = true;
return false;
}
// Wait for service discovery to set resultHardwareVersion
if ( resultHardwareVersion <= 0) {
return false;
}
return true;
}

@SuppressLint("MissingPermission")
private boolean startWaitingForDisconnectIfBondedAndHardwareVersionDiscovered() {
if ( !bondedAndHardwareVersionDiscovered()) {
return false;
}
// Wait for micro:bit to disconnect
waitingForDisconnect = true;
delayStopAll();
delayStart( delayCallbackWaitingForDisconnect, 6000);
logi("Start waiting for disconnect");
return true;
}

private void onDisconnect() {
// If actually pairing, micro:bit will break the connection
// Allow time for tick to appear
logi("onDisconnect");
if ( bondedAndHardwareVersionDiscovered()) {
delayStopAll();
delayStart( delayCallbackSignalResultPaired, 300);
} else if ( waitingForDisconnect) {
delayStopAll();
delayStart( delayCallbackSignalResultPaired, 300);
} else {
logi("ERROR - disconnected");
logi("Prepare delayed retry");
gattState = enumGattState.Error;
delayStart( delayCallbackConnect, 1000);
}
}
private void logi(String message) {
if(DEBUG) {
Log.i(TAG, "### " + Thread.currentThread().getId() + " # " + message);
Expand Down Expand Up @@ -248,13 +284,15 @@ public void run() {
}
}
};

private final Runnable delayCallbackCheck = new Runnable() {
@Override
public void run() {
logi("delayCallbackCheck pairChecks " + pairChecks);
if (pairing) {
signalResultPairedIfBondedAndHardwareVersionDiscovered();
if ( startWaitingForDisconnectIfBondedAndHardwareVersionDiscovered()) {
return;
}
if ( pairChecks > 0) {
// We are connected and polling for bonding
pairChecks -= 1;
Expand All @@ -266,6 +304,22 @@ public void run() {
}
};

private final Runnable delayCallbackWaitingForDisconnect = new Runnable() {
@Override
public void run() {
logi("delayCallbackWaitingForDisconnect wasNotBonded = " + wasNotBonded);
signalResult( wasNotBonded ? enumResult.Paired : enumResult.AlreadyPaired);
}
};

private final Runnable delayCallbackSignalResultPaired = new Runnable() {
@Override
public void run() {
logi("delayCallbackSignalResultPaired wasNotBonded = " + wasNotBonded);
signalResult( wasNotBonded ? enumResult.Paired : enumResult.AlreadyPaired);
}
};

private void delayStopAll()
{
logi("delayStopAll");
Expand All @@ -275,6 +329,8 @@ private void delayStopAll()
mainLooperHandler.removeCallbacks( delayCallbackDiscover);
mainLooperHandler.removeCallbacks( delayCallbackBond);
mainLooperHandler.removeCallbacks( delayCallbackCheck);
mainLooperHandler.removeCallbacks( delayCallbackWaitingForDisconnect);
mainLooperHandler.removeCallbacks( delayCallbackSignalResultPaired);
}

private void delayStop( Runnable callback)
Expand Down Expand Up @@ -460,8 +516,17 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState
return;
}

if ( gatt.getDevice().getBondState() != BluetoothDevice.BOND_BONDED) {
wasNotBonded = true;
}

logi("onConnectionStateChange " + newState + " status " + status);
if ( status != 0) {
if ( status != BluetoothGatt.GATT_SUCCESS) {
if ( newState == STATE_DISCONNECTED) {
onDisconnect();
return;
}
delayStopAll();
pairStop();
logi("ERROR - status");
logi("Prepare for retry after a short delay");
Expand All @@ -487,6 +552,7 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState
// about 600ms after establishing connection. Values 600 - 1600ms should be OK.
} else {
logi("calling discoverServices()");
wasNotBonded = true;
gattState = enumGattState.WaitingForServices;
boolean success = gatt.discoverServices();
if (!success) {
Expand All @@ -502,7 +568,7 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState
else if( newState == STATE_DISCONNECTED) {
// If actually pairing, micro:bit will break the connection
logi("STATE_DISCONNECTED");
delayStartCheck();
onDisconnect();
}
}

Expand Down Expand Up @@ -537,7 +603,10 @@ public void onServicesDiscovered(BluetoothGatt gatt, int status) {

gattState = enumGattState.ServicesDiscovered;

if (resultDevice.getBondState() == BluetoothDevice.BOND_NONE) {
if ( startWaitingForDisconnectIfBondedAndHardwareVersionDiscovered())
return;

if ( resultDevice.getBondState() == BluetoothDevice.BOND_NONE) {
logi("Delay calling createBond() to wait for bonding to start automatically");
delayStart( delayCallbackBond, 1500);
}
Expand Down Expand Up @@ -571,6 +640,7 @@ private Boolean pairConnect() {

paramCallback.BLEPairGetActivity().registerReceiver( pairReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));

waitingForDisconnect = false;
resultHardwareVersion = 0;
gattState = enumGattState.Connecting;
pairing = true;
Expand Down Expand Up @@ -643,13 +713,16 @@ public void onReceive(Context context, Intent intent) {
final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
logi("pairReceiver -" + " name = " + name + " addr = " + addr + " state = " + state + " prevState = " + prevState);
if ( state != BluetoothDevice.BOND_BONDED && prevState != BluetoothDevice.BOND_BONDED) {
wasNotBonded = true;
}
if (name == null || name.isEmpty() || addr.isEmpty()) {
return;
}
// Check the changed device is the one we are trying to pair
if ( pairing && nameIsMicrobitWithCode(device.getName(), paramCallback.BLEPairGetDeviceCode())) {
if (state == BluetoothDevice.BOND_BONDED) {
signalResultPairedIfBondedAndHardwareVersionDiscovered();
startWaitingForDisconnectIfBondedAndHardwareVersionDiscovered();
}
}
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@
<string name="connection_failed">Unable to connect</string>
<string name="reconnection_failed">Unable to reconnect</string>
<string name="reset_to_dfu_mode_failed">Unable to reset to DFU mode</string>
<string name="connection_broken">Connection lost</string>
<string name="retry">Retry?</string>
<string name="menu_import">Import</string>
<string name="menu_export">Export</string>
Expand Down