Android-DFU-Library icon indicating copy to clipboard operation
Android-DFU-Library copied to clipboard

DFU problems on Huawei devices

Open hyyu opened this issue 5 years ago • 7 comments

Hi,

I'm developing an app using the nordic android DFU library, and I try to update the user device's bootloader + firmware without user interaction.

The issue I'm facing is that it is partially functional: I get many 133 and 129 Bluetooth errors while testing, but also the "DFU CHARACTERISTICS NOT FOUND" one (4102) on Huawei devices. The other devices I've tested don't have that much errors.

Is there a known problem with Huawei devices Bluetooth or did I do something wrong?

My code for both updates below:

Code to start the Bootloader update:

	private fun startBootloaderDFUProcess() {
		Log.i(TAG, "Starting Bootloader DFU update")

		val starter = DfuServiceInitiator(deviceMacAddress!!)
			.setDeviceName(deviceName!!)
			.setKeepBond(true)

		starter.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true)
		starter.setZip(bootloaderPathName!!)
		starter.start(context!!, DfuService::class.java)

		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
			DfuServiceInitiator.createDfuNotificationChannel(context!!)
		}

	}

Code for the DfuProgressListener for the Bootloader:

	private val dfuBootloaderProgressListener = object : DfuProgressListenerAdapter() {
		override fun onEnablingDfuMode(deviceAddress: String) {
			super.onEnablingDfuMode(deviceAddress)
			dfu_description.text = getString(R.string.dfu_initializing_update)
		}

		override fun onProgressChanged(
			deviceAddress: String,
			percent: Int,
			speed: Float,
			avgSpeed: Float,
			currentPart: Int,
			partsTotal: Int
		) {
			super.onProgressChanged(deviceAddress, percent, speed, avgSpeed, currentPart, partsTotal)
			mPercent = percent / 2
			dfu_loading_bar.progress = mPercent
			tv_update_percent.text = getString(R.string.dfu_percent, mPercent)
		}

		override fun onError(deviceAddress: String, error: Int, errorType: Int, message: String?) {
			super.onError(deviceAddress, error, errorType, message)
			Log.v(TAG, "onError : $error type : $errorType message : $message")
			when (error) {
				BOOTLOADER_FW_VERSION_FAILURE -> {
					viewModel.launchScanForDfuTarg()
				}
				DFU_DISCONNECTED -> {
					startBootloaderDFUProcess()
				}
				else -> {
					PrefEnovap.saveIsBackBlockedToPref(false)
					displayErrorOnSnackbarLong(dfu_base, getString(R.string.dfu_error_disconnection))
					(activity as MainActivity).onBackPressed()
				}
			}
		}

		override fun onDfuAborted(deviceAddress: String) {
			super.onDfuAborted(deviceAddress)
			Log.v(TAG, "dfu aborted")
		}

		override fun onFirmwareValidating(deviceAddress: String) {
			super.onFirmwareValidating(deviceAddress)
			Log.v(TAG, "onFirmwareValidating")
		}

		override fun onDeviceDisconnected(deviceAddress: String) {
			super.onDeviceDisconnected(deviceAddress)
			Log.v(TAG, "onDeviceDisconnected")
		}

		override fun onDeviceConnected(deviceAddress: String) {
			super.onDeviceConnected(deviceAddress)
			Log.v(TAG, "onDeviceConnected")
		}

		override fun onDfuProcessStarting(deviceAddress: String) {
			super.onDfuProcessStarting(deviceAddress)
			Log.v(TAG, "onDfuProcessStartingBootloader")
			if (!enovap_button_dfu.isAnimating) {
				enovap_button_dfu.startAnimation()
			}
			dfu_description.text = getString(R.string.dfu_updating)
			mPercent = 0
			dfu_loading_bar.progress = mPercent
			tv_update_percent.text = getString(R.string.dfu_percent, mPercent)
		}

		override fun onDfuProcessStarted(deviceAddress: String) {
			super.onDfuProcessStarted(deviceAddress)
			Log.v(TAG, "onDfuProcessStarted")
		}

		override fun onDeviceConnecting(deviceAddress: String) {
			super.onDeviceConnecting(deviceAddress)
			Log.v(TAG, "onDeviceConnecting")
		}

		override fun onDfuCompleted(deviceAddress: String) {
			super.onDfuCompleted(deviceAddress)
			Log.v(TAG, "onDfuCompleted")
			startFirmwareDFUProcess()
		}
	}

Code to start the Firmware update:

	private fun startFirmwareDFUProcess() {
		Log.i(TAG, "Starting Firmware DFU update")
		DfuServiceListenerHelper.registerProgressListener(context!!, dfuFirmwareProgressListener)

		val starter = DfuServiceInitiator(deviceMacAddress!!)
			.setDeviceName(deviceName!!)
			.setKeepBond(true)

		starter.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true)
		starter.setZip(firmwarePathName!!)
		starter.start(context!!, DfuService::class.java)

		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
			DfuServiceInitiator.createDfuNotificationChannel(context!!)
		}
	}

Code for the DfuProgressListener for the Firmware:

	private val dfuFirmwareProgressListener = object : DfuProgressListenerAdapter() {
		override fun onProgressChanged(
			deviceAddress: String,
			percent: Int,
			speed: Float,
			avgSpeed: Float,
			currentPart: Int,
			partsTotal: Int
		) {
			super.onProgressChanged(deviceAddress, percent, speed, avgSpeed, currentPart, partsTotal)
			enovap_button_dfu.startAnimation()
			mPercent = FIRMWARE_START_PERCENT + percent / 2
			dfu_loading_bar.progress = mPercent
			tv_update_percent.text = getString(R.string.dfu_percent, mPercent)
		}

		override fun onError(deviceAddress: String, error: Int, errorType: Int, message: String?) {
			super.onError(deviceAddress, error, errorType, message)
			Log.v(TAG, "onError : $error message : $message")
			when (error) {
				BOOTLOADER_FW_VERSION_FAILURE -> {
					viewModel.launchScanForDfuTarg()
				}
				else -> {
					DfuServiceListenerHelper.unregisterProgressListener(context!!, this)
					PrefEnovap.saveIsBackBlockedToPref(false)
					displayErrorOnSnackbarLong(dfu_base, getString(R.string.dfu_error_disconnection))
					(activity as MainActivity).onBackPressed()
				}
			}
		}

		override fun onDfuAborted(deviceAddress: String) {
			super.onDfuAborted(deviceAddress)
			Log.v(TAG, "dfu aborted")
		}

		override fun onFirmwareValidating(deviceAddress: String) {
			super.onFirmwareValidating(deviceAddress)
			Log.v(TAG, "onFirmwareValidating")
		}

		override fun onDeviceDisconnected(deviceAddress: String) {
			super.onDeviceDisconnected(deviceAddress)
			Log.v(TAG, "onDeviceDisconnected")
		}

		override fun onDeviceConnected(deviceAddress: String) {
			super.onDeviceConnected(deviceAddress)
			Log.v(TAG, "onDeviceConnected")
		}

		override fun onDfuProcessStarting(deviceAddress: String) {
			super.onDfuProcessStarting(deviceAddress)
			Log.v(TAG, "onDfuProcessStarting")
			dfu_loading_bar.progress = FIRMWARE_START_PERCENT
			tv_update_percent.text = getString(R.string.dfu_percent, FIRMWARE_START_PERCENT)
		}

		override fun onDfuProcessStarted(deviceAddress: String) {
			super.onDfuProcessStarted(deviceAddress)
			Log.v(TAG, "onDfuProcessStarted")
		}

		override fun onDeviceConnecting(deviceAddress: String) {
			super.onDeviceConnecting(deviceAddress)
			Log.v(TAG, "onDeviceConnecting")
		}

		override fun onDfuCompleted(deviceAddress: String) {
			super.onDfuCompleted(deviceAddress)
			DfuServiceListenerHelper.unregisterProgressListener(context!!, this)
			activity?.runOnUiThread {
				PrefEnovap.saveIsBackBlockedToPref(false)
				dfu_description.gone()
				dfu_icon.fadeOut {}
				dfu_icon.gone()
				dfu_title.fadeOut {}
				dfu_title.gone()
				dfu_subtitle.fadeOut {}
				dfu_subtitle.gone()
				tv_update_percent.fadeOut {}
				tv_update_percent.gone()
				dfu_loading_bar.fadeOut {}
				dfu_loading_bar.gone()
				enovap_button_dfu.fadeOut {}
				enovap_button_dfu.gone()
				enovap_button_dfu.revertAnimation()
				dfu_icon.setImageDrawable(getDrawable(context!!, R.drawable.ic_up_to_date))
				dfu_title.text = getString(R.string.enovap_up_to_date)
				dfu_subtitle.text = getString(R.string.dfu_up_to_date_description)
				Handler().postDelayed({
					dfu_icon.visible()
					dfu_icon.fadeIn {}
					dfu_title.visible()
					dfu_title.fadeIn {}
					dfu_subtitle.visible()
					dfu_subtitle.fadeIn {}
					enovap_button_dfu_finish.visible()
					enovap_button_dfu_finish.fadeIn {}
				}, 400)
			}
		}
	}

hyyu avatar May 02 '19 08:05 hyyu

I have the same problem, i am testing on Huawei y9 Prime: E/DfuBaseService: Connection state change error: 133 newState: 0 E/DfuBaseService: Device not reachable. Check if the device with address EA:87:46:6D:DC:B8 is in range, is advertising and is connectable.

animecomico avatar Mar 16 '20 19:03 animecomico

Set the device Bluetooth in mode DFU. In this mode on my device BLE the name of device and mac change (device.45667[EA:87:46:6D:DC:B8] --> device.45267[EA:87:46:6E:DC:B8]), so, when start the DfuServiceInitiator object, set the new DeviceName and MAC.

Is required set the device in DFU mode.

animecomico avatar Mar 17 '20 14:03 animecomico

.setPacketsReceiptNotificationsEnabled(true)

it work fine now,but i find other problems on Huawei devices

zhangwm1219 avatar Apr 17 '20 13:04 zhangwm1219

setPacketsReceiptNotificationsEnabled

I have the same problem. Where do I set .setPacketsReceiptNotificationsEnabled(true)?

MrHazee avatar May 13 '20 13:05 MrHazee

In the DfuServiceInitiator instance: https://github.com/NordicSemiconductor/Android-DFU-Library/blob/5c2d28e9db2ba28b3df67fd61d01a2ce1419fcf0/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java#L221

philips77 avatar May 13 '20 13:05 philips77

@philips77 When setting setPacketsReceiptNotificationsEnabled to true, what changes that fixes the issue users reported above? What I'm wondering when setting that to true do you have to ensure a notification is received before doing more work, If so, is this on the controller application to implement the notification is received?

Also, I'm curious is it be common practice to set a retry policy?

mattgraham1 avatar May 19 '21 15:05 mattgraham1

The library will do this automatically. Also, if your using non modified DFU bootloader it will send those notifications automatically.

philips77 avatar May 20 '21 18:05 philips77