Krolyxon
Commit 832675a

Branches

Refs →

budctlandroidappsrcmainkotliniogithubkrolyxonbudctlLink.kt

59 lines · 1.7 KB · Kotlin

1package io.github.krolyxon.budctl
2
3import java.io.Closeable
4import java.io.IOException
5import java.io.InputStream
6import java.io.OutputStream
7import java.util.concurrent.LinkedBlockingQueue
8import java.util.concurrent.TimeUnit
9import kotlin.concurrent.thread
10
11/**
12 * One RFCOMM link to the buds. A Bluetooth socket's input stream has no read timeout, so a thread
13 * of its own reads it into a queue and [recv] waits on the queue. Closing the socket ends that
14 * thread.
15 */
16class Link(private val input: InputStream, private val output: OutputStream, private val socket: Closeable) {
17 private val received = LinkedBlockingQueue<ByteArray>()
18
19 init {
20 thread(name = "budctl-link", isDaemon = true) {
21 val buffer = ByteArray(1024)
22 try {
23 while (true) {
24 val n = input.read(buffer)
25 if (n < 0) break
26 if (n > 0) received.put(buffer.copyOf(n))
27 }
28 } catch (_: IOException) {
29 // Closed, by us or by the buds.
30 }
31 received.put(CLOSED)
32 }
33 }
34
35 fun send(bytes: ByteArray) {
36 output.write(bytes)
37 output.flush()
38 }
39
40 /** The next bytes; null if none came within [timeoutMs]; an empty array once the link is closed. */
41 fun recv(timeoutMs: Long): ByteArray? {
42 val bytes = received.poll(timeoutMs, TimeUnit.MILLISECONDS) ?: return null
43 // Closed stays closed, for the next call too.
44 if (bytes.isEmpty()) received.put(CLOSED)
45 return bytes
46 }
47
48 fun close() {
49 try {
50 socket.close()
51 } catch (_: IOException) {
52 // Already closed.
53 }
54 }
55
56 private companion object {
57 val CLOSED = ByteArray(0)
58 }
59}