Warning, /network/kdeconnect-android/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.kt is written in an unsupported language. File is not indexed.
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Saikrishna Arcot <saiarcot895@gmail.com> 0003 * SPDX-FileCopyrightText: 2024 Rob Emery <git@mintsoft.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 package org.kde.kdeconnect.Backends.BluetoothBackend 0008 0009 import android.bluetooth.BluetoothDevice 0010 import android.content.Context 0011 import android.util.Log 0012 import androidx.annotation.WorkerThread 0013 import org.json.JSONException 0014 import org.json.JSONObject 0015 import org.kde.kdeconnect.Backends.BaseLink 0016 import org.kde.kdeconnect.Device 0017 import org.kde.kdeconnect.DeviceInfo 0018 import org.kde.kdeconnect.NetworkPacket 0019 import java.io.IOException 0020 import java.io.InputStream 0021 import java.io.InputStreamReader 0022 import java.io.OutputStream 0023 import java.io.Reader 0024 import java.util.UUID 0025 import kotlin.text.Charsets.UTF_8 0026 0027 class BluetoothLink(context: Context?, connection: ConnectionMultiplexer, input: InputStream, output: OutputStream, remoteAddress: BluetoothDevice, deviceInfo: DeviceInfo, linkProvider: BluetoothLinkProvider) : BaseLink(context!!, linkProvider) { 0028 private val connection: ConnectionMultiplexer? 0029 private val input: InputStream 0030 private val output: OutputStream 0031 private val remoteAddress: BluetoothDevice 0032 private val linkProvider: BluetoothLinkProvider 0033 private val deviceInfo: DeviceInfo 0034 private var continueAccepting = true 0035 private val receivingThread = Thread(object : Runnable { 0036 override fun run() { 0037 val sb = StringBuilder() 0038 try { 0039 val reader: Reader = InputStreamReader(input, UTF_8) 0040 val buf = CharArray(512) 0041 while (continueAccepting) { 0042 while (sb.indexOf("\n") == -1 && continueAccepting) { 0043 var charsRead: Int 0044 if (reader.read(buf).also { charsRead = it } > 0) { 0045 sb.append(buf, 0, charsRead) 0046 } 0047 if (charsRead < 0) { 0048 disconnect() 0049 return 0050 } 0051 } 0052 if (!continueAccepting) break 0053 val endIndex = sb.indexOf("\n") 0054 if (endIndex != -1) { 0055 val message = sb.substring(0, endIndex + 1) 0056 sb.delete(0, endIndex + 1) 0057 processMessage(message) 0058 } 0059 } 0060 } catch (e: IOException) { 0061 Log.e("BluetoothLink/receiving", "Connection to " + remoteAddress.address + " likely broken.", e) 0062 disconnect() 0063 } 0064 } 0065 0066 private fun processMessage(message: String) { 0067 val np: NetworkPacket 0068 np = try { 0069 NetworkPacket.unserialize(message) 0070 } catch (e: JSONException) { 0071 Log.e("BluetoothLink/receiving", "Unable to parse message.", e) 0072 return 0073 } 0074 if (np.hasPayloadTransferInfo()) { 0075 try { 0076 val transferUuid = UUID.fromString(np.payloadTransferInfo.getString("uuid")) 0077 val payloadInputStream = connection.getChannelInputStream(transferUuid) 0078 np.payload = NetworkPacket.Payload(payloadInputStream, np.payloadSize) 0079 } catch (e: Exception) { 0080 Log.e("BluetoothLink/receiving", "Unable to get payload", e) 0081 } 0082 } 0083 packetReceived(np) 0084 } 0085 }) 0086 0087 init { 0088 this.connection = connection 0089 this.input = input 0090 this.output = output 0091 this.deviceInfo = deviceInfo 0092 this.remoteAddress = remoteAddress 0093 this.linkProvider = linkProvider 0094 } 0095 0096 fun startListening() { 0097 receivingThread.start() 0098 } 0099 0100 override fun getName(): String { 0101 return "BluetoothLink" 0102 } 0103 0104 override fun getDeviceInfo(): DeviceInfo { 0105 return deviceInfo 0106 } 0107 0108 override fun disconnect() { 0109 if (connection == null) { 0110 return 0111 } 0112 continueAccepting = false 0113 try { 0114 connection.close() 0115 } catch (ignored: IOException) { 0116 } 0117 linkProvider.disconnectedLink(this, remoteAddress) 0118 } 0119 0120 @Throws(JSONException::class, IOException::class) 0121 private fun sendMessage(np: NetworkPacket) { 0122 val message = np.serialize().toByteArray(UTF_8) 0123 output.write(message) 0124 } 0125 0126 @WorkerThread 0127 @Throws(IOException::class) 0128 override fun sendPacket(np: NetworkPacket, callback: Device.SendPacketStatusCallback, sendPayloadFromSameThread: Boolean): Boolean { 0129 // sendPayloadFromSameThread is ignored, we always send from the same thread! 0130 0131 return try { 0132 var transferUuid: UUID? = null 0133 if (np.hasPayload()) { 0134 transferUuid = connection!!.newChannel() 0135 val payloadTransferInfo = JSONObject() 0136 payloadTransferInfo.put("uuid", transferUuid.toString()) 0137 np.payloadTransferInfo = payloadTransferInfo 0138 } 0139 sendMessage(np) 0140 if (transferUuid != null) { 0141 try { 0142 connection!!.getChannelOutputStream(transferUuid).use { payloadStream -> 0143 val BUFFER_LENGTH = 1024 0144 val buffer = ByteArray(BUFFER_LENGTH) 0145 var bytesRead: Int 0146 var progress: Long = 0 0147 val stream = np.payload.inputStream 0148 while (stream.read(buffer).also { bytesRead = it } != -1) { 0149 progress += bytesRead.toLong() 0150 payloadStream.write(buffer, 0, bytesRead) 0151 if (np.payloadSize > 0) { 0152 callback.onPayloadProgressChanged((100 * progress / np.payloadSize).toInt()) 0153 } 0154 } 0155 payloadStream.flush() 0156 } 0157 } catch (e: Exception) { 0158 callback.onFailure(e) 0159 return false 0160 } 0161 } 0162 callback.onSuccess() 0163 true 0164 } catch (e: Exception) { 0165 callback.onFailure(e) 0166 false 0167 } 0168 } 0169 }