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 }