Documentation

Code Quality & Test Coverage Audit Report

Project: Drive Assist (com.geely.drivemem)
Target Platform: Geely EX2 / Geometry E (IHU629G Head Unit, Android 9 / API 28)
Date: 2026-09-10 (Section 2 refreshed 2026-09-24 — see its own note on the methodology change; Sections 3+ are unchanged from the original audit and predate the test/coverage growth below)
Status: Integrated & Verified Baseline


1. Executive Summary

This report documents the implementation and results of the automated code quality and test coverage pipeline established for the Drive Assist automotive application.

Key milestones achieved:


2. Test Execution & Coverage Metrics

2.1 Test Execution Summary

The unit test suite consists of pure JUnit 4 tests executed against mock environments and data models:

Metric Value
Total Test Suites 35 suites
Total Tests Executed 325 tests
Failures 0
Errors 0
Skipped 0
Pass Rate 100.0%
Execution Duration ~1.0s (test task only)

Test Suite Breakdown

| Test Suite | Tests | Failures | Time | Primary Focus | |—|—|—|—|—| | com.geely.drivemem.EffortTableTest | 39 | 0 | 0.014s | HVAC comfort ruler interpolation and effort lookup tables | | com.geely.drivemem.ComfortCardVisibilityTest | 36 | 0 | 0.017s | Comfort-screen card visibility rules (gate, turbo, charging, journey, HA panel) | | com.geely.drivemem.ChargeSessionTest | 19 | 0 | 0.013s | Battery charging session start/stop, energy tracking, crash recovery | | com.geely.drivemem.ClipsTest | 18 | 0 | 0.030s | Media clip index parsing and timestamp math | | com.geely.drivemem.TripSessionTest | 16 | 0 | 0.007s | Trip park/drive edge detection, grace periods, segment stitching | | com.geely.drivemem.sensors.DrivingConsumptionTest | 16 | 0 | 0.003s | Driving energy/speed-bucket accumulation, measured-vs-estimated tally | | com.geely.drivemem.DailyStatsProviderTest | 16 | 0 | 0.033s | Daily/weekly/monthly statistics aggregation | | com.geely.drivemem.sensors.EnergySourceTest | 11 | 0 | 0.001s | Measured/estimated/mixed energy-source classification and combine() | | com.geely.drivemem.CarDataHubApplyTest | 11 | 0 | 0.017s | Car property updates and VHAL property mapping | | com.geely.drivemem.GateStateTest | 10 | 0 | 0.008s | Gate open/closed state, connected/sender consistency, press() gating | | com.geely.drivemem.EnergyIntegratorTest | 10 | 0 | 0.002s | Trapezoidal power integration, trip/window energy accumulators | | com.geely.drivemem.CarDataHubTest | 10 | 0 | 0.005s | Signal bus dispatch and listener registration | | com.geely.drivemem.CarDbSqliteTest | 9 | 0 | 0.266s | Real SQLite schema creation, migration, and query correctness against CarDb | | com.geely.drivemem.ModesTest | 8 | 0 | 0.002s | Vehicle drive mode (Eco/Sport) and regen state machines | | com.geely.drivemem.CertImporterTest | 7 | 0 | 0.292s | TLS PKCS12 / PEM certificate import and validation | | com.geely.drivemem.UpdaterInstallGateTest | 6 | 0 | 0.000s | OTA install-blocked gate: charging/session/grace-period checks | | com.geely.drivemem.TelemetryTest | 6 | 0 | 0.004s | Telemetry number formatting and rounding rules | | com.geely.drivemem.OdoStatsTest | 6 | 0 | 0.005s | Odometer math, delta calculations, and daily counters | | com.geely.drivemem.MusicStateTest | 6 | 0 | 0.005s | Media session metadata and playback state | | com.geely.drivemem.DailyStatsProviderSqliteTest | 6 | 0 | 0.021s | Real SQL query tests for daily/period energy-balance aggregation | | com.geely.drivemem.ChargeSessionLogTest | 6 | 0 | 0.019s | Charging session CSV export and log rotation | | com.geely.drivemem.sensors.Obd2ReaderTest | 5 | 0 | 0.001s | Regression coverage for the single-dropped-packet “shown as estimated” bug | | com.geely.drivemem.controls.PurgeTest | 5 | 0 | 0.002s | Per-window crack-open target regulator math | | com.geely.drivemem.CarTraceTest | 5 | 0 | 0.005s | Car property change tracing/logging | | com.geely.drivemem.CarActorHarnessScenarioTest | 5 | 0 | 0.049s | Deterministic CarActor scenarios via the synchronous test harness | | com.geely.drivemem.AbrpUploaderTest | 5 | 0 | 0.041s | ABRP telemetry JSON payload serialization | | com.geely.drivemem.TelemetryChargingSourceTest | 4 | 0 | 0.001s | car.is_charging derivation from charge_a + plug_connected | | com.geely.drivemem.PanelStateTest | 4 | 0 | 0.002s | Comfort panel UI display state | | com.geely.drivemem.MqttDiscoveryTest | 4 | 0 | 0.001s | Home Assistant MQTT discovery JSON payloads | | com.geely.drivemem.UpdaterCacheBustTest | 3 | 0 | 0.007s | OTA URL cache-busting shared by check()/update() | | com.geely.drivemem.ui.ChargeCostDialogTest | 3 | 0 | 0.031s | Charge-cost entry dialog input parsing | | com.geely.drivemem.MqttTlsTest | 3 | 0 | 0.110s | MQTT TLS handshake/config | | com.geely.drivemem.net.AbrpSamplerTest | 2 | 0 | 0.001s | ABRP sampler poll-delay cadence (active vs. idle) | | com.geely.drivemem.EntityBusTest | 2 | 0 | 0.001s | Pub/sub bus change-only delivery and duplicate-listener semantics | | com.geely.drivemem.CarActorDeriveTest | 2 | 0 | 0.027s | deriveFrom() computed-property recomputation on every ingest | | com.geely.drivemem.sensors.CarTraceConsumptionTest | 1 | 0 | 0.003s | Car trace consumption helper |


2.2 JaCoCo Code Coverage Metrics

Methodology unchanged since the 2026-09-14 refresh (see git history for that note); numbers below are a straight refresh of the same scoped measurement, run 2026-09-24 after this session’s test additions (typed Prefs, real-SQLite tests, the CarActor test harness, ComfortCardVisibility, and the energy-source/gate fixes). The jacocoTestReport task excludes ui/, art/, services/, and controls/ from the coverage ratio — those packages have no unit-test harness (no Robolectric; they’re exercised on a real device instead), so folding their all-zero instruction counts into the denominator measured “how much of the app is UI” rather than “how much of the tested logic is tested.” The excluded code is still compiled and shipped, and still covered — just by on-device testing instead of this report.

JaCoCo code coverage was captured during testDebugUnitTest, scoped to the testable packages (hvac, state, car, sensors, net, util, plus the top-level package):

Counter Type Total Covered Missed Coverage %
Instructions 42,315 8,395 33,920 19.84%
Branches 4,460 950 3,510 21.30%
Lines 7,231 1,633 5,598 22.58%
Cyclomatic Complexity 3,443 642 2,801 18.65%
Methods 1,191 289 902 24.27%
Classes 123 53 70 43.09%

Package Coverage Breakdown (testable packages only)

| Package | Instructions Covered | Instruction % | Lines Covered | Line % | Branches Covered | Branch % | |—|—|—|—|—|—|—| | com.geely.drivemem.car | 2,221 / 5,908 | 37.6% | 346 / 904 | 38.3% | 181 / 495 | 36.6% | | com.geely.drivemem.state | 1,805 / 5,202 | 34.7% | 493 / 1,097 | 44.9% | 207 / 708 | 29.2% | | com.geely.drivemem.hvac | 726 / 2,625 | 27.7% | 105 / 431 | 24.4% | 137 / 402 | 34.1% | | com.geely.drivemem.sensors | 2,112 / 8,517 | 24.8% | 402 / 1,503 | 26.7% | 237 / 996 | 23.8% | | com.geely.drivemem.net | 1,118 / 10,341 | 10.8% | 225 / 1,732 | 13.0% | 151 / 1,111 | 13.6% | | com.geely.drivemem.util | 413 / 9,683 | 4.3% | 62 / 1,545 | 4.0% | 37 / 748 | 4.9% | | com.geely.drivemem (root) | 0 / 39 | 0.0% | 0 / 19 | 0.0% | — | — |

car and state overtook hvac as the best-covered packages this session — the CarActor test harness, EntityBus/CarDataHub tests, ChargeSession/TripSession/GateState growth, and ComfortCardVisibility (extracted specifically to make it unit-testable) all landed there.

Excluded from the ratio (compiled and shipped; exercised on a real device instead of a JVM unit test — sizes below are from the 2026-09-14 baseline, not re-measured this pass, and have likely grown given this session’s TelemetryActivity split into per-section ui/ classes): | Package | Instructions | Why excluded | |—|—|—| | com.geely.drivemem.ui | ~20,700 | Activities/custom Views — need a real window manager | | com.geely.drivemem.art | ~5,300 | Canvas art renderers — visual output, checked by eye on-device | | com.geely.drivemem.services | ~2,000 | Background services — need NotificationManager/system state | | com.geely.drivemem.controls | ~1,700 | Physical car-control widgets — need a live VHAL connection |

Key Classes Coverage Analysis


3. Static Code Analysis: Android Lint

3.1 Configuration Rationale (Android 9 / API 28 Automotive Head Unit)

The application is deployed directly to the vehicle head unit (Geely IHU629G / ECARX 250060) as a sideloaded system utility. It is not distributed via Google Play. Standard Android Lint settings produce extensive false positives that obstruct build pipelines:

  1. ExpiredTargetSdkVersion: Google Play requires API 33+, whereas the vehicle OS is Android 9 (API 28). Disabled to permit targeting API 28.
  2. MissingTranslation: Non-blocking partial localizations (e.g. Catalan, Basque, Hungarian). Suppressed to prevent translation gaps from breaking compilation.
  3. MissingPermission: Flagged BluetoothAdapter.enable() for missing android.permission.BLUETOOTH_CONNECT. BLUETOOTH_CONNECT is an API 31+ runtime permission that does not exist in API 28. Suppressed as a false positive.
  4. ObsoleteSdkInt: Redundant backward compatibility guards (Build.VERSION.SDK_INT >= 26) retained intentionally for potential deployment across legacy ECARX variants. Suppressed.
  5. StringFormatMatches & StringFormatInvalid: Elevated to fatal errors to catch format string mismatch crashes at compile time.

3.2 String Formatting Defect Remediated

3.3 Lint Summary Findings


4. Static Code Analysis: Checkstyle

4.1 Configuration

Checkstyle 10.12.7 was configured via config/checkstyle/checkstyle.xml with rules tailored for Android Java 8/11 development:

4.2 Findings Summary

A total of 2,338 warnings across 68 files were recorded:

Checkstyle Rule Violations Description
NeedBracesCheck 788 Single-line if, for, while statements without braces
LeftCurlyCheck 624 Inconsistent brace placement on method/class headers
OneStatementPerLineCheck 551 Compact ternary or assignment chains on a single line
MultipleVariableDeclarationsCheck 145 Multiple fields declared on a single line (e.g., int a = 1, b = 2;)
UnusedImportsCheck 118 Leftover unused import statements across activities
RightCurlyCheck 72 Closing brace formatting on else / catch blocks
WhitespaceAfterCheck 36 Missing space after commas or cast operators
MissingSwitchDefaultCheck 2 Switch statements lacking a default case branch
ModifierOrderCheck 1 Non-standard modifier order (e.g. final static instead of static final)
RedundantModifierCheck 1 Redundant public on interface method

5. Static Code Analysis: SpotBugs

5.1 Configuration

SpotBugs 6.0.26 was integrated with Effort.MAX and Confidence.MEDIUM. Filter config/spotbugs/exclude.xml eliminates generated classes (R$*, BuildConfig) and intentional singleton context caches.

5.2 Findings Summary

A total of 117 SpotBugs issues were identified across 6 categories:

Category Count Primary Implications
BAD_PRACTICE 39 Ignored return values, non-standard comparator contracts
MALICIOUS_CODE 29 Public static mutable fields and arrays
MT_CORRECTNESS 20 Static DateFormat instances invoked across threads
I18N 18 String.getBytes() or FileReader using default platform encoding
STYLE 8 Unread fields, non-short-circuit boolean evaluation
PERFORMANCE 3 Boxing in loops, suboptimal random usage

Critical SpotBugs Patterns

  1. STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE (18 occurrences):
    • java.text.SimpleDateFormat is not thread-safe. Static formatters shared across threads in TelemetryActivity, MqttReporter, and ChargeSessionLog can corrupt date strings or throw exceptions during concurrent vehicle state updates.
    • Remediation: Replace with java.time.format.DateTimeFormatter (thread-safe) or wrap in ThreadLocal<SimpleDateFormat>.
  2. DM_DEFAULT_ENCODING (18 occurrences):
    • Relying on the default platform charset when writing CSV logs or reading config files creates silent data corruption when moving between head unit firmware builds.
    • Remediation: Explicitly pass StandardCharsets.UTF_8.
  3. RV_RETURN_VALUE_IGNORED_BAD_PRACTICE (12 occurrences):
    • Return values of File.mkdirs() and File.delete() are ignored in CertImporter, ChargeSessionLog, and AdbGate. If storage is full or read-only, failures occur silently.
    • Remediation: Check return boolean and log error conditions.

6. Catalog of Deprecated APIs

Direct compilation inspection with javac -Xlint:deprecation identified 12 distinct deprecated API patterns:

# Deprecated API Deprecated In Recommended Replacement Project Locations
1 BluetoothAdapter.getDefaultAdapter() API 31 BluetoothManager.getAdapter() Obd2Reader.java:291, 321, 439
CarplayState.java:64
2 BluetoothAdapter.enable() API 33 User prompt via ACTION_REQUEST_ENABLE CarplayState.java:67
3 BluetoothGattDescriptor.setValue(byte[])
BluetoothGatt.writeDescriptor(desc)
API 33 BluetoothGatt.writeDescriptor(desc, value) Obd2Reader.java:580, 581
4 BluetoothGattCallback.onCharacteristicChanged(gatt, char)
BluetoothGattCharacteristic.getValue()
API 33 onCharacteristicChanged(gatt, char, byte[]) Obd2Reader.java:590, 591
5 BluetoothGattCharacteristic.setValue(byte[])
BluetoothGatt.writeCharacteristic(char)
API 33 BluetoothGatt.writeCharacteristic(char, byte[], writeType) Obd2Reader.java:676, 677
6 WifiManager.setWifiEnabled(boolean) API 29 Settings.Panel.ACTION_WIFI / Privileged API BootReceiver.java:123, 211
OutTempService.java:55
7 LayoutParams.FLAG_TRANSLUCENT_STATUS
View.SYSTEM_UI_FLAG_*
View.setSystemUiVisibility(int)
API 30 WindowInsetsController Style.java:242, 245, 249, 250
8 WifiManager.getConnectionInfo()
WifiManager.getDhcpInfo()
API 31 ConnectivityManager.getNetworkCapabilities() AdbGate.java:64, 120
WifiIconService.java:76, 85
9 WifiManager.calculateSignalLevel(int, int) API 30 WifiManager.calculateSignalLevel(int) WifiIconService.java:78
10 PackageInfo.versionCode API 28 PackageInfo.getLongVersionCode() Updater.java:117
11 X509Certificate.getSubjectDN() Java 16 X509Certificate.getSubjectX500Principal() MqttTls.java:105
12 LocationListener.onStatusChanged(String, int, Bundle) API 29 Removed / default no-op in API 29+ TelemetryService.java:169

7. Cyclomatic Complexity Hotspots

7.1 Top 10 Most Complex Classes

Branch token analysis (if, for, while, case, catch, &&, ||, ?):

Rank Class / File Total Lines Total Complexity Key Responsibilities & Architectural Burden
1 com.geely.drivemem.ui.TelemetryActivity 2,456 274 Monolithic activity: MQTT UI, live logs, diagnostic series, certificate importation
2 com.geely.drivemem.ui.ComfortActivity 1,685 265 Main launcher UI: column layout, climate ruler, gate card, modal dialogs
3 com.geely.drivemem.net.MqttReporter 1,404 260 MQTT discovery JSON serialization, payload dispatch, connection state machine
4 com.geely.drivemem.sensors.Obd2Reader 746 171 BLE scanner, GATT characteristics parser, ELM327 protocol engine
5 com.geely.drivemem.net.CertImporter 753 166 PKCS12 / PEM extraction, KeyStore installation, TLS socket validation
6 com.geely.drivemem.util.Diagnostics 559 148 ADB broadcast debug action dispatcher (FANTEST, AUDIOPROBE, MEMDUMP)
7 com.geely.drivemem.car.CarAccess 633 138 CarPropertyManager abstraction, property ID reflection, vendor adaptors
8 com.geely.drivemem.net.AbrpUploader 539 128 ABRP HTTP payload generator, upload state machine, file audit logger
9 com.geely.drivemem.hvac.ComfortRuler 455 121 Temperature target blending, AC/fan calculations, debounce rules
10 com.geely.drivemem.art.VaporArtView 857 106 Canvas rendering, starfield animation, Konami code particle animations

7.2 Top 10 Most Complex Methods

Rank Class & Method Line Complexity Method Description & Refactoring Priority
1 Obd2Reader.scanRecordName 512 64 Low-level BLE PDU record parser traversing raw advertising byte arrays. Refactor to separate binary parser.
2 ClipPlayerActivity.tick 324 45 Media player timer loop managing UI timeline, video sync, and state transitions.
3 AbrpUploader.buildTlm 367 39 Giant JSON telemetry builder mapping vehicle signals and GPS coordinates.
4 ComfortRuler.apply 279 38 Main climate control decision tree mapping target temp to physical HVAC actions.
5 ComfortActivity.stopCarActorPolls 1,481 35 Teardown and sensor poll unregistration sequence.
6 CertImporter.testTls 620 34 Multi-fallback TLS handshake testing custom SSLSocket factories.
7 Updater.check 64 34 Remote update checker parsing APK archive headers and version queries.
8 Telemetry.roundFor 61 30 Formatting and scale truncation rules across dozens of data channels.
9 DailyStatsProvider.getDayOverview 204 29 SQLite aggregation query over daily trip sessions.
10 Style.darken 466 29 Color arithmetic and theme palette shading.

8. Unhandled Lifecycle States & Architectural Risks

8.1 Activity Lifecycle Deficiencies

  1. TelemetryActivity.java:
    • Missing onPause and onStop: Event listeners (EntityBus, Obd2Reader, AbrpUploader) registered in onCreate remain active when the user switches to another app or the head unit screen sleeps. This leaks CPU cycles and battery.
    • Remediation: Unregister bus listeners in onPause() / onStop() and re-register in onResume().
  2. ComfortActivity.java:
    • Missing onSaveInstanceState / onRestoreInstanceState: If the system terminates the process while parked or during deep sleep, UI state (active card selections, ruler positions) resets to default values.
    • Remediation: Store active tab index and temporary settings in the saved instance state bundle.
  3. ClipPlayerActivity.java:
    • Missing onStop and onSaveInstanceState. Media playback threads can remain active in background.

8.2 Service Lifecycle Gaps

8.3 Ineffective Lifecycle Broadcasts

8.4 Static Context References


9. Verification & Pipeline Commands

All code quality and test coverage tasks are fully verified via Gradle:

# 1. Run unit tests and generate JaCoCo coverage reports
./gradlew test jacocoTestReport
# Artifacts:
#   drivemem/build/reports/jacoco/test/jacocoTestReport.xml
#   drivemem/build/reports/jacoco/test/html/index.html
#   drivemem/build/reports/tests/testDebugUnitTest/index.html

# 2. Verify clean production release build (uninstrumented)
./gradlew :drivemem:assembleRelease
# Artifact:
#   drivemem/build/outputs/apk/release/drive_assist.apk

# 3. Run all static analysis checks
./gradlew lint checkstyle spotbugs
# Artifacts:
#   drivemem/build/reports/lint-results.html (.xml, .txt)
#   drivemem/build/reports/checkstyle/checkstyle.html (.xml)
#   drivemem/build/reports/spotbugs/spotbugs.html (.xml)