1 package com.android.ndkports
2 
3 import java.io.File
4 
5 data class AdbException(val args: Iterable<String>, val output: String) :
6     RuntimeException("${formatCmd(args)}:\n$output") {
<lambda>null7     val cmd: String by lazy { formatCmd(args) }
8 
9     companion object {
formatCmdnull10         fun formatCmd(args: Iterable<String>) = args.joinToString(" ")
11     }
12 }
13 
14 private fun adb(args: Iterable<String>, serial: String? = null): String {
15     val adbCmd = if (serial == null) {
16         listOf("adb")
17     } else {
18         listOf("adb", "-s", serial)
19     }
20     val result = ProcessBuilder(adbCmd + args).redirectErrorStream(true).start()
21     val output = result.inputStream.bufferedReader().use { it.readText() }
22     if (result.waitFor() != 0) {
23         throw AdbException(args, output)
24     }
25     return output
26 }
27 
28 data class Device(val serial: String) {
<lambda>null29     private val abis: List<Abi> by lazy {
30         val abiProps = listOf(
31             "ro.product.cpu.abi",
32             "ro.product.cpu.abi2",
33             "ro.product.cpu.abilist",
34         )
35         val abiSet = mutableSetOf<Abi>()
36         for (abiProp in abiProps) {
37             for (abiName in getProp(abiProp).trim().split(",")) {
38                 Abi.fromAbiName(abiName)?.let { abiSet.add(it) }
39             }
40         }
41         abiSet.toList().sortedBy { it.abiName }
42     }
43 
<lambda>null44     private val version: Int by lazy {
45         getProp("ro.build.version.sdk").trim().toInt()
46     }
47 
compatibleWithnull48     fun compatibleWith(abi: Abi, minSdkVersion: Int) =
49         abi in abis && minSdkVersion <= version
50 
51     fun push(src: File, dest: File) =
52         run(listOf("push", src.toString(), dest.toString()))
53 
54     fun shell(cmd: Iterable<String>) = run(listOf("shell") + cmd)
55 
56     private fun getProp(name: String): String = shell(listOf("getprop", name))
57 
58     private fun run(args: Iterable<String>): String = adb(args, serial)
59 }
60 
61 class DeviceFleet {
62     private fun lineHasUsableDevice(line: String): Boolean {
63         if (line.isBlank()) {
64             return false
65         }
66         if (line == "List of devices attached") {
67             return false
68         }
69         if (line.contains("offline")) {
70             return false
71         }
72         if (line.contains("unauthorized")) {
73             return false
74         }
75         if (line.startsWith("* daemon")) {
76             return false
77         }
78         return true
79     }
80 
81     private val devices: List<Device> by lazy {
82         adb(listOf("devices")).lines().filter { lineHasUsableDevice(it) }.map {
83             Device(it.split("\\s".toRegex()).first())
84         }
85     }
86 
87     fun findDeviceFor(abi: Abi, minSdkVersion: Int): Device? =
88         devices.find { it.compatibleWith(abi, minSdkVersion) }
89 }