From 4822991becc3afe9621cf3490291ee178bae2e6d Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 4 Oct 2026 22:26:56 -0400 Subject: [PATCH] Read an unknown SoC as Qualcomm's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic runtime is opened only when the QNN build does not fit, and the fit rests on ro.soc.manufacturer. A property the app cannot read, or an Android older than 12 that has none, read as "not Qualcomm" would put a Qualcomm device on the generic rung and off its Hexagon — what 0.22.1 had just fixed. Only a device that names another vendor is now not Qualcomm's; the tablet reports QTI. --- core/dr-inference-engine/src/hardware.rs | 33 +++++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/core/dr-inference-engine/src/hardware.rs b/core/dr-inference-engine/src/hardware.rs index c26369c..fe58e18 100644 --- a/core/dr-inference-engine/src/hardware.rs +++ b/core/dr-inference-engine/src/hardware.rs @@ -95,10 +95,12 @@ pub fn detect() -> Gpus { #[cfg(target_os = "android")] pub fn detect() -> Gpus { - // `ro.soc.manufacturer` exists from Android 12. Qualcomm's FastRPC - // library, which the Hexagon path loads anyway, is the second witness: - // a Qualcomm device read as anything else would trade its NPU for the - // generic rung. + // Fail-safe: only a device that names another vendor is not Qualcomm. + // `ro.soc.manufacturer` exists from Android 12, and a property or file + // the app cannot read reads as nothing; nothing keeps the QNN build + // first, as 0.22 had it, where a Qualcomm device mistaken for another + // would trade its NPU for the generic rung. Qualcomm's FastRPC library, + // which the Hexagon path loads anyway, overrules a name. let soc = crate::probe::system_property("ro.soc.manufacturer"); let fastrpc = [ "/vendor/lib64/libcdsprpc.so", @@ -107,13 +109,19 @@ pub fn detect() -> Gpus { .iter() .any(|p| std::path::Path::new(p).exists()); Gpus { - qualcomm: soc.eq_ignore_ascii_case("QTI") - || soc.eq_ignore_ascii_case("Qualcomm") - || fastrpc, + qualcomm: qualcomm_soc(&soc) || fastrpc, ..Gpus::default() } } +/// Whether `ro.soc.manufacturer` leaves the device Qualcomm's: it says so, +/// or it says nothing. +#[cfg(any(target_os = "android", test))] +fn qualcomm_soc(manufacturer: &str) -> bool { + let m = manufacturer.trim(); + m.is_empty() || m.eq_ignore_ascii_case("QTI") || m.eq_ignore_ascii_case("Qualcomm") +} + #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "android")))] pub fn detect() -> Gpus { Gpus::default() @@ -127,6 +135,17 @@ mod tests { p.iter().map(|s| s.to_string()).collect() } + #[test] + fn only_a_named_other_vendor_is_not_qualcomm() { + assert!(qualcomm_soc("QTI")); + assert!(qualcomm_soc("Qualcomm")); + // Unreadable, or older than Android 12: the QNN build stays first. + assert!(qualcomm_soc("")); + assert!(!qualcomm_soc("Mediatek")); + assert!(!qualcomm_soc("Google")); + assert!(!qualcomm_soc("Samsung")); + } + #[test] fn the_card_beats_the_integrated_gpu_and_both_beat_the_generic_rung() { let cpu = offers(&["CPUExecutionProvider"]);