App 2.40.4: the recorder's listening works on Android 9
listenToProperties looked only for registerCallback (Android 10); the car's car library has registerListener/unregisterListener. Take either, unsubscribe property by property, and say why when it fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package com.evsuite.hardware
|
||||
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class PropertyListeningTest {
|
||||
|
||||
class Config(val propertyId: Int, val changeMode: Int, val minSampleRate: Float, val maxSampleRate: Float)
|
||||
class Value(val propertyId: Int, val areaId: Int, val value: Any)
|
||||
|
||||
/** Shaped like Android 9's CarPropertyManager, the car's: registerListener, and an
|
||||
* unregisterListener(listener) that skips every other property as the car's does. */
|
||||
class Android9Manager(private val configs: List<Config>) {
|
||||
interface CarPropertyEventListener {
|
||||
fun onChangeEvent(value: Any?)
|
||||
fun onErrorEvent(propertyId: Int, areaId: Int)
|
||||
}
|
||||
|
||||
val active = LinkedHashMap<Int, MutableList<CarPropertyEventListener>>()
|
||||
val rates = HashMap<Int, Float>()
|
||||
|
||||
fun getPropertyList(): List<Config> = configs
|
||||
|
||||
fun registerListener(listener: CarPropertyEventListener, propertyId: Int, rate: Float): Boolean {
|
||||
active.getOrPut(propertyId) { ArrayList() }.add(listener)
|
||||
rates[propertyId] = rate
|
||||
return true
|
||||
}
|
||||
|
||||
fun unregisterListener(listener: CarPropertyEventListener) {
|
||||
val ids = active.keys.toMutableList()
|
||||
var i = 0
|
||||
while (i < ids.size) {
|
||||
unregisterListener(listener, ids[i])
|
||||
if (active[ids[i]] == null) ids.removeAt(i) // as SparseArray.remove under keyAt(i)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
fun unregisterListener(listener: CarPropertyEventListener, propertyId: Int) {
|
||||
val list = active[propertyId] ?: return
|
||||
list.remove(listener)
|
||||
if (list.isEmpty()) active.remove(propertyId)
|
||||
}
|
||||
|
||||
fun fire(propertyId: Int, value: Any) {
|
||||
active[propertyId]?.toList()?.forEach { it.onChangeEvent(Value(propertyId, 0x01000000, value)) }
|
||||
}
|
||||
}
|
||||
|
||||
class NoListeners {
|
||||
fun getPropertyList(): List<Config> = emptyList()
|
||||
}
|
||||
|
||||
private val field = EVHardware::class.java.getDeclaredField("sCarPropertyManager").apply { isAccessible = true }
|
||||
|
||||
@After
|
||||
fun reset() = field.set(null, null)
|
||||
|
||||
@Test
|
||||
fun `subscribes through Android 9's registerListener and unsubscribes every property`() {
|
||||
val manager = Android9Manager(listOf(
|
||||
Config(0x2160A15B, 1, 0f, 0f), // in the block, on change
|
||||
Config(0x2140A15A, 1, 0f, 0f),
|
||||
Config(0x2160A1A3, 2, 0f, 10f), // continuous
|
||||
Config(0x2140A101, 1, 0f, 0f),
|
||||
Config(0x11400400, 1, 0f, 0f), // outside the block
|
||||
))
|
||||
field.set(null, manager)
|
||||
val heard = ArrayList<String>()
|
||||
|
||||
val listening = EVHardware.listenToProperties(AssistBlock::contains) { id, area, value ->
|
||||
heard += "%08X/%d=%s".format(id, area, value)
|
||||
}
|
||||
|
||||
assertNotNull(EVHardware.listenProblem, listening)
|
||||
assertEquals(4, listening!!.subscribed)
|
||||
assertEquals(0, listening.refused)
|
||||
assertEquals(setOf(0x2160A15B, 0x2140A15A, 0x2160A1A3, 0x2140A101), manager.active.keys)
|
||||
assertEquals(0f, manager.rates[0x2160A15B])
|
||||
assertEquals(1f, manager.rates[0x2160A1A3])
|
||||
|
||||
manager.fire(0x2160A15B, 90f)
|
||||
assertEquals(1, heard.size)
|
||||
assertTrue(heard[0], heard[0].startsWith("2160A15B/16777216="))
|
||||
|
||||
listening.close()
|
||||
assertTrue(manager.active.toString(), manager.active.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `says why when there is no way to subscribe`() {
|
||||
field.set(null, NoListeners())
|
||||
assertNull(EVHardware.listenToProperties(AssistBlock::contains) { _, _, _ -> })
|
||||
assertEquals("no way to subscribe", EVHardware.listenProblem)
|
||||
|
||||
field.set(null, null)
|
||||
assertNull(EVHardware.listenToProperties(AssistBlock::contains) { _, _, _ -> })
|
||||
assertEquals("no car service", EVHardware.listenProblem)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user