Ich arbeite an der Android TV Remote App. Ich verwende Connect SDK, um Geräte zu erkennen. Um die Android-App mit Smart TV zu koppeln, verwende ich eine direkte SSL-Verbindung zur IP-Adresse des Fernsehers an Port 6467 und für die Kopplung sende ich Protobuf-Nachrichten (Pairing-Anfrage → Optionen → Konfiguration) und erwarte, dass der Fernseher auf dem Bildschirm einen 6-stelligen Code für die Kopplung anzeigt, der aber überhaupt nicht angezeigt wird. Hier ist mein Codeausschnitt:
Ich arbeite an der [b]Android TV Remote App[/b]. Ich verwende [b]Connect SDK[/b], um Geräte zu erkennen. Um die Android-App mit Smart TV zu koppeln, verwende ich eine direkte SSL-Verbindung zur IP-Adresse des Fernsehers an Port 6467 und für die Kopplung sende ich Protobuf-Nachrichten (Pairing-Anfrage → Optionen → Konfiguration) und erwarte, dass der Fernseher auf dem Bildschirm einen 6-stelligen Code für die Kopplung anzeigt, der aber überhaupt nicht angezeigt wird. Hier ist mein Codeausschnitt: [code]class AndroidTVPairingManager( private val deviceIp: String, private val deviceName: String, private val listener: AndroidTVPairingListener ) { private var sslSocket: SSLSocket? = null private var inputStream: InputStream? = null private var outputStream: OutputStream? = null private var isConnected = false private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val protocol = AndroidTVRemoteProtocol()
private val PAIRING_PORT = 6467 private val CONNECTION_TIMEOUT = 15000
companion object { private const val TAG = "AndroidTVPairing"
interface AndroidTVPairingListener { fun onPairingCodeRequired(code: String) fun onPairingSuccess() fun onPairingFailed(error: String) fun onConnectionEstablished() fun onDisconnected() }
private fun createSSLContext(): SSLContext { val keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC") keyPairGenerator.initialize(2048, SecureRandom()) val keyPair = keyPairGenerator.generateKeyPair() val certificate = generateCertificate(keyPair)
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) keyStore.load(null, null) keyStore.setKeyEntry("client", keyPair.private, "password".toCharArray(), arrayOf(certificate))
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) keyManagerFactory.init(keyStore, "password".toCharArray())
val trustAllCerts = arrayOf(object : X509TrustManager { override fun checkClientTrusted(chain: Array, authType: String) {} override fun checkServerTrusted(chain: Array, authType: String) {} override fun getAcceptedIssuers(): Array = arrayOf() })
val sslContext = SSLContext.getInstance("TLS") sslContext.init(keyManagerFactory.keyManagers, trustAllCerts, SecureRandom()) return sslContext }
private fun generateCertificate(keyPair: KeyPair): X509Certificate { val now = Date() val notBefore = Date(now.time - 1000L * 60 * 60 * 24) val notAfter = Date(now.time + 1000L * 60 * 60 * 24 * 365 * 10) val serialNumber = BigInteger.valueOf(now.time) val subject = X500Name("CN=androidtv/livingTV") val publicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.public.encoded)
private suspend fun sendConfiguration() { withContext(Dispatchers.IO) { try { val message = protocol.buildConfiguration() val complete = addLengthPrefix(message)
Log.d(TAG, "→ CONFIGURATION: ${complete.joinToString(" ") { "%02X".format(it) }}") outputStream?.write(complete) // Don't flush - will flush all together Log.d(TAG, "✓ Written to buffer") } catch (e: Exception) { Log.e(TAG, "❌ Failed", e) } } }
private suspend fun waitForPairingCode() { withContext(Dispatchers.IO) { try { Log.d(TAG, "Reading TV response...") sslSocket?.soTimeout = 5000 // 5 seconds
// Read length byte val lengthByte = inputStream?.read() ?: -1 if (lengthByte == -1) { Log.d(TAG, "No immediate response from TV") // This is OK - TV shows code on screen withContext(Dispatchers.Main) { listener.onPairingCodeRequired("") } return@withContext }
val messageLength = lengthByte and 0xFF Log.d(TAG, "← Response length: $messageLength bytes")
if (messageLength > 0) { // Read the message val messageData = ByteArray(messageLength) var totalRead = 0
while (totalRead < messageLength) { val bytesRead = inputStream?.read(messageData, totalRead, messageLength - totalRead) ?: -1 if (bytesRead == -1) break totalRead += bytesRead }
// The pairing code is shown ON THE TV SCREEN // Not sent to us over network! Log.d(TAG, "✓✓✓ CHECK YOUR TV SCREEN FOR PAIRING CODE ✓✓✓") withContext(Dispatchers.Main) { listener.onPairingCodeRequired("") }
} catch (e: Exception) { Log.d(TAG, "Response read complete") // Still OK - code is on TV withContext(Dispatchers.Main) { listener.onPairingCodeRequired("") } } } }
fun sendPairingCode(code: String) { scope.launch { try { Log.d(TAG, "→ Sending user code: $code")
// Need to compute alpha (challenge response) val alpha = computeAlpha(code) val message = protocol.buildSecret(alpha) val complete = addLengthPrefix(message)
class AndroidTVRemoteProtocol { companion object { private const val TAG = "AndroidTVProtocol"
private const val WIRE_TYPE_VARINT = 0 private const val WIRE_TYPE_LENGTH_DELIMITED = 2
// Message type field numbers in PairingMessage private const val FIELD_PAIRING_REQUEST = 1 private const val FIELD_PAIRING_OPTION = 2 private const val FIELD_PAIRING_CONFIGURATION = 3 private const val FIELD_PAIRING_SECRET = 4 private const val FIELD_STATUS = 200 private const val FIELD_PROTOCOL_VERSION = 201 }
/** * Build pairing request wrapped in PairingMessage */ fun buildPairingRequest(serviceName: String, clientName: String): ByteArray { val pairingRequest = ByteArrayOutputStream()
// Field 1: service_name writeString(pairingRequest, 1, serviceName) // Field 2: client_name writeString(pairingRequest, 2, clientName)
// Wrap in PairingMessage return wrapInPairingMessage(FIELD_PAIRING_REQUEST, pairingRequest.toByteArray()) }
/** * Build OPTIONS wrapped in PairingMessage * PairingOption has: * field 1: preferred_role (varint) * field 2: input_encodings (repeated, length-delimited) */ fun buildOptions(): ByteArray { val pairingOption = ByteArrayOutputStream()
// Field 1: preferred_role = ROLE_TYPE_INPUT (1) writeVarint(pairingOption, (1 shl 3) or WIRE_TYPE_VARINT) writeVarint(pairingOption, 1)
// Field 2: input_encodings (REPEATED - write encoding directly, not nested) val encoding = buildPairingEncoding() // Write as repeated field (just write field 2 with encoding bytes) writeVarint(pairingOption, (2 shl 3) or WIRE_TYPE_LENGTH_DELIMITED) writeVarint(pairingOption, encoding.size) pairingOption.write(encoding)
/** * Wrap message in PairingMessage with status and protocol version */ private fun wrapInPairingMessage(fieldNumber: Int, payload: ByteArray): ByteArray { val wrapper = ByteArrayOutputStream()
// Field X: the actual message (pairing_request, option, etc.) writeVarint(wrapper, (fieldNumber shl 3) or WIRE_TYPE_LENGTH_DELIMITED) writeVarint(wrapper, payload.size) wrapper.write(payload)
// Field 200: status = STATUS_OK (1) writeVarint(wrapper, (FIELD_STATUS shl 3) or WIRE_TYPE_VARINT) writeVarint(wrapper, 1)
// Field 201: protocol_version = 2 writeVarint(wrapper, (FIELD_PROTOCOL_VERSION shl 3) or WIRE_TYPE_VARINT) writeVarint(wrapper, 2)
return wrapper.toByteArray() }
/** * Parse message - single byte length prefix */ fun parseMessage(data: ByteArray): ProtocolMessage? { if (data.isEmpty()) return null
try { val messageLength = data[0].toInt() and 0xFF Log.d(TAG, "Length: $messageLength bytes")
private fun writeString(output: OutputStream, fieldNumber: Int, value: String) { val bytes = value.toByteArray(Charsets.UTF_8) writeBytes(output, fieldNumber, bytes) }
private fun writeBytes(output: OutputStream, fieldNumber: Int, value: ByteArray) { writeVarint(output, (fieldNumber shl 3) or WIRE_TYPE_LENGTH_DELIMITED) writeVarint(output, value.size) output.write(value) }
private fun writeVarint(output: OutputStream, value: Int) { var v = value while (v and 0x7F.inv() != 0) { output.write((v and 0x7F) or 0x80) v = v ushr 7 } output.write(v and 0x7F) }
class ProtocolMessage private constructor( val type: MessageType, val secret: String? = null ) {
enum class MessageType { SECRET, SECRET_ACK, UNKNOWN }
Ich versuche, einen LG -Fernseher mit dem ConnectSDK in Android zu steuern. Ich kann den Fernseher aus der App verbinden und steuern. Ich erhalte die Liste aller Apps, die ich in JSON aus der...
Chinesisches Mediengerät funktionierte auf einem Android-Fork namens Jetta OS 1.21 und wird in ADB-Geräten nicht angezeigt.
Ich habe ADB auf diesem Gerät erfolgreich in der Schnittstelle aktiviert...
Problem:
Wenn ich mein Android-Gerät zum Debuggen mit ADB an meinen Mac anschließe, wird das Gerät als nicht autorisiert angezeigt:
$ adb devices
List of devices attached
RZCW92YYCHL unauthorized...
Warum kann ich in einem mobilen Gerät den Kameratream sowohl vorne als auch hinten anzeigen, aber in einem anderen nur auf der Vorderseite kann ich nicht. Manchmal kann ich auf einigen anderen...