NordicSemiconductor / Android-DFU-Library

Device Firmware Update library and Android app
http://www.nordicsemi.com/dfu
BSD 3-Clause "New" or "Revised" License
763 stars 269 forks source link

Android version compatibility problem ? Cannot connect to some devices #120

Closed ArilesGit closed 5 years ago

ArilesGit commented 6 years ago

Hi !

I've been struggling with this the last two days and I can't find any clue. I hope someone here can give me a hint, I would really appreciate it.

I have 4 devices :

After I connect with the bluetooth device to update, I send a few specific commands to make go to DFU mode, then it disconnects after a few seconds. All ok up to this step.

Then, I use Android-DFU-Library to start the fw update process. It works greate with the android 5.1 samsung device but fails to connect [status 133] with the other 3 devices. Any idea what's happening ?

Thank you very much for your help. And thank you for making this library available, of course.

philips77 commented 6 years ago

133 happens for many reasons. Usually, the device may not be available. The Bluetooth adapter may also need some more time before connecting, after you switched to the bootloader mode.

In whatever moment does the error occur? Do you have to wait for it 20 sec, or does it happen just after connect attempt? Did you try doing it manually in nRF Connect?

ArilesGit commented 6 years ago

Thank you for the quick reply.

Yes, it works when à do it manually in nRF Connect. It even works when I use my app to enable DFU mode first, then nRF Connect to connect to the device right after a little scan to find the device.

The way I do it in my app is : after DFU mode is enabled, I scan for 10s in order to find the new mac address and to make sure there is only one device in DFU mode at a time. Then, I use this library to do the actual firmware update.

It takes about 30s to throw the status=133 error, so it looks like it is because of a timeout.

07-26 10:49:47.525 ... D/BluetoothGatt: connect() - device: ... , auto: false
07-26 10:50:17.543 ... D/BluetoothGatt: onClientConnectionState() - status=133 clientIf=5...

Some more logs :

07-26 10:49:46.590 ... I/DfuBaseService: DFU service created. Version: 1.6.1
07-26 10:49:47.524 ... I/DfuBaseService: Connecting to the device...
07-26 10:50:17.543 ... E/DfuBaseService: Connection state change error: 133 newState: 0
07-26 10:50:17.544 ... E/DfuBaseService: Device not reachable. Check if the device with address [MAC] is in range, is advertising and is connectable
07-26 10:50:17.549 ... I/DfuBaseService: Refreshing result: true
07-26 10:50:17.549 ... I/DfuBaseService: Cleaning up...
07-26 10:50:18.186 ... I/DfuBaseService: DFU service destroyed
ArilesGit commented 6 years ago

Hi @philips77 ! Did you have the time to read my last comment ?

Also, I asked for help on stackoverflow too, I hope more people can see this.

Thank you anyway.

philips77 commented 6 years ago

Wops! Your question waited here a bit! I'll try to come to you soon.

philips77 commented 5 years ago

30 sec is indeed a timeout. This may be because the device is not available, is already connected to some other device, or you are using a wrong device address. You say you scan before starting the library, do you find the device there and use this object in the DFU initiator? Android may not connect to a device if it has private address type and it has not been scanned since last Bluetooth restart. For example, if you'd do:

BluetoothDevice device = adapter.getRemoteDevice(someAddress);
BluetoothGatt gatt = device.connectGatt(context, false, callback);

the connection would fail, as Android would assume public address type. The DFU bootloader changes the address when in bootloader mode, so it needs to be scanned.

Why don't you use the DFU library to switch to bootloader mode automatically? Do you have a custom way to do this?

ArilesGit commented 5 years ago

Hi @philips77, thanks for coming back to this issue !

This may be because the device is not available, is already connected to some other device, or you are using a wrong device address.

I don't think I missed any of the above. It always works with the same samsung device and never with the others. Also, it works when I do it through nrfConnect.

You say you scan before starting the library, do you find the device there and use this object in the DFU initiator?

Yes, and yes.

Why don't you use the DFU library to switch to bootloader mode automatically? Do you have a custom way to do this?

Yes. That's because the device needs a special constructor defined code to a particular gatt characteristic to make it enter DFU mode.

philips77 commented 5 years ago

nRF Connect scans and connects to the device before starting DFU. Try doing the same in your app. After you scan, connect to it (just to make sure it's connectable), and without disconnecting start the DFU. If you manage to connect to the device, after you've scanned for it, DFU will connect as well (there will be 2 virtual connections to the same device, make sure you ignore all notifications coming from it). You may disconnect and close yours anytime or wait until it disconnects on its own when the DFU is complete.

ArilesGit commented 5 years ago

Ok, I'm already scanning. As far as I can remember, the only thing I don't do is to connect before starting DFU. I'll try this soon and report back. Thank you @philips77 !

ArilesGit commented 5 years ago

Hi !

I still get the same error. I works through nrfConnect but not in my app and I can't see what I'm doing wrong right now. I'll be doing more tests today.

After DFU mode is enabled, I scan and then try to connect like this:

            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                if (results.size() == 1) {
                    BluetoothDevice device = results.get(0).getDevice();
                    BluetoothGatt gatt = device.connectGatt(context, false, callBack);

                 } else if (...) { //Other cases: too many devices or none 
                      ...
                 }
            }

Edit: I also tried with the adapter's getRemoteDevice(mac) method. It doesn't work either when I enable DFU mode through nrfConnect then use the app to connect. I'm a bit lost here... :slightly_frowning_face:

philips77 commented 5 years ago

Did you try without report delay in the scanner? Or connecting from a UI thread? Or try stopping scanning before connecting. But as you are having troubles connecting from DFU lib and from your code, and in both cases you get 133 after 30 sec, that means that the device is unreachable. It may be this public-private type address issue. Are you sure you have the BLUETOOTH, BLUETOOTHADMIN and LOCATION* permissions in Android Manifest? And you requested Location runtime permission in the app?

ArilesGit commented 5 years ago

I have BLUETOOTH, BLUETOOTH_ADMIN and ACCESS_COARSE_LOCATION and I'm able to connect to BLE devices without any issue. The only time I get into this problem is in the case of DFU mode enabled.

I'm already waiting till the end of the scan. I user the scan parameters below and stop the scan as soon as I get batch result.

  ScanSettings scanSettings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setReportDelay(scanDuration)
            .build();

So, now I will try without report delay and/or from a UI thread, as you suggested and report back. Again, thank you for your time @philips77, I appreciate it !

ArilesGit commented 5 years ago

Man, it finally works ! :tada: It was because of the report delay. Thank you very much @philips77 !

Can you explain why that happened ? And why some devices behave differently than others ?

philips77 commented 5 years ago

My intuition tells my, that while scanning with report delay used offloading scanning, that is Android registers a request in the low power controller and receives updates every given number of milliseconds, the security table may not be updated for devices obtained this way. Android doesn't know whether the addressee are public or private, so assumes public. This may work on some devices. Did you check if your phone returns true from BluetoothAdapter.isOffloadedBatchingSupported()?

philips77 commented 5 years ago

Nevertheless, it's good to know. I'll add it to the BLE on Android document v2, that I'm preparing.

ArilesGit commented 5 years ago

Did you check if your phone returns true from BluetoothAdapter.isOffloadedBatchingSupported()?

Yes, it does indeed return true.

Thank you very much, you've been really helpful :+1:

fengqiangboy commented 5 years ago

How to set ScanSettings in DfuServiceInitiator? Or it can be set in other place?

ArilesGit commented 5 years ago

Don't use ScanSettings, just find the device and pass its mac address to the DfuServiceInitiator constructor.

BluetoothLeScanner scanner = adapter.getBluetoothLeScanner();
scanner.startScan(scanCallback);

And then, filter the scan result yourself to make sure you get the right device in the callback:

    @Override
    public void onScanResult(int callbackType, ScanResult result) {
         //For example, check device mac here
    }