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

[Android] Local name is not always updated during Scan #995

Open
murilommp opened this issue Nov 9, 2023 · 3 comments
Open

[Android] Local name is not always updated during Scan #995

murilommp opened this issue Nov 9, 2023 · 3 comments

Comments

@murilommp
Copy link

murilommp commented Nov 9, 2023

Hello, everyone.

I'm building a cordova app for Android with the goal of write data to and read data from a proprietary BLE device. Everything works great, but I am facing an issue when scanning devices with "ble.startScan()" just after I have changed the device name. The new device name is not updated immediately.

My proprietary device is advertising it's name correctaly and I can receive the updated information when I use third party apps (like ST BLE TooBox) to perform an scan.

I realize that method asJSONObject() in Peripheral class (in file Peripheral.java) uses method getName() from class BluetoothDevice to fill attribute name in JSON object passed as parameter to javascript callback. I saw in method documentation that the name returned by it may be old due to have been extracted from cache.

I also checked that class ScanRecord has the method getDeviceName and it returns the local name received in advertising packet.
So I created an attribute called "advertsingName" in class Peripheral to hold that information:

private BluetoothDevice device;
private byte[] advertisingData;
private String advertsingName = null;
private int advertisingRSSI;
private boolean autoconnect = false;
private boolean connected = false;
private boolean connecting = false;
private ConcurrentLinkedQueue<BLECommand> commandQueue = new ConcurrentLinkedQueue<BLECommand>();
private final Map<Integer, L2CAPContext> l2capContexts = new HashMap<Integer, L2CAPContext>();
private final AtomicBoolean bleProcessing = new AtomicBoolean();

Then, I changed the constructor from:

public Peripheral(BluetoothDevice device, int advertisingRSSI, byte[] scanRecord) {
        this.device = device;
        this.advertisingRSSI = advertisingRSSI;
        this.advertisingData = scanRecord;
}

to

public Peripheral(BluetoothDevice device, int advertisingRSSI, byte[] scanRecord, String advertsingName) {
      this.device = device;
      this.advertisingRSSI = advertisingRSSI;
      this.advertisingData = scanRecord;
      this.advertsingName = advertsingName;
}

And finally, I changed method asJSONObject from:

public JSONObject asJSONObject()  {

	JSONObject json = new JSONObject();

	try {
		json.put("name", device.getName());
		json.put("id", device.getAddress()); // mac address
		if (advertisingData != null) {
			json.put("advertising", byteArrayToJSON(advertisingData));
		}
		// TODO real RSSI if we have it, else
		if (advertisingRSSI != FAKE_PERIPHERAL_RSSI) {
			json.put("rssi", advertisingRSSI);
		}
	} catch (JSONException e) { // this shouldn't happen
		e.printStackTrace();
	}

	return json;
}

to

public JSONObject asJSONObject()  {

	JSONObject json = new JSONObject();

	try {
		if(this.advertsingName == null)
			json.put("name", device.getName());
		else
			json.put("name", this.advertsingName);

		json.put("id", device.getAddress()); // mac address
		if (advertisingData != null) {
			json.put("advertising", byteArrayToJSON(advertisingData));
		}
		// TODO real RSSI if we have it, else
		if (advertisingRSSI != FAKE_PERIPHERAL_RSSI) {
			json.put("rssi", advertisingRSSI);
		}
	} catch (JSONException e) { // this shouldn't happen
		e.printStackTrace();
	}

	return json;
}

I also changed the onScanResult() in BLECentralPlugin.java file from:

public void onScanResult(int callbackType, ScanResult result) {
	LOG.w(TAG, "Scan Result");
	super.onScanResult(callbackType, result);
	BluetoothDevice device = result.getDevice();
	String address = device.getAddress();
	boolean alreadyReported = peripherals.containsKey(address) && !peripherals.get(address).isUnscanned();

	if (!alreadyReported) {

		Peripheral peripheral = new Peripheral(device, result.getRssi(), result.getScanRecord().getBytes());
		peripherals.put(device.getAddress(), peripheral);

		if (discoverCallback != null) {
			PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, peripheral.asJSONObject());
			pluginResult.setKeepCallback(true);
			discoverCallback.sendPluginResult(pluginResult);
		}

	} else {
		Peripheral peripheral = peripherals.get(address);
		if (peripheral != null) {
			peripheral.update(result.getRssi(), result.getScanRecord().getBytes());
			if (reportDuplicates && discoverCallback != null) {
				PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, peripheral.asJSONObject());
				pluginResult.setKeepCallback(true);
				discoverCallback.sendPluginResult(pluginResult);
			}
		}
	}
}

to

public void onScanResult(int callbackType, ScanResult result) {
	LOG.w(TAG, "Scan Result");
	super.onScanResult(callbackType, result);
	BluetoothDevice device = result.getDevice();
	String address = device.getAddress();
	boolean alreadyReported = peripherals.containsKey(address) && !peripherals.get(address).isUnscanned();

	if (!alreadyReported) {

		Peripheral peripheral = new Peripheral(device, result.getRssi(), result.getScanRecord().getBytes(), result.getScanRecord().getDeviceName());
		peripherals.put(device.getAddress(), peripheral);

		if (discoverCallback != null) {
			PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, peripheral.asJSONObject());
			pluginResult.setKeepCallback(true);
			discoverCallback.sendPluginResult(pluginResult);
		}

	} else {
		Peripheral peripheral = peripherals.get(address);
		if (peripheral != null) {
			peripheral.update(result.getRssi(), result.getScanRecord().getBytes());
			if (reportDuplicates && discoverCallback != null) {
				PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, peripheral.asJSONObject());
				pluginResult.setKeepCallback(true);
				discoverCallback.sendPluginResult(pluginResult);
			}
		}
	}
}

Now I always have the local name updated during scan, even just after I have writen a new value to it.

Is there any other way to have the local name updated during scan instead having a cached info?

Thanks in advance!

@peitschie
Copy link
Collaborator

Thanks for the tip and code samples @murilommp

I'll have a look... it seems like something we should easily support in the plugin.

@GerardoPrototype
Copy link

The same thing happened to me, I performed a scan and changed the name.
On my Android the name was updated but my iPhone was not. I restarted the phone but it still showed the same name.

@peitschie
Copy link
Collaborator

@GerardoPrototype I think you're hitting an iOS caching issue. The specific code suggestion here is for Android only, which sounds like it's already working correctly for you.

iOS does a lot of caching. For some good info, check out: https://developer.apple.com/forums/thread/19381

@peitschie peitschie changed the title Local name is not always updated during Scan [Android] Local name is not always updated during Scan Nov 16, 2023
akrcc pushed a commit to akrcc/cordova-plugin-ble-central that referenced this issue Dec 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants