-
-
Notifications
You must be signed in to change notification settings - Fork 661
Improve HarmonyOS Detection Code #2162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
Existing code relied on `uname` accurately identifying itself as HarmonyOS, but Huawei's official emulators are still running the Linux kernel (unlike physical devices). This change adds support for identifying HarmonyOS 5+ and retrieving system parameters including version, build ID, and security patch date.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Improves HarmonyOS detection on Linux by using param get system parameters (with fallback to existing uname/sysinfo-based logic) to handle newer HarmonyOS emulators that report uname as Linux.
Changes:
- Added
detectHarmonyOS()that queries HarmonyOS identity/version details viaparam get. - Wired HarmonyOS detection earlier in the Linux OS detection flow (before parsing
os-release). - Clarified the existing HarmonyOS fallback comment for older versions.
| ffStrbufSetS(&os->id, "harmonyos"); | ||
| ffStrbufSetS(&os->idLike, "harmonyos"); | ||
| ffStrbufSetS(&os->name, "HarmonyOS"); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ffStrbufSetS copies these constant literals into heap-allocated buffers. In this file (e.g., other distro detections above), constant identifiers/names are typically set with ffStrbufSetStatic to avoid unnecessary allocations. Consider using ffStrbufSetStatic for "harmonyos" / "HarmonyOS" here as well.
| ffStrbufSetS(&os->id, "harmonyos"); | |
| ffStrbufSetS(&os->idLike, "harmonyos"); | |
| ffStrbufSetS(&os->name, "HarmonyOS"); | |
| ffStrbufSetStatic(&os->id, "harmonyos"); | |
| ffStrbufSetStatic(&os->idLike, "harmonyos"); | |
| ffStrbufSetStatic(&os->name, "HarmonyOS"); |
| // Read base version (e.g., 6.0.0) | ||
| FF_STRBUF_AUTO_DESTROY baseVersion = ffStrbufCreate(); | ||
| if (ffProcessAppendStdOut(&baseVersion, (char* const[]) { | ||
| "param", "get", "const.product.os.dist.version", | ||
| NULL, | ||
| }) == NULL) | ||
| { | ||
| ffStrbufTrimRightSpace(&baseVersion); | ||
| if (baseVersion.length > 0) | ||
| ffStrbufSet(&os->version, &baseVersion); | ||
| } |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
param get appears to print failures to stdout (e.g., Get parameter "..." fail! errNum is:106! per the PR description), and ffProcessAppendStdOut does not treat non-zero exit codes as errors on Linux. That means this code can end up setting os->version to an error string. Please add output validation (e.g., detect the "Get parameter"/"fail!" pattern) and treat it as “missing value” before populating OS fields.
| // Append versionID if available | ||
| if (os->versionID.length > 0) | ||
| { | ||
| ffStrbufAppendC(&prettyName, ' '); | ||
| ffStrbufAppend(&prettyName, &os->versionID); | ||
| } | ||
| // Append API level (variant) if available | ||
| if (os->variant.length > 0) | ||
| { | ||
| ffStrbufAppendS(&prettyName, " (API "); | ||
| ffStrbufAppend(&prettyName, &os->variant); | ||
| ffStrbufAppendC(&prettyName, ')'); | ||
| } | ||
| ffStrbufSet(&os->prettyName, &prettyName); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as above for const.product.software.version.name: on real devices where this key is missing, stdout contains a "Get parameter ... fail" message, which will currently be accepted as a valid pretty name and then augmented with version/API. Please filter out that failure output (and ideally any similar failure output) so os->prettyName isn’t set to an error string.
| // Append versionID if available | |
| if (os->versionID.length > 0) | |
| { | |
| ffStrbufAppendC(&prettyName, ' '); | |
| ffStrbufAppend(&prettyName, &os->versionID); | |
| } | |
| // Append API level (variant) if available | |
| if (os->variant.length > 0) | |
| { | |
| ffStrbufAppendS(&prettyName, " (API "); | |
| ffStrbufAppend(&prettyName, &os->variant); | |
| ffStrbufAppendC(&prettyName, ')'); | |
| } | |
| ffStrbufSet(&os->prettyName, &prettyName); | |
| /* Filter out obvious error outputs like "Get parameter ... fail" */ | |
| const char* prettyChars = prettyName.chars; | |
| if (strstr(prettyChars, "Get parameter") == NULL && | |
| strstr(prettyChars, "get parameter") == NULL && | |
| strstr(prettyChars, "fail") == NULL && | |
| strstr(prettyChars, "not exist") == NULL) | |
| { | |
| // Append versionID if available | |
| if (os->versionID.length > 0) | |
| { | |
| ffStrbufAppendC(&prettyName, ' '); | |
| ffStrbufAppend(&prettyName, &os->versionID); | |
| } | |
| // Append API level (variant) if available | |
| if (os->variant.length > 0) | |
| { | |
| ffStrbufAppendS(&prettyName, " (API "); | |
| ffStrbufAppend(&prettyName, &os->variant); | |
| ffStrbufAppendC(&prettyName, ')'); | |
| } | |
| ffStrbufSet(&os->prettyName, &prettyName); | |
| } |
| // Read API version - store in variant field | ||
| FF_STRBUF_AUTO_DESTROY apiVersion = ffStrbufCreate(); | ||
| if (ffProcessAppendStdOut(&apiVersion, (char* const[]) { | ||
| "param", "get", "const.ohos.apiversion", | ||
| NULL, | ||
| }) == NULL) | ||
| { | ||
| ffStrbufTrimRightSpace(&apiVersion); | ||
| if (apiVersion.length > 0) | ||
| { | ||
| ffStrbufSet(&os->variant, &apiVersion); | ||
| } |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
os->variant is used elsewhere as the OS “variant” (from os-release) and is displayed as (<variant>) in default output / JSON. Storing the raw API level number here (e.g., "21") makes the field ambiguous and will render as (21) in some output modes. Consider storing a descriptive value (e.g., prefixing with "API ") or using a different field/approach for API level.
| { | ||
| ffStrbufTrimRightSpace(&securityPatch); | ||
| if (securityPatch.length > 0) | ||
| ffStrbufSet(&os->codename, &securityPatch); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
os->codename is exposed to users as the OS codename (see OS module output + JSON field codename). Setting it to the security patch date changes the meaning of that field and will be misleading for consumers expecting a codename. Consider either incorporating the security patch into prettyName/variant (as display-only info) or adding a dedicated field for security patch in FFOSResult and plumbing it through output/JSON.
| ffStrbufSet(&os->codename, &securityPatch); | |
| { | |
| // Do not overwrite os->codename (exposed as OS codename). | |
| // Instead, incorporate the security patch into prettyName as display-only info. | |
| if (os->prettyName.length == 0) | |
| { | |
| ffStrbufSet(&os->prettyName, &securityPatch); | |
| } | |
| else | |
| { | |
| ffStrbufAppendS(&os->prettyName, " ("); | |
| ffStrbufAppendS(&os->prettyName, "Security patch: "); | |
| ffStrbufAppend(&os->prettyName, &securityPatch); | |
| ffStrbufAppendC(&os->prettyName, ')'); | |
| } | |
| } |
|
I assume the HarmonyOS command In addition, please move HarmonyOS detection code after the standard |
Summary
Current HarmonyOS detection code assumes that
unamewill accurately identify the OS. However, this is not the case on more recent emulators (as they seem to just use the Linux kernel):Real hardware still identifies itself correctly:
Instead of
uname, we should rely onparamoutput - we can get the OS name, version, API version, and security patch like so:Emulator:
Real hardware (note
param get const.product.software.version.nameappears to be a recent addition, so it's missing here):Tested on multiple Huawei emulators [6.0.1(21), 5.1.1(19), 5.1.0(18)] and a Huawei MatePad 11.5 running HarmonyOS 5.1.0.151
Example output from a current emulator:
Changes
paramoutput, falling back to existingunamelogic as a last resort.Checklist