diff --git a/EVHardware/CHANGELOG.md b/EVHardware/CHANGELOG.md index 398280d..b089351 100644 --- a/EVHardware/CHANGELOG.md +++ b/EVHardware/CHANGELOG.md @@ -824,7 +824,7 @@ All notable changes to this project are documented here. Format follows - **The glass probe refused to run on a car whose ignition was on.** It read `getCurrentIgnitionState()`, which answers on the AAOS `IgnitionState` scale, against - `CarIgnitionItem.RUN` — and the two scales do not line up: RUN is 2 for Katman5, where 2 is + `CarIgnitionItem.RUN` — and the two scales do not line up: RUN is 2 for Layer5, where 2 is OFF. Contact mis read as 4 and was refused; contact coupé read as 2 and was let through. `setEsc` compared the same two scales and now refuses only a *known* non-RUN ignition, as its own documentation always said it did. diff --git a/EVHardware/README.md b/EVHardware/README.md index f786753..974a3b8 100644 --- a/EVHardware/README.md +++ b/EVHardware/README.md @@ -60,7 +60,7 @@ Discover the rest of the suite: ## Overview | Area | Types | |---|---| -| Vehicle access | `EVHardware` — Katman1/4/5 reflection over `android.car` + SAIC SDK, per-firmware routing | +| Vehicle access | `EVHardware` — Layer1/4/5 reflection over `android.car` + SAIC SDK, per-firmware routing | | Safety | `VehicleWriteGate` (0 km/h, fail-closed) + `@RequiresStandstill` on every gated setter | | Vendor services | `saic.*` — the AIDL the head unit's own HVAC, charging, radio, hands-free and TTS apps use | | Radio | `SaicRadio` — play, silence, fail-closed play/pause toggle, FM/AM tune, station stepping (DAB included), screen | diff --git a/EVHardware/lib/src/main/java/com/evsuite/hardware/EVHardware.kt b/EVHardware/lib/src/main/java/com/evsuite/hardware/EVHardware.kt index 38d9fea..2a19657 100644 --- a/EVHardware/lib/src/main/java/com/evsuite/hardware/EVHardware.kt +++ b/EVHardware/lib/src/main/java/com/evsuite/hardware/EVHardware.kt @@ -22,10 +22,13 @@ import kotlinx.coroutines.launch /** * Hardware abstraction layer for MG4 vehicle control. * - * Runtime communication layers ("Katman", Turkish for layer, as the names in this file call them): - * Katman1 — android.car.Car → CarPropertyManager / CarHvacManager (async ServiceConnection) - * Katman2 — ServiceManager.getService("vehiclesetting") → raw IBinder (often SELinux-blocked) - * Katman3 — bindService(VehicleService) → IHubService → sub-services (not needed for our use case) + * Runtime communication layers, numbered as the names in this file use them (Layer1 … Layer5): + * Layer1 — android.car.Car → CarPropertyManager / CarHvacManager (async ServiceConnection) + * Layer2 — ServiceManager.getService("vehiclesetting") → raw IBinder (often SELinux-blocked) + * Layer3 — bindService(VehicleService) → IHubService → sub-services (not needed for our use case) + * Layer4 — SAIC's settings SDK in the launcher's context: VehiclePropertyManager (SWI133), + * VehicleSettingManager (SWI68/SWI165), CarVehicleSettingClient via CarAdapterClient (A9) + * Layer5 — ignition state: VehicleConditionManager, or ICarGeneralService via CarAdapterClient (A9) */ object EVHardware { @@ -146,11 +149,11 @@ object EVHardware { private const val VSM_SERVICE_CODE = 0x8 // queryClient(0x8) → ICarVehicleSettingService private const val VEHICLE_SETTING_PKG = "com.saicmotor.vehiclesetting" // SWI131: carapi inside VS - // Katman5 — VehicleConditionManager (IVehicleConditionService via IHubService "vehiclecondition") + // Layer5 — VehicleConditionManager (IVehicleConditionService via IHubService "vehiclecondition") private const val VCM_CLASS = "com.saicmotor.sdk.vehiclesettings.manager.VehicleConditionManager" private const val VCM_LISTENER_CLASS = "com.saicmotor.sdk.vehiclesettings.IVehicleConditionListener" - // Katman5 SWI69/SWI131 — ICarGeneralService via CarAdapterClient (queryClient(0x1)) + // Layer5 SWI69/SWI131 — ICarGeneralService via CarAdapterClient (queryClient(0x1)) private const val CAR_GENERAL_CLIENT_CLASS = "com.saicmotor.carapi.client.CarGeneralClient" private const val BIND_CODE_CAR_GENERAL = 0x1 // ICarAdapterService.queryClient(0x1) @@ -177,7 +180,7 @@ object EVHardware { } /** - * Values returned by IVehicleConditionService.getVehicleIgnition() (Katman5). + * Values returned by IVehicleConditionService.getVehicleIgnition() (Layer5). * Values exposed by the supported SWI133 runtime interface. */ object CarIgnitionItem { @@ -232,9 +235,9 @@ object EVHardware { /** Set once the getters above have been looked up, present or absent. */ @Volatile private var sCpmMethodsResolved = false @Volatile private var sVehicleBinder: IBinder? = null - @Volatile private var sVpm: Any? = null // VehiclePropertyManager instance (SWI133, Katman4) + @Volatile private var sVpm: Any? = null // VehiclePropertyManager instance (SWI133, Layer4) @Volatile private var sVpmService: Any? = null // mIVehiclePropertyService field value (SWI133) - @Volatile private var sVsm: Any? = null // VehicleSettingManager instance (SWI68, Katman4) + @Volatile private var sVsm: Any? = null // VehicleSettingManager instance (SWI68, Layer4) // The A9 adapter, kept after the VSM client is built: it is the door to every other // carapi client too — the climate one among them — and nothing else can open it. @Volatile private var sCarAdapter: Any? = null @@ -253,49 +256,49 @@ object EVHardware { @Volatile private var sDriveModeListener: DriveModeListener? = null @Volatile private var sHvacListener: HvacListener? = null - // ── Katman5 — IGNITION_STATE via CarPropertyManager (standard AAOS) ────── + // ── Layer5 — IGNITION_STATE via CarPropertyManager (standard AAOS) ────── @Volatile private var sIgnitionCallbackProxy: Any? = null @Volatile private var sIgnitionCallbackRegistered = false private val ignitionCallbacks = java.util.concurrent.CopyOnWriteArrayList<(Int) -> Unit>() - // ── Katman5 — VehicleConditionManager / ICarGeneralService ─────────────── + // ── Layer5 — VehicleConditionManager / ICarGeneralService ─────────────── @Volatile private var sVcm: Any? = null @Volatile private var sVcmListener: Any? = null @Volatile private var sVcmCallbackRegistered = false @Volatile private var sLastVcmIgnitionState = -1 // filters repeated false RUN events private val vehicleConditionCallbacks = java.util.concurrent.CopyOnWriteArrayList<(Int) -> Unit>() - private val katman5ReadyListeners = java.util.concurrent.CopyOnWriteArrayList<() -> Unit>() + private val layer5ReadyListeners = java.util.concurrent.CopyOnWriteArrayList<() -> Unit>() - /** Listeners notified as soon as Katman1 (CPM + HVAC) is operational. */ - private val katman1ReadyListeners = java.util.concurrent.CopyOnWriteArrayList<() -> Unit>() + /** Listeners notified as soon as Layer1 (CPM + HVAC) is operational. */ + private val layer1ReadyListeners = java.util.concurrent.CopyOnWriteArrayList<() -> Unit>() - /** Listeners notified as soon as Katman4 (mIVehiclePropertyService) is operational. */ - private val katman4ReadyListeners = java.util.concurrent.CopyOnWriteArrayList<() -> Unit>() + /** Listeners notified as soon as Layer4 (mIVehiclePropertyService) is operational. */ + private val layer4ReadyListeners = java.util.concurrent.CopyOnWriteArrayList<() -> Unit>() /** * Runs [action] as soon as CarPropertyManager and CarHvacManager are available. * If already ready, runs immediately. Otherwise queued and triggered on connection. */ - fun whenKatman1Ready(action: () -> Unit) { + fun whenLayer1Ready(action: () -> Unit) { if (sCarPropertyManager != null && sCarHvacManager != null) { action() } else { - katman1ReadyListeners.add(action) + layer1ReadyListeners.add(action) } } /** - * Runs [action] as soon as the ADAS service (Katman4) is available. + * Runs [action] as soon as the ADAS service (Layer4) is available. * SWI133 → mIVehiclePropertyService ; SWI68/SWI69/SWI131 → mVehicleSettingService */ - fun whenKatman4Ready(action: () -> Unit) { + fun whenLayer4Ready(action: () -> Unit) { val ready = if (FirmwareInfo.isVsmBased()) sVsmService != null else sVpmService != null - if (ready) action() else katman4ReadyListeners.add(action) + if (ready) action() else layer4ReadyListeners.add(action) } - /** Runs [action] as soon as Katman5 (IVehicleConditionService) is operational. */ - fun whenKatman5Ready(action: () -> Unit) { - if (sVcmCallbackRegistered) action() else katman5ReadyListeners.add(action) + /** Runs [action] as soon as Layer5 (IVehicleConditionService) is operational. */ + fun whenLayer5Ready(action: () -> Unit) { + if (sVcmCallbackRegistered) action() else layer5ReadyListeners.add(action) } /** Registers a callback invoked on every ignition state change (CarIgnitionItem). */ @@ -338,12 +341,12 @@ object EVHardware { * Current ignition state, as [IgnitionState] (AAOS) values. * * CarPropertyManager first. But SWI68 does not expose IGNITION_STATE through CPM: the - * property comes back empty, and the ignition state looked unreadable although Katman5 - * reports it correctly — the log shows it (`Katman5: initial state = 2 (RUN/READY)`) while + * property comes back empty, and the ignition state looked unreadable although Layer5 + * reports it correctly — the log shows it (`Layer5: initial state = 2 (RUN/READY)`) while * the diagnostics classed IGNITION_STATE as NOT_READABLE. * * Hence the fallback to the last state delivered by IVehicleConditionService, converted: - * Katman5 speaks [CarIgnitionItem], this function returns [IgnitionState], and the two + * Layer5 speaks [CarIgnitionItem], this function returns [IgnitionState], and the two * scales do not line up (RUN is 2 in one, OFF is 2 in the other). * * Returns -1 if nothing is readable, 0 if the property exists but says nothing. @@ -356,7 +359,7 @@ object EVHardware { return vcmIgnitionAsAaos() ?: global } - /** The last Katman5 state translated into [IgnitionState], or null if there was none. */ + /** The last Layer5 state translated into [IgnitionState], or null if there was none. */ private fun vcmIgnitionAsAaos(): Int? = when (sLastVcmIgnitionState) { CarIgnitionItem.OFF -> IgnitionState.OFF CarIgnitionItem.ACCESSORY -> IgnitionState.ACC @@ -909,22 +912,22 @@ object EVHardware { bindCarService(context) sVehicleBinder = getBinderService("vehiclesetting") when { - FirmwareInfo.getGeneration() == FirmwareInfo.Gen.SWI68 -> initKatman4Swi68(context) - FirmwareInfo.getGeneration() == FirmwareInfo.Gen.SWI165 -> initKatman4Swi68(context) // same SDK as SWI68 - FirmwareInfo.getGeneration() == FirmwareInfo.Gen.SWI132 -> initKatman4Swi69(context) // CarVehicleSettingClient, same path as SWI69 - FirmwareInfo.isNewGenVsm() -> initKatman4Swi69(context) // SWI69 + SWI131 - else -> initKatman4(context) + FirmwareInfo.getGeneration() == FirmwareInfo.Gen.SWI68 -> initLayer4Swi68(context) + FirmwareInfo.getGeneration() == FirmwareInfo.Gen.SWI165 -> initLayer4Swi68(context) // same SDK as SWI68 + FirmwareInfo.getGeneration() == FirmwareInfo.Gen.SWI132 -> initLayer4Swi69(context) // CarVehicleSettingClient, same path as SWI69 + FirmwareInfo.isNewGenVsm() -> initLayer4Swi69(context) // SWI69 + SWI131 + else -> initLayer4(context) } - // Katman5 — IGNITION_STATE push detection (VehicleConditionManager or ICarGeneralService) + // Layer5 — IGNITION_STATE push detection (VehicleConditionManager or ICarGeneralService) // SWI132 uses the same observed ICarGeneralService path as SWI69/SWI131. if (FirmwareInfo.isNewGenVsm() || FirmwareInfo.getGeneration() == FirmwareInfo.Gen.SWI132) - initKatman5Swi69(context) + initLayer5Swi69(context) else - initKatman5(context) + initLayer5(context) if (sVehicleBinder != null) - AppLogger.i(TAG, " ✓ Katman2: vehiclesetting binder OK") + AppLogger.i(TAG, " ✓ Layer2: vehiclesetting binder OK") else - AppLogger.w(TAG, " ✗ Katman2: vehiclesetting null (SELinux — expected)") + AppLogger.w(TAG, " ✗ Layer2: vehiclesetting null (SELinux — expected)") // Start the door watcher at boot if the feature is enabled (all firmwares). startDoorWatcherIfEnabled() @@ -935,7 +938,7 @@ object EVHardware { } // ------------------------------------------------------------------------- - // Katman1 — android.car.Car (async, mirrors original bindCarService exactly) + // Layer1 — android.car.Car (async, mirrors original bindCarService exactly) // ------------------------------------------------------------------------- private fun bindCarService(context: Context) { @@ -944,12 +947,12 @@ object EVHardware { val carClass: Class<*> try { carClass = Class.forName("android.car.Car") - AppLogger.i(TAG, " Katman1: android.car.Car class found ✓") + AppLogger.i(TAG, " Layer1: android.car.Car class found ✓") } catch (e: ClassNotFoundException) { - AppLogger.w(TAG, " Katman1: android.car.Car not found — not Automotive?") + AppLogger.w(TAG, " Layer1: android.car.Car not found — not Automotive?") return } catch (e: Exception) { - AppLogger.e(TAG, " Katman1: forName error: ${e.message}") + AppLogger.e(TAG, " Layer1: forName error: ${e.message}") return } @@ -958,7 +961,7 @@ object EVHardware { // Attempt 1: createCar(Context) try { car = carClass.getMethod("createCar", Context::class.java).invoke(null, context) - if (car != null) AppLogger.i(TAG, " Katman1: createCar(Context) → success") + if (car != null) AppLogger.i(TAG, " Layer1: createCar(Context) → success") } catch (_: Exception) {} // Attempt 2: createCar(Context, Handler) @@ -966,7 +969,7 @@ object EVHardware { try { car = carClass.getMethod("createCar", Context::class.java, Handler::class.java) .invoke(null, context, null) - if (car != null) AppLogger.i(TAG, " Katman1: createCar(Context, Handler) → success") + if (car != null) AppLogger.i(TAG, " Layer1: createCar(Context, Handler) → success") } catch (_: Exception) {} } @@ -974,18 +977,18 @@ object EVHardware { var scMethodFound: java.lang.reflect.Method? = null try { scMethodFound = carClass.getMethod("createCar", Context::class.java, ServiceConnection::class.java) - AppLogger.i(TAG, " Katman1: createCar(Context, ServiceConnection) method found") + AppLogger.i(TAG, " Layer1: createCar(Context, ServiceConnection) method found") } catch (_: Exception) {} if (car == null && scMethodFound != null) { try { val sc = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { - AppLogger.i(TAG, " Katman1: ServiceConnection.onServiceConnected") + AppLogger.i(TAG, " Layer1: ServiceConnection.onServiceConnected") tryGetManagersFromCar(carClass) } override fun onServiceDisconnected(name: ComponentName?) { - AppLogger.w(TAG, " Katman1: Car service disconnected") + AppLogger.w(TAG, " Layer1: Car service disconnected") sCarPropertyManager = null sCarHvacManager = null clearCarPropertyMethods() @@ -993,14 +996,14 @@ object EVHardware { } } car = scMethodFound.invoke(null, context, sc) - if (car != null) AppLogger.i(TAG, " Katman1: createCar(Context, SC) → callback pending") + if (car != null) AppLogger.i(TAG, " Layer1: createCar(Context, SC) → callback pending") } catch (e: Exception) { - AppLogger.w(TAG, " Katman1: createCar(Context, SC) error: ${e.message}") + AppLogger.w(TAG, " Layer1: createCar(Context, SC) error: ${e.message}") } } if (car == null) { - AppLogger.e(TAG, " Katman1: all createCar methods failed") + AppLogger.e(TAG, " Layer1: all createCar methods failed") // Not necessarily fatal: on a cold head unit the Car service is simply not up // yet. Giving up here would leave the whole process without a vehicle forever. armCarServiceWatchdog(context, carClass) @@ -1012,11 +1015,11 @@ object EVHardware { // Call car.connect() if available (required on older builds) try { carClass.getMethod("connect").invoke(car) - AppLogger.i(TAG, " Katman1: car.connect() called") + AppLogger.i(TAG, " Layer1: car.connect() called") } catch (_: NoSuchMethodException) { // connect() not present on all builds, ignore } catch (e: Exception) { - AppLogger.w(TAG, " Katman1: car.connect() error: ${e.message}") + AppLogger.w(TAG, " Layer1: car.connect() error: ${e.message}") } // Try sync managers immediately @@ -1075,9 +1078,9 @@ object EVHardware { (carClass.getMethod("isConnected").invoke(car) as? Boolean) ?: true } catch (_: Exception) { true } - AppLogger.i(TAG, " Katman1: isConnected() → $connected") + AppLogger.i(TAG, " Layer1: isConnected() → $connected") if (!connected) { - AppLogger.w(TAG, " Katman1: car not yet connected") + AppLogger.w(TAG, " Layer1: car not yet connected") return } @@ -1088,9 +1091,9 @@ object EVHardware { val svc = carClass.getField("PROPERTY_SERVICE").get(null) as String sCarPropertyManager = getCarManager.invoke(car, svc) cacheCarPropertyMethods(sCarPropertyManager) - AppLogger.i(TAG, " Katman1: CarPropertyManager READY ✓") + AppLogger.i(TAG, " Layer1: CarPropertyManager READY ✓") } catch (e: Exception) { - AppLogger.w(TAG, " Katman1: CarPropertyManager unavailable: ${e.message}") + AppLogger.w(TAG, " Layer1: CarPropertyManager unavailable: ${e.message}") } } @@ -1098,28 +1101,28 @@ object EVHardware { try { val svc = carClass.getField("HVAC_SERVICE").get(null) as String sCarHvacManager = getCarManager.invoke(car, svc) - AppLogger.i(TAG, " Katman1: CarHvacManager READY ✓") + AppLogger.i(TAG, " Layer1: CarHvacManager READY ✓") } catch (e: Exception) { - AppLogger.w(TAG, " Katman1: CarHvacManager unavailable: ${e.message}") + AppLogger.w(TAG, " Layer1: CarHvacManager unavailable: ${e.message}") } } - // Notify whenKatman1Ready subscribers as soon as both managers are ready - if (sCarPropertyManager != null && sCarHvacManager != null && katman1ReadyListeners.isNotEmpty()) { - val toNotify = katman1ReadyListeners.toList() - katman1ReadyListeners.clear() + // Notify whenLayer1Ready subscribers as soon as both managers are ready + if (sCarPropertyManager != null && sCarHvacManager != null && layer1ReadyListeners.isNotEmpty()) { + val toNotify = layer1ReadyListeners.toList() + layer1ReadyListeners.clear() Handler(Looper.getMainLooper()).post { toNotify.forEach { it() } } } // Try to register the IGNITION_STATE callback (best effort) if (sCarPropertyManager != null) registerIgnitionPropertyCallback() } catch (e: Exception) { - AppLogger.e(TAG, " Katman1: tryGetManagersFromCar error: ${e.message}") + AppLogger.e(TAG, " Layer1: tryGetManagersFromCar error: ${e.message}") } } // ------------------------------------------------------------------------- - // Katman2 — ServiceManager raw binder + // Layer2 — ServiceManager raw binder // ------------------------------------------------------------------------- private fun getBinderService(serviceName: String): IBinder? { @@ -1136,10 +1139,10 @@ object EVHardware { } // ------------------------------------------------------------------------- - // Katman4 — VehiclePropertyManager via saicmotor.hmi.launcher context + // Layer4 — VehiclePropertyManager via saicmotor.hmi.launcher context // ------------------------------------------------------------------------- - private fun initKatman4(context: Context) { + private fun initLayer4(context: Context) { if (sVpm != null) return val launcherCtx: Context val vpmClass: Class<*> @@ -1151,8 +1154,8 @@ object EVHardware { vpmClass = launcherCtx.classLoader .loadClass("com.saicmotor.sdk.vehiclesettings.manager.VehiclePropertyManager") } catch (e: Exception) { - AppLogger.w(TAG, " Katman4: package/class error: ${e.message} — will retry") - Handler(Looper.getMainLooper()).postDelayed({ initKatman4(context.applicationContext) }, 5_000) + AppLogger.w(TAG, " Layer4: package/class error: ${e.message} — will retry") + Handler(Looper.getMainLooper()).postDelayed({ initLayer4(context.applicationContext) }, 5_000) return } @@ -1184,8 +1187,8 @@ object EVHardware { } if (vpm == null) { - AppLogger.w(TAG, " Katman4: all attempts failed — will retry") - Handler(Looper.getMainLooper()).postDelayed({ initKatman4(context.applicationContext) }, 10_000) + AppLogger.w(TAG, " Layer4: all attempts failed — will retry") + Handler(Looper.getMainLooper()).postDelayed({ initLayer4(context.applicationContext) }, 10_000) return } @@ -1221,7 +1224,7 @@ object EVHardware { } tryGetVpmService(vpm!!) - AppLogger.i(TAG, " Katman4: VPM ready — mIVehiclePropertyService=${if (sVpmService != null) "OK ✓" else "null (en attente)"}") + AppLogger.i(TAG, " Layer4: VPM ready — mIVehiclePropertyService=${if (sVpmService != null) "OK ✓" else "null (en attente)"}") } private fun initWithServiceListener(vpm: Any, context: Context, launcherCtx: Context) { @@ -1231,7 +1234,7 @@ object EVHardware { val methodSummary = vpmClass.methods.joinToString(", ") { m -> "${m.name}(${m.parameterTypes.joinToString(",") { it.simpleName }})" } - AppLogger.d(TAG, " Katman4: VPM methods = $methodSummary") + AppLogger.d(TAG, " Layer4: VPM methods = $methodSummary") // Strategy 1: Inspect the actual init() signature to get the real listener type val initMethod2 = vpmClass.methods.firstOrNull { m -> @@ -1241,7 +1244,7 @@ object EVHardware { if (initMethod2 != null) { val listenerType = initMethod2.parameterTypes[1] - AppLogger.i(TAG, " Katman4: init() found, listener type = ${listenerType.name}") + AppLogger.i(TAG, " Layer4: init() found, listener type = ${listenerType.name}") // Try dynamic proxy with the actual listener interface type if (listenerType.isInterface) { @@ -1251,11 +1254,11 @@ object EVHardware { ) { _, method, _ -> when (method.name) { "onServiceConnected" -> { - AppLogger.i(TAG, " Katman4: onServiceConnected ✓") + AppLogger.i(TAG, " Layer4: onServiceConnected ✓") tryGetVpmService(vpm) } "onServiceDisconnected" -> { - AppLogger.w(TAG, " Katman4: onServiceDisconnected") + AppLogger.w(TAG, " Layer4: onServiceDisconnected") sVpmService = null } else -> {} @@ -1263,53 +1266,53 @@ object EVHardware { null } initMethod2.invoke(vpm, context, proxy) - AppLogger.i(TAG, " Katman4: init(Context, proxy) ✓") + AppLogger.i(TAG, " Layer4: init(Context, proxy) ✓") return } catch (e: Exception) { - AppLogger.d(TAG, " Katman4: init(Context, proxy) failed: ${e.message}") + AppLogger.d(TAG, " Layer4: init(Context, proxy) failed: ${e.message}") } } // Fallback: try init(Context, null) — works if listener is nullable try { initMethod2.invoke(vpm, context, null) - AppLogger.i(TAG, " Katman4: init(Context, null) ✓") + AppLogger.i(TAG, " Layer4: init(Context, null) ✓") return } catch (e: Exception) { - AppLogger.d(TAG, " Katman4: init(Context, null) failed: ${e.message}") + AppLogger.d(TAG, " Layer4: init(Context, null) failed: ${e.message}") } } // Strategy 2: init(Context) single-param try { vpmClass.getMethod("init", Context::class.java).invoke(vpm, context) - AppLogger.i(TAG, " Katman4: init(Context) ✓") + AppLogger.i(TAG, " Layer4: init(Context) ✓") return } catch (_: NoSuchMethodException) { } catch (e: Exception) { - AppLogger.d(TAG, " Katman4: init(Context) failed: ${e.message}") + AppLogger.d(TAG, " Layer4: init(Context) failed: ${e.message}") } // Strategy 3: init() no-arg try { vpmClass.getMethod("init").invoke(vpm) - AppLogger.i(TAG, " Katman4: init() ✓") + AppLogger.i(TAG, " Layer4: init() ✓") return } catch (_: NoSuchMethodException) { } catch (e: Exception) { - AppLogger.d(TAG, " Katman4: init() failed: ${e.message}") + AppLogger.d(TAG, " Layer4: init() failed: ${e.message}") } - AppLogger.w(TAG, " Katman4: no working init() — mIVehiclePropertyService stays null") + AppLogger.w(TAG, " Layer4: no working init() — mIVehiclePropertyService stays null") } /** Runs [block], returns the result or null, logs the result/error. */ private fun tryInvoke(label: String, block: () -> Any?): Any? = try { val r = block() - AppLogger.i(TAG, " Katman4: $label → ${if (r != null) "OK ($r)" else "null"}") + AppLogger.i(TAG, " Layer4: $label → ${if (r != null) "OK ($r)" else "null"}") r } catch (e: Exception) { - AppLogger.d(TAG, " Katman4: $label → ${e.javaClass.simpleName}: ${e.message}") + AppLogger.d(TAG, " Layer4: $label → ${e.javaClass.simpleName}: ${e.message}") null } @@ -1322,10 +1325,10 @@ object EVHardware { val svc = f.get(vpm) if (svc != null) { sVpmService = svc - AppLogger.i(TAG, " Katman4: mIVehiclePropertyService READY ✓") - // Notify any pending Katman4 listeners - val toNotify = katman4ReadyListeners.toList() - katman4ReadyListeners.clear() + AppLogger.i(TAG, " Layer4: mIVehiclePropertyService READY ✓") + // Notify any pending Layer4 listeners + val toNotify = layer4ReadyListeners.toList() + layer4ReadyListeners.clear() Handler(Looper.getMainLooper()).post { toNotify.forEach { it() } } } return @@ -1655,10 +1658,10 @@ object EVHardware { } } - // Katman4 SWI68 — VehicleSettingManager via saicmotor.hmi.launcher context + // Layer4 SWI68 — VehicleSettingManager via saicmotor.hmi.launcher context // ------------------------------------------------------------------------- - private fun initKatman4Swi68(context: Context) { + private fun initLayer4Swi68(context: Context) { if (sVsm != null) return val launcherCtx: Context val vsmClass: Class<*> @@ -1671,7 +1674,7 @@ object EVHardware { AppLogger.i(TAG, " SWI68: VehicleSettingManager class found ✓") } catch (e: Exception) { AppLogger.w(TAG, " SWI68: class load error: ${e.message} — retry in 5s") - Handler(Looper.getMainLooper()).postDelayed({ initKatman4Swi68(context.applicationContext) }, 5_000) + Handler(Looper.getMainLooper()).postDelayed({ initLayer4Swi68(context.applicationContext) }, 5_000) return } @@ -1757,8 +1760,8 @@ object EVHardware { if (svc != null) { sVsmService = svc AppLogger.i(TAG, " SWI68: mVehicleSettingService READY ✓") - val toNotify = katman4ReadyListeners.toList() - katman4ReadyListeners.clear() + val toNotify = layer4ReadyListeners.toList() + layer4ReadyListeners.clear() Handler(Looper.getMainLooper()).post { toNotify.forEach { it() } } } return @@ -1767,7 +1770,7 @@ object EVHardware { } // ------------------------------------------------------------------------- - // Katman4 SWI69/SWI131 — CarVehicleSettingClient via CarAdapterClient + // Layer4 SWI69/SWI131 — CarVehicleSettingClient via CarAdapterClient // // Runtime integration contract: // CarAdapterClient.getInstance(ctx).start() @@ -1779,7 +1782,7 @@ object EVHardware { // (getAccTjaState, setAccTjaState, getLasWarningSound, getFcwState, etc.) // ------------------------------------------------------------------------- - private fun initKatman4Swi69(context: Context) { + private fun initLayer4Swi69(context: Context) { if (sVsm != null) return val launcherCtx: Context @@ -1795,7 +1798,7 @@ object EVHardware { AppLogger.i(TAG, " SWI69: CarAdapterClient + CarVehicleSettingClient classes found ✓") } catch (e: Exception) { AppLogger.w(TAG, " SWI69: class load error: ${e.message} — retry in 5s") - Handler(Looper.getMainLooper()).postDelayed({ initKatman4Swi69(context.applicationContext) }, 5_000) + Handler(Looper.getMainLooper()).postDelayed({ initLayer4Swi69(context.applicationContext) }, 5_000) return } @@ -1808,7 +1811,7 @@ object EVHardware { if (adapter == null) { AppLogger.w(TAG, " SWI69: CarAdapterClient.getInstance() failed — retry in 10s") - Handler(Looper.getMainLooper()).postDelayed({ initKatman4Swi69(context.applicationContext) }, 10_000) + Handler(Looper.getMainLooper()).postDelayed({ initLayer4Swi69(context.applicationContext) }, 10_000) return } @@ -1878,8 +1881,8 @@ object EVHardware { sVsmService = ibinder AppLogger.i(TAG, " SWI69: CarVehicleSettingClient READY ✓") - val toNotify = katman4ReadyListeners.toList() - katman4ReadyListeners.clear() + val toNotify = layer4ReadyListeners.toList() + layer4ReadyListeners.clear() Handler(Looper.getMainLooper()).post { toNotify.forEach { it() } } } catch (e: Exception) { AppLogger.d(TAG, " SWI69: tryInitClientFromAdapter error: ${e.message}") @@ -1892,7 +1895,7 @@ object EVHardware { * [a9ClientBinder] returns null for two entirely different reasons — the code is not served, * or there is no adapter to ask — and a survey that cannot tell them apart records "absent" * for a question that was never put to the vehicle. `sCarAdapter` is assigned only by - * `initKatman4Swi69`, so on SWI68 every `queryClient` in a probe fails before it reaches a + * `initLayer4Swi69`, so on SWI68 every `queryClient` in a probe fails before it reaches a * binder. RI-002 was nearly closed on that false negative; the probe now says so out loud. */ internal fun hasCarAdapterClient(): Boolean = sCarAdapter != null && sCarAdapterClass != null @@ -2185,11 +2188,11 @@ object EVHardware { when (result) { is Int -> result is Number -> result.toInt() - null -> { AppLogger.d(TAG, " Katman4: getMixProperty(0x${propId.toString(16)}) = null"); -1 } - else -> { AppLogger.d(TAG, " Katman4: getMixProperty(0x${propId.toString(16)}) = $result (${result.javaClass.simpleName})"); -1 } + null -> { AppLogger.d(TAG, " Layer4: getMixProperty(0x${propId.toString(16)}) = null"); -1 } + else -> { AppLogger.d(TAG, " Layer4: getMixProperty(0x${propId.toString(16)}) = $result (${result.javaClass.simpleName})"); -1 } } } catch (e: Exception) { - AppLogger.d(TAG, " Katman4: getMixProperty(0x${propId.toString(16)}) exc: ${e.message}") + AppLogger.d(TAG, " Layer4: getMixProperty(0x${propId.toString(16)}) exc: ${e.message}") -1 } } @@ -2201,10 +2204,10 @@ object EVHardware { vpm.javaClass .getMethod("setMixProperty", Class::class.java, Int::class.java, Any::class.java) .invoke(vpm, Int::class.javaObjectType, propId, value) - AppLogger.i(TAG, " Katman4: setMixProperty(0x${propId.toString(16)}, $value) ✓") + AppLogger.i(TAG, " Layer4: setMixProperty(0x${propId.toString(16)}, $value) ✓") true } catch (e: Exception) { - AppLogger.d(TAG, " Katman4: setMixProperty(0x${propId.toString(16)}, $value) exc: ${e.message}") + AppLogger.d(TAG, " Layer4: setMixProperty(0x${propId.toString(16)}, $value) exc: ${e.message}") false } } @@ -2359,7 +2362,7 @@ object EVHardware { } /** - * SAIC proprietary binder transact (Katman2 fallback). + * SAIC proprietary binder transact (Layer2 fallback). * Runtime parcel contract: [interfaceToken, AREA_GLOBAL, 1, value, float[], byte[]] */ private fun binderTransact(binder: IBinder?, descriptor: String, txCode: Int, value: Int): Boolean { @@ -2532,7 +2535,7 @@ object EVHardware { } // ------------------------------------------------------------------------- - // ADAS API (Katman4) + // ADAS API (Layer4) // ------------------------------------------------------------------------- fun overspeedAlarmOnOrNull(): Boolean? { @@ -2603,7 +2606,7 @@ object EVHardware { return setIntPropertyVpm(PROP_SPEED_LIMIT_TONE, if (on) 1 else 0) } - /** Returns 0–4 (Off/Limiter/Auto/ACC/ICA), or -1 if Katman4 not ready. */ + /** Returns 0–4 (Off/Limiter/Auto/ACC/ICA), or -1 if Layer4 not ready. */ fun getMixedIntelligentDrive(): Int = getMixIntProperty(PROP_MIX_INTELLIGENT_DRIVE) @RequiresStandstill fun setMixedIntelligentDrive(value: Int): Boolean { @@ -3161,7 +3164,7 @@ object EVHardware { if (!hasDrowsinessAndEsc()) return false // [IgnitionState], not [CarIgnitionItem]: getCurrentIgnitionState() answers on the - // AAOS scale, where the two do not line up — RUN is 2 for Katman5 and OFF is 2 here. + // AAOS scale, where the two do not line up — RUN is 2 for Layer5 and OFF is 2 here. // Unreadable is 0 or -1 and is deliberately not a refusal, per the KDoc above. val ignition = getCurrentIgnitionState() if (ignition > 0 && ignition != IgnitionState.ON) { @@ -3439,10 +3442,10 @@ object EVHardware { } } - fun isKatman4Ready(): Boolean = + fun isLayer4Ready(): Boolean = if (FirmwareInfo.isVsmBased()) sVsm != null && sVsmService != null else sVpm != null && sVpmService != null - fun isKatman4VpmCreated(): Boolean = sVpm != null || sVsm != null + fun isLayer4VpmCreated(): Boolean = sVpm != null || sVsm != null fun isCarPropertyManagerReady(): Boolean = sCarPropertyManager != null fun isCarHvacManagerReady(): Boolean = sCarHvacManager != null @@ -3540,7 +3543,7 @@ object EVHardware { } // ------------------------------------------------------------------------- - // Katman5 SWI69/SWI131 — ICarGeneralService via CarAdapterClient (queryClient(0x1)) + // Layer5 SWI69/SWI131 — ICarGeneralService via CarAdapterClient (queryClient(0x1)) // ------------------------------------------------------------------------- private data class Swi69Ctx( @@ -3566,16 +3569,16 @@ object EVHardware { return null } - private fun initKatman5Swi69(context: Context) { + private fun initLayer5Swi69(context: Context) { if (sVcmCallbackRegistered) return val classes = findSwi69Classes(context) ?: run { - AppLogger.w(TAG, " Katman5 SWI69: CarAdapterClient/CarGeneralClient not found — retry in 10s") - Handler(Looper.getMainLooper()).postDelayed({ initKatman5Swi69(context.applicationContext) }, 10_000) + AppLogger.w(TAG, " Layer5 SWI69: CarAdapterClient/CarGeneralClient not found — retry in 10s") + Handler(Looper.getMainLooper()).postDelayed({ initLayer5Swi69(context.applicationContext) }, 10_000) return } val (_, adapterClass, generalClientClass) = classes - AppLogger.i(TAG, " Katman5 SWI69: classes loaded ✓") + AppLogger.i(TAG, " Layer5 SWI69: classes loaded ✓") fun trySetupGeneralClient(): Boolean { if (sVcmCallbackRegistered) return true @@ -3589,14 +3592,14 @@ object EVHardware { adapterClass.getMethod("queryClient", Int::class.javaPrimitiveType!!) .invoke(adapter, BIND_CODE_CAR_GENERAL) as? IBinder } catch (_: Exception) { null } ?: run { - AppLogger.d(TAG, " Katman5 SWI69: queryClient(0x${BIND_CODE_CAR_GENERAL.toString(16)}) → null") + AppLogger.d(TAG, " Layer5 SWI69: queryClient(0x${BIND_CODE_CAR_GENERAL.toString(16)}) → null") return false } val client = try { generalClientClass.getConstructor(IBinder::class.java).newInstance(ibinder) } catch (e: Exception) { - AppLogger.w(TAG, " Katman5 SWI69: CarGeneralClient ctor error: ${e.message}") + AppLogger.w(TAG, " Layer5 SWI69: CarGeneralClient ctor error: ${e.message}") return false } sCarGeneral = client // kept for A9 screen brightness (setScreenBrightness) @@ -3604,13 +3607,13 @@ object EVHardware { val registMethod = generalClientClass.methods.firstOrNull { it.name == "registListener" && it.parameterCount == 1 } ?: run { - AppLogger.w(TAG, " Katman5 SWI69: registListener not found") + AppLogger.w(TAG, " Layer5 SWI69: registListener not found") return false } val callbackType = registMethod.parameterTypes[0] if (!callbackType.isInterface) { - AppLogger.w(TAG, " Katman5 SWI69: callback non-interface — proxy impossible") + AppLogger.w(TAG, " Layer5 SWI69: callback non-interface — proxy impossible") return false } @@ -3629,7 +3632,7 @@ object EVHardware { 0x7 -> { // TRANSACTION_onIgnitionStateChange data.enforceInterface("com.saicmotor.carapi.general.ICarGeneralCallback") val ignition = data.readInt() - AppLogger.i(TAG, " Katman5 SWI69: onTransact ignition=$ignition (${carIgnitionName(ignition)})") + AppLogger.i(TAG, " Layer5 SWI69: onTransact ignition=$ignition (${carIgnitionName(ignition)})") dispatchVehicleConditionIgnition(ignition) reply?.writeNoException() true @@ -3648,7 +3651,7 @@ object EVHardware { // In-process path (rare) — the service calls the interface directly val ignition = args?.get(0) as? Int if (ignition != null) { - AppLogger.i(TAG, " Katman5 SWI69: ignition=$ignition (${carIgnitionName(ignition)})") + AppLogger.i(TAG, " Layer5 SWI69: ignition=$ignition (${carIgnitionName(ignition)})") dispatchVehicleConditionIgnition(ignition) } } @@ -3657,7 +3660,7 @@ object EVHardware { null } } catch (e: Exception) { - AppLogger.w(TAG, " Katman5 SWI69: proxy creation error: ${e.message}") + AppLogger.w(TAG, " Layer5 SWI69: proxy creation error: ${e.message}") return false } @@ -3665,27 +3668,27 @@ object EVHardware { registMethod.invoke(client, proxy) sVcmListener = callbackBinder // strong reference to the Binder to avoid GC sVcmCallbackRegistered = true - AppLogger.i(TAG, " Katman5 SWI69: ICarGeneralCallback registered ✓") + AppLogger.i(TAG, " Layer5 SWI69: ICarGeneralCallback registered ✓") - val toNotify = katman5ReadyListeners.toList() - katman5ReadyListeners.clear() + val toNotify = layer5ReadyListeners.toList() + layer5ReadyListeners.clear() Handler(Looper.getMainLooper()).post { toNotify.forEach { it() } } Handler(Looper.getMainLooper()).postDelayed({ try { val ignition = generalClientClass.getMethod("getIgnitionState").invoke(client) as? Int if (ignition != null) { - AppLogger.i(TAG, " Katman5 SWI69: initial state ignition=$ignition") + AppLogger.i(TAG, " Layer5 SWI69: initial state ignition=$ignition") dispatchVehicleConditionIgnition(ignition) } } catch (e: Exception) { - AppLogger.d(TAG, " Katman5 SWI69: getIgnitionState error: ${e.message}") + AppLogger.d(TAG, " Layer5 SWI69: getIgnitionState error: ${e.message}") } }, 500L) true } catch (e: Exception) { - AppLogger.w(TAG, " Katman5 SWI69: registListener error: ${e.message}") + AppLogger.w(TAG, " Layer5 SWI69: registListener error: ${e.message}") false } } @@ -3699,11 +3702,11 @@ object EVHardware { } // ------------------------------------------------------------------------- - // Katman5 — VehicleConditionManager (IVehicleConditionService via IHubService) + // Layer5 — VehicleConditionManager (IVehicleConditionService via IHubService) // SWI133 / SWI68 / SWI165 // ------------------------------------------------------------------------- - private fun initKatman5(context: Context) { + private fun initLayer5(context: Context) { if (sVcmCallbackRegistered) return val launcherCtx = listOf(LAUNCHER68_PKG, LAUNCHER69_PKG).firstNotNullOfOrNull { pkg -> @@ -3714,15 +3717,15 @@ object EVHardware { ) } catch (_: Exception) { null } } ?: run { - AppLogger.w(TAG, " Katman5: launcher package not found") + AppLogger.w(TAG, " Layer5: launcher package not found") return } val vcmClass = try { launcherCtx.classLoader.loadClass(VCM_CLASS) } catch (e: Exception) { - AppLogger.w(TAG, " Katman5: VCM class not found: ${e.message} — retry in 10s") - Handler(Looper.getMainLooper()).postDelayed({ initKatman5(context.applicationContext) }, 10_000) + AppLogger.w(TAG, " Layer5: VCM class not found: ${e.message} — retry in 10s") + Handler(Looper.getMainLooper()).postDelayed({ initLayer5(context.applicationContext) }, 10_000) return } @@ -3734,7 +3737,7 @@ object EVHardware { } catch (_: Exception) { null } if (existing != null) { - AppLogger.i(TAG, " Katman5: VCM singleton already exists ✓") + AppLogger.i(TAG, " Layer5: VCM singleton already exists ✓") sVcm = existing setupVcmCallback(existing, launcherCtx) return @@ -3755,7 +3758,7 @@ object EVHardware { ) { _, method, args -> when (method.name) { "onServiceConnected" -> { - AppLogger.i(TAG, " Katman5: onServiceConnected ✓") + AppLogger.i(TAG, " Layer5: onServiceConnected ✓") val mgr = args?.getOrNull(0) ?.takeIf { it.javaClass.name.contains("VehicleConditionManager") } val instance = mgr ?: try { @@ -3769,7 +3772,7 @@ object EVHardware { } } "onServiceDisconnected" -> { - AppLogger.w(TAG, " Katman5: onServiceDisconnected") + AppLogger.w(TAG, " Layer5: onServiceDisconnected") sVcmCallbackRegistered = false sVcmListener = null } @@ -3777,19 +3780,19 @@ object EVHardware { null } } catch (e: Exception) { - AppLogger.d(TAG, " Katman5: proxy init error: ${e.message}") + AppLogger.d(TAG, " Layer5: proxy init error: ${e.message}") null } } else null try { initMethod.invoke(null, context.applicationContext, listenerArg) - AppLogger.i(TAG, " Katman5: VehicleConditionManager.init() called") + AppLogger.i(TAG, " Layer5: VehicleConditionManager.init() called") } catch (e: Exception) { - AppLogger.w(TAG, " Katman5: init() error: ${e.message}") + AppLogger.w(TAG, " Layer5: init() error: ${e.message}") } } else { - AppLogger.w(TAG, " Katman5: init(Context, listener) not found") + AppLogger.w(TAG, " Layer5: init(Context, listener) not found") } // Retries — the singleton is available after the async connection @@ -3803,7 +3806,7 @@ object EVHardware { f.get(null) } catch (_: Exception) { null } if (mgr != null && sVcm == null) { - AppLogger.i(TAG, " Katman5: singleton obtained @${delay}ms") + AppLogger.i(TAG, " Layer5: singleton obtained @${delay}ms") sVcm = mgr setupVcmCallback(mgr, launcherCtx) } else if (sVcm != null && !sVcmCallbackRegistered) { @@ -3824,14 +3827,14 @@ object EVHardware { m.parameterTypes[0].isInterface && m.parameterTypes[0].methods.any { it.name.contains("ConditionChange", ignoreCase = true) } } ?: run { - AppLogger.w(TAG, " Katman5: registerVehicleConditionCallback not found — methods: ${ + AppLogger.w(TAG, " Layer5: registerVehicleConditionCallback not found — methods: ${ vcm.javaClass.methods.filter { it.name.startsWith("register") }.joinToString { it.name } }") return } val callbackType = registerMethod.parameterTypes[0] - AppLogger.i(TAG, " Katman5: callback type = ${callbackType.name}") + AppLogger.i(TAG, " Layer5: callback type = ${callbackType.name}") val proxy = try { java.lang.reflect.Proxy.newProxyInstance( @@ -3846,18 +3849,18 @@ object EVHardware { // moved. Only the transitions are logged; every event is still // dispatched, because suppressing one would change behaviour. if (sIgnitionLog.accept(ignition)) { - AppLogger.i(TAG, " Katman5 event: ignition=$ignition (${carIgnitionName(ignition)})") + AppLogger.i(TAG, " Layer5 event: ignition=$ignition (${carIgnitionName(ignition)})") } dispatchVehicleConditionIgnition(ignition) } } catch (e: Exception) { - AppLogger.w(TAG, " Katman5: onChangeEvent parse error: ${e.message}") + AppLogger.w(TAG, " Layer5: onChangeEvent parse error: ${e.message}") } } null } } catch (e: Exception) { - AppLogger.w(TAG, " Katman5: proxy creation error: ${e.message}") + AppLogger.w(TAG, " Layer5: proxy creation error: ${e.message}") return } @@ -3865,10 +3868,10 @@ object EVHardware { registerMethod.invoke(vcm, proxy) sVcmListener = proxy sVcmCallbackRegistered = true - AppLogger.i(TAG, " Katman5: callback registered ✓") + AppLogger.i(TAG, " Layer5: callback registered ✓") - val toNotify = katman5ReadyListeners.toList() - katman5ReadyListeners.clear() + val toNotify = layer5ReadyListeners.toList() + layer5ReadyListeners.clear() Handler(Looper.getMainLooper()).post { toNotify.forEach { it() } } // Immediate read (the callback only fires on CHANGE) @@ -3877,13 +3880,13 @@ object EVHardware { vcm.javaClass.getMethod("getVehicleIgnition").invoke(vcm) as? Int } catch (_: Exception) { null } if (ignition != null) { - AppLogger.i(TAG, " Katman5: initial state = $ignition (${carIgnitionName(ignition)})") + AppLogger.i(TAG, " Layer5: initial state = $ignition (${carIgnitionName(ignition)})") dispatchVehicleConditionIgnition(ignition) } }, 500L) } catch (e: Exception) { - AppLogger.w(TAG, " Katman5: registerVehicleConditionCallback error: ${e.message}") + AppLogger.w(TAG, " Layer5: registerVehicleConditionCallback error: ${e.message}") } } @@ -3938,15 +3941,15 @@ object EVHardware { sb.appendLine() sb.appendLine("── Services ──") - sb.appendLine("Katman1 CPM : ${if (sCarPropertyManager != null) "✓" else "✗"}") - sb.appendLine("Katman1 HVAC : ${if (sCarHvacManager != null) "✓" else "✗"}") - sb.appendLine("Katman4 created : ${if (isKatman4VpmCreated()) "✓" else "✗"}") - sb.appendLine("Katman4 ready : ${if (isKatman4Ready()) "✓" else "✗"}") + sb.appendLine("Layer1 CPM : ${if (sCarPropertyManager != null) "✓" else "✗"}") + sb.appendLine("Layer1 HVAC : ${if (sCarHvacManager != null) "✓" else "✗"}") + sb.appendLine("Layer4 created : ${if (isLayer4VpmCreated()) "✓" else "✗"}") + sb.appendLine("Layer4 ready : ${if (isLayer4Ready()) "✓" else "✗"}") sb.appendLine("Binder vsett : ${if (sVehicleBinder != null) "✓ OK" else "✗ null"}") - val katman5Path = if (FirmwareInfo.isNewGenVsm() || gen == FirmwareInfo.Gen.SWI132) + val layer5Path = if (FirmwareInfo.isNewGenVsm() || gen == FirmwareInfo.Gen.SWI132) "ICarGeneralService" else "VehicleConditionMgr" - sb.appendLine("Katman5 ready : ${if (sVcmCallbackRegistered) "✓ ($katman5Path)" else "✗ ($katman5Path)"}") - sb.appendLine("Katman5 ign : ${carIgnitionName(sLastVcmIgnitionState)} ($sLastVcmIgnitionState)") + sb.appendLine("Layer5 ready : ${if (sVcmCallbackRegistered) "✓ ($layer5Path)" else "✗ ($layer5Path)"}") + sb.appendLine("Layer5 ign : ${carIgnitionName(sLastVcmIgnitionState)} ($sLastVcmIgnitionState)") sb.appendLine() if (gen == FirmwareInfo.Gen.SWI132) { @@ -4013,7 +4016,7 @@ object EVHardware { } sb.appendLine() - // ── CarVehicleSettingClient (Katman4) ───────────────────────── + // ── CarVehicleSettingClient (Layer4) ───────────────────────── sb.appendLine("── SWI132 CarVehicleSettingClient (sVsm=${if (sVsm != null) "✓" else "✗ null"}) ──") fun vsmGet(method: String): Int = try { (callVsm(method) as? Int) ?: -1 @@ -4177,7 +4180,7 @@ object EVHardware { fun initAudio(context: Context) { // The caradapter bind only concerns the A9. On the old SDK, loudness goes through - // SmartSoundManager (initialised in the Katman4 flow), so nothing to bind here. + // SmartSoundManager (initialised in the Layer4 flow), so nothing to bind here. if (!isA9Sound()) return if (sAudioHelper?.isBinderAlive == true) return if (sAudioServiceConn != null) return diff --git a/EVHardware/lib/src/main/java/com/evsuite/hardware/GlassProbe.kt b/EVHardware/lib/src/main/java/com/evsuite/hardware/GlassProbe.kt index 2e7d889..e38c70c 100644 --- a/EVHardware/lib/src/main/java/com/evsuite/hardware/GlassProbe.kt +++ b/EVHardware/lib/src/main/java/com/evsuite/hardware/GlassProbe.kt @@ -95,7 +95,7 @@ object GlassProbe { glass: Glass = VehicleGlass, stopped: () -> Boolean = { VehicleWriteGate.decideNow() == VehicleWriteGate.Decision.ALLOWED }, // [IgnitionState], not [CarIgnitionItem]: getCurrentIgnitionState() answers on the AAOS - // scale, and the two do not line up — RUN is 2 for Katman5, where 2 is OFF here. Read + // scale, and the two do not line up — RUN is 2 for Layer5, where 2 is OFF here. Read // against the wrong one, a car with the ignition on refused the probe as if it were off. ignitionRun: () -> Boolean = { EVHardware.getCurrentIgnitionState() == EVHardware.IgnitionState.ON }, sleep: (Long) -> Unit = { Thread.sleep(it) }, diff --git a/EVHardware/lib/src/main/java/com/evsuite/hardware/model/DrivingProfile.kt b/EVHardware/lib/src/main/java/com/evsuite/hardware/model/DrivingProfile.kt index 32c7f70..ae68535 100644 --- a/EVHardware/lib/src/main/java/com/evsuite/hardware/model/DrivingProfile.kt +++ b/EVHardware/lib/src/main/java/com/evsuite/hardware/model/DrivingProfile.kt @@ -10,7 +10,7 @@ data class DrivingProfile( val steeringHeat: Boolean = false, val seatHeatLeft: Int = 0, // 0=off, 1, 2, 3 val seatHeatRight: Int = 0, - // ADAS SWI133 (Katman4) — default OFF for compatibility with existing profiles + // ADAS SWI133 (Layer4) — default OFF for compatibility with existing profiles val overspeedAlarm: Boolean = false, val speedLimitTone: Boolean = false, val adasMode: Int = 0, // 0=Off, 1=Limiter, 2=Auto, 3=ACC, 4=ICA diff --git a/EVHardware/lib/src/main/java/com/evsuite/hardware/probe/RuntimeInterfaceProbe.kt b/EVHardware/lib/src/main/java/com/evsuite/hardware/probe/RuntimeInterfaceProbe.kt index eee97ce..e20e02a 100644 --- a/EVHardware/lib/src/main/java/com/evsuite/hardware/probe/RuntimeInterfaceProbe.kt +++ b/EVHardware/lib/src/main/java/com/evsuite/hardware/probe/RuntimeInterfaceProbe.kt @@ -471,7 +471,7 @@ object RuntimeInterfaceProbe { */ internal const val NO_ADAPTER_CLIENT = "no CarAdapterClient on this firmware — sCarAdapter is assigned only by " + - "initKatman4Swi69, so this call failed before it reached a binder. ABSENT here is " + + "initLayer4Swi69, so this call failed before it reached a binder. ABSENT here is " + "not a reading of the vehicle." /** diff --git a/SPECS.md b/SPECS.md index 143da58..1910e2c 100644 --- a/SPECS.md +++ b/SPECS.md @@ -145,8 +145,9 @@ tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does * `GlassProbeTest`). Comments, log texts and test names only: no code changed (checked in the diff). Kept on purpose: the French translation `values-fr/strings.xml`, place names in test data, the quoted French sentence in `ChargeStopPlan` and the voice phrase in `SaicNavGuidance`, - and "Katman" (Turkish for layer), which names the layers in function names and logs — now - explained at the top of `EVHardware.kt`. EVHardware, app tests and lint pass. + and "Katman" (Turkish for layer), which named the layers in function names and logs — + renamed to "Layer" right after (`whenLayer1Ready`, `initLayer4Swi69`, `Layer5: …` in the log, + and so on; nothing outside EVHardware used the names). EVHardware, app tests and lint pass. - 2026-10-25 — README.md brought up to date: the tab screenshots retaken with 2.35.2 on the emulator right after installing (`screenshots/nilsmg4-2.35.2-*.png`, the 2.18.1 ones removed), "read-only" now names the drive mode exception, the upload frequency matches the app (1 min by