android device reboots during data transfer after some time or reconnecting after recent connection
yamin8000 opened this issue · comments
My issue may look unprofessional, especially by the way I'm presenting it, but somehow, on some devices (Honor/Car Navigation Android devices), this library keeps rebooting the device, Sometimes during data transfer and sometimes after reconnecting after a recent disconnection, On many other devices it's completely OK.
I tried to find the exact Exception but until now I failed to catch an Exception, maybe even it doesn't throw anything at all.
You may ask for a code sample which I can provide but it's nothing unordinary and I'm using SerialInputOutputManager.Listener
and onNewData
listener.
I tried to catch exceptions and send them to Crashlytics and a custom exception-saving mechanism but until now nothing is thrown. I think saving exceptions to a file inside the device is a better idea than even monitoring the device using Wifi ADB.
I never expected a simple code could cause an Android device to reboot, does anybody had a similar issue working with USB Serial?
I suspected that the USB device (Arduino Uno) that sends data maybe is the issue and uses too much power and changed it (tried with another SMD Arduino and an ESP8266) and also used another simple app to monitor data and it works fine.
A portion of my code:
class UsbHelper(
private val context: Context,
private val stateCallback: UsbStateCallback,
private val bpDataCallback: BloodPressureDataCallback? = null,
private val dataCallback: MainDataCallback? = null
) {
private val permissionIntent = PendingIntent.getBroadcast(
context,
0,
Intent(USB.INTENT_ACTION_GRANT_USB),
PendingIntent.FLAG_MUTABLE
)
private var port: UsbSerialPort? = null
private var connection: UsbDeviceConnection? = null
private var usbManager: UsbManager
private var usbDevice: UsbDevice? = null
private var ioManager: SerialInputOutputManager? = null
private var statusCallback: ((String) -> Unit)? = null
private val serialListener = object : SerialInputOutputManager.Listener {
private var buffer = ""
override fun onNewData(bytes: ByteArray?) {
if (bytes != null) {
statusCallback?.invoke("${context.getString(R.string.data_received)} ${now()}")
val data = String(bytes)
buffer += data
val mainDataMatch = USB.Protocol.MAIN_DATA_REGEX.find(buffer)
val bpIntermediateMatch = USB.Protocol.BP_INTERMEDIATE_REGEX.find(buffer)
if (mainDataMatch != null) {
processData(mainDataMatch.value)
buffer = ""
log("valid main data: ${mainDataMatch.value}")
//statusCallback?.invoke("valid main data: ${mainDataMatch.value}")
} else if (bpIntermediateMatch != null) {
processData("${bpIntermediateMatch.value}f")
buffer = ""
log("valid bp intermediate data: ${bpIntermediateMatch.value}")
//statusCallback?.invoke("valid bp intermediate data: ${bpIntermediateMatch.value}")
} //else statusCallback?.invoke("raw: $data")
log("raw data: $data")
}
}
override fun onRunError(e: Exception?) {
log(e?.message ?: "")
}
}
init {
usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
try {
context.registerReceiver(
permissionReceiver(),
IntentFilter(USB.INTENT_ACTION_GRANT_USB)
)
context.registerReceiver(
attachReceiver(),
IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED)
)
context.registerReceiver(
disconnectReceiver(),
IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED)
)
} catch (e: Exception) {
reportException(e)
log(e.stackTraceToString())
}
}
private fun attachReceiver() = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
stateCallback.onAttach()
refreshWithReview()
}
}
fun setStatusCallback(callback: (String) -> Unit) {
statusCallback = callback
}
private fun disconnectReceiver() = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
stop()
stateCallback.onDisconnected()
}
}
private fun permissionReceiver() = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == USB.INTENT_ACTION_GRANT_USB)
handleUsbPermissionIntent(intent)
}
}
private fun handleUsbPermissionIntent(
intent: Intent
) {
val isGranted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
log("USB granted: $isGranted")
if (isGranted)
statusCallback?.invoke(context.getString(R.string.usb_permission_granted))
val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
connect(device)
log("USB permission for device:${device?.deviceName} is granted? $isGranted")
}
fun connect(
deviceFromIntent: UsbDevice?
) {
log("Connecting")
val drivers = getDrivers()
val probedDevice = drivers.firstOrNull()?.device
val port = drivers.firstOrNull()?.ports?.firstOrNull()
usbDevice = deviceFromIntent ?: probedDevice
this.port = port
if (usbDevice != null && port != null) {
if (usbManager.hasPermission(usbDevice)) {
connection = usbManager.openDevice(usbDevice)
handleDataTransfer()
stateCallback.onConnected()
} else usbManager.requestPermission(usbDevice, permissionIntent)
} else stateCallback.onNoDevice()
}
private fun getDrivers(): List<UsbSerialDriver> {
val drivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager)
return if (drivers == null || drivers.isEmpty())
getCustomProber().findAllDrivers(usbManager)
else drivers
}
private fun handleDataTransfer() {
log("Transferring data")
openPort()
setPortParameters()
ioManager = SerialInputOutputManager(port, serialListener)
ioManager?.setThreadPriority(10)
ioManager?.readTimeout = 1
ioManager?.start()
}
private fun openPort() {
try {
port?.open(connection)
} catch (e: IOException) {
reportException(e)
val failedToOpenPortMessage = e.message ?: "Failed to open port"
log(failedToOpenPortMessage)
log(e.stackTraceToString())
statusCallback?.invoke(failedToOpenPortMessage)
}
}
private fun setPortParameters() {
try {
port?.setParameters(
9600,
UsbSerialPort.DATABITS_8,
UsbSerialPort.STOPBITS_1,
UsbSerialPort.PARITY_NONE
)
port?.dtr = true
port?.rts = true
} catch (e: Exception) {
reportException(e)
val failedToSetPortParameters = e.message ?: "Failed to set port parameters"
log(failedToSetPortParameters)
log(e.stackTraceToString())
statusCallback?.invoke(failedToSetPortParameters)
}
}
private fun processData(
rawData: String
) {
val data = rawData.removePrefix(USB.Protocol.START.toString())
when (rawData.last()) {
USB.Protocol.HEIGHT_WEIGHT_TEMPERATURE -> {
processWeightHeightTemperature(data)
}
USB.Protocol.BLOOD_PRESSURE -> processBloodPressure(data)
USB.Protocol.BLOOD_OXYGEN -> processWeightHeightOxygen(data)
else -> {}
}
}
private fun processWeightHeightOxygen(
data: String
) {
processWeightHeight(data)
processOxygen(data)
}
private fun processOxygen(data: String) {
dataCallback?.onNewOxygen(getNewOxygen(data))
}
private fun getNewOxygen(
data: String
) = data.substring(USB.Protocol.OXYGEN_RANGE)
.sanitizeForNumberParsing()
.toIntOrNull() ?: 0
private fun processBloodPressure(
data: String
) {
bpDataCallback?.onNewBloodPressure(getNewBloodPressure(data))
}
private fun getNewBloodPressure(
data: String
) = BloodPressure(
sys = data.substring(USB.Protocol.SYS_RANGE).parseThreeDigitFloat(),
dia = data.substring(USB.Protocol.DIA_RANGE).parseThreeDigitFloat(),
pulse = getNewPulse(data)
)
private fun getNewPulse(
data: String
) = data.substring(USB.Protocol.PULSE_RANGE)
.sanitizeForNumberParsing()
.toIntOrNull() ?: 0
private fun processWeightHeightTemperature(
data: String
) {
processWeightHeight(data)
processTemperature(data)
}
private fun processTemperature(
data: String
) {
val newTemperature = data.substring(USB.Protocol.TEMPERATURE_RANGE).parseFloat()
dataCallback?.onNewTemperature(newTemperature)
}
private fun processWeightHeight(
data: String
) {
val temp = getWeightAndHeight(data.take(USB.Protocol.WEIGHT_HEIGHT_SIZE))
dataCallback?.onNewWeight(temp.weight)
dataCallback?.onNewHeight(temp.height)
}
private fun getWeightAndHeight(
data: String
) = WeightAndHeight(
weight = data.substring(USB.Protocol.WEIGHT_RANGE).parseFloat(),
height = data.substring(USB.Protocol.HEIGHT_RANGE).parseFloat()
)
fun print() {
if (usbDevice != null) {
statusCallback?.invoke(context.getString(R.string.printing))
write(USB.Protocol.Commands.PRINT)
} else stateCallback.onNoDevice()
}
fun refreshWithReview() {
refresh()
write(USB.Protocol.Commands.REVIEW)
}
fun refresh() {
stop()
connect(usbDevice)
log("refreshing")
}
fun stop() {
usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
ioManager?.listener = null
ioManager = null
usbDevice = null
try {
port?.close()
} catch (e: IOException) {
reportException(e)
log(e.message ?: "Port: $port already closed.")
}
port = null
connection?.close()
connection = null
}
fun write(data: String) {
ioManager?.writeAsync(data.toByteArray())
}
}
Could be to high power consumption or a bug in the Linux kernel of the particular Android device, both unlikely to be caught on application level.
Could be to high power consumption or a bug in the Linux kernel of the particular Android device, both unlikely to be caught on application level.
It's silly but the problem was fixed for now when the power adapter of that particular Android device was changed.
Thank you.