• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.github.google.bumble.btbench
16 
17 import android.bluetooth.BluetoothSocket
18 import java.io.IOException
19 import java.nio.ByteBuffer
20 import java.util.logging.Logger
21 import kotlin.math.min
22 
23 private val Log = Logger.getLogger("btbench.packet")
24 
25 fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
26 
27 abstract class Packet(val type: Int, val payload: ByteArray = ByteArray(0)) {
28     companion object {
29         const val RESET = 0
30         const val SEQUENCE = 1
31         const val ACK = 2
32 
33         const val LAST_FLAG = 1
34 
fromnull35         fun from(data: ByteArray): Packet {
36             return when (data[0].toInt()) {
37                 RESET -> ResetPacket()
38                 SEQUENCE -> SequencePacket(
39                     data[1].toInt(),
40                     ByteBuffer.wrap(data, 2, 4).getInt(),
41                     data.sliceArray(6..<data.size)
42                 )
43 
44                 ACK -> AckPacket(data[1].toInt(), ByteBuffer.wrap(data, 2, 4).getInt())
45                 else -> GenericPacket(data[0].toInt(), data.sliceArray(1..<data.size))
46             }
47         }
48     }
49 
toBytesnull50     open fun toBytes(): ByteArray {
51         return ByteBuffer.allocate(1 + payload.size).put(type.toByte()).put(payload).array()
52     }
53 }
54 
55 class GenericPacket(type: Int, payload: ByteArray) : Packet(type, payload)
56 class ResetPacket : Packet(RESET)
57 
58 class AckPacket(val flags: Int, val sequenceNumber: Int) : Packet(ACK) {
toBytesnull59     override fun toBytes(): ByteArray {
60         return ByteBuffer.allocate(1 + 1 + 4).put(type.toByte()).put(flags.toByte())
61             .putInt(sequenceNumber).array()
62     }
63 }
64 
65 class SequencePacket(val flags: Int, val sequenceNumber: Int, payload: ByteArray) :
66     Packet(SEQUENCE, payload) {
toBytesnull67     override fun toBytes(): ByteArray {
68         return ByteBuffer.allocate(1 + 1 + 4 + payload.size).put(type.toByte()).put(flags.toByte())
69             .putInt(sequenceNumber).put(payload).array()
70     }
71 }
72 
73 abstract class PacketSink {
onPacketnull74     fun onPacket(packet: Packet) {
75         when (packet) {
76             is ResetPacket -> onResetPacket()
77             is AckPacket -> onAckPacket()
78             is SequencePacket -> onSequencePacket(packet)
79         }
80     }
81 
onResetPacketnull82     abstract fun onResetPacket()
83     abstract fun onAckPacket()
84     abstract fun onSequencePacket(packet: SequencePacket)
85 }
86 
87 interface DataSink {
88     fun onData(data: ByteArray)
89 }
90 
91 interface PacketIO {
92     var packetSink: PacketSink?
sendPacketnull93     fun sendPacket(packet: Packet)
94 }
95 
96 class StreamedPacketIO(private val dataSink: DataSink) : PacketIO {
97     private var bytesNeeded: Int = 0
98     private var rxPacket: ByteBuffer? = null
99     private var rxHeader = ByteBuffer.allocate(2)
100 
101     override var packetSink: PacketSink? = null
102 
103     fun onData(data: ByteArray) {
104         var current = data
105         while (current.isNotEmpty()) {
106             if (bytesNeeded > 0) {
107                 val chunk = current.sliceArray(0..<min(bytesNeeded, current.size))
108                 rxPacket!!.put(chunk)
109                 current = current.sliceArray(chunk.size..<current.size)
110                 bytesNeeded -= chunk.size
111                 if (bytesNeeded == 0) {
112                     // Packet completed.
113                     //Log.fine("packet complete: ${current.toHex()}")
114                     packetSink?.onPacket(Packet.from(rxPacket!!.array()))
115 
116                     // Reset.
117                     reset()
118                 }
119             } else {
120                 val headerBytesNeeded = 2 - rxHeader.position()
121                 val headerBytes = current.sliceArray(0..<min(headerBytesNeeded, current.size))
122                 current = current.sliceArray(headerBytes.size..<current.size)
123                 rxHeader.put(headerBytes)
124                 if (rxHeader.position() != 2) {
125                     return
126                 }
127                 bytesNeeded = rxHeader.getShort(0).toInt()
128                 if (bytesNeeded == 0) {
129                     Log.warning("found 0 size packet!")
130                     reset()
131                     return
132                 }
133                 rxPacket = ByteBuffer.allocate(bytesNeeded)
134             }
135         }
136     }
137 
138     private fun reset() {
139         rxPacket = null
140         rxHeader.position(0)
141     }
142 
143     override fun sendPacket(packet: Packet) {
144         val packetBytes = packet.toBytes()
145         val packetData =
146             ByteBuffer.allocate(2 + packetBytes.size).putShort(packetBytes.size.toShort())
147                 .put(packetBytes).array()
148         dataSink.onData(packetData)
149     }
150 }
151 
152 class SocketDataSink(private val socket: BluetoothSocket) : DataSink {
onDatanull153     override fun onData(data: ByteArray) {
154         socket.outputStream.write(data)
155     }
156 }
157 
158 class SocketDataSource(
159     private val socket: BluetoothSocket,
160     private val onData: (data: ByteArray) -> Unit
161 ) {
receivenull162     fun receive() {
163         val buffer = ByteArray(4096)
164         do {
165             try {
166                 val bytesRead = socket.inputStream.read(buffer)
167                 if (bytesRead <= 0) {
168                     break
169                 }
170                 onData(buffer.sliceArray(0..<bytesRead))
171             } catch (error: IOException) {
172                 Log.warning("IO Exception: $error")
173                 break
174             }
175         } while (true)
176         Log.info("end of stream")
177     }
178 }