mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-28 07:44:06 +01:00
Global (Android): code cleanup
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// The NDK marks an API unavailable whenever it is newer than the API level the build targets, and
|
||||
// fastfetch keeps running on devices below the levels it uses: AImageDecoder is 30, decoding past
|
||||
// the first frame is 31, and AMediaCodec_getName is 28. CMake compiles the files that reach for
|
||||
@@ -7,10 +10,36 @@
|
||||
// those entry points into weak references and makes every unguarded use an error. Two things follow:
|
||||
//
|
||||
// * A weak reference resolves to null on an older device, so every use needs a run-time check --
|
||||
// that is FF_API_AT_LEAST. Unguarded, the call would jump to a null pointer.
|
||||
// * Annotating a helper with FF_REQUIRES_API keeps the check in one place: the compiler rejects
|
||||
// that is FF_ANDROID_API_AT_LEAST. Unguarded, the call would jump to a null pointer.
|
||||
// * Annotating a helper with FF_ANDROID_REQUIRES_API keeps the check in one place: the compiler rejects
|
||||
// any call of that helper which is not itself inside a guard.
|
||||
//
|
||||
// https://developer.android.com/ndk/guides/using-newer-apis
|
||||
#define FF_REQUIRES_API(x) [[clang::availability(android, introduced = x)]]
|
||||
#define FF_API_AT_LEAST(x) __builtin_available(android x, *)
|
||||
#define FF_ANDROID_REQUIRES_API(x) [[clang::availability(android, introduced = x)]]
|
||||
#define FF_ANDROID_API_AT_LEAST(x) __builtin_available(android x, *)
|
||||
|
||||
// The UIDs that hold android.permission.DUMP, which is what `dumpsys` checks before it prints
|
||||
// anything: every other UID is refused on stdout and the command still exits 0, which a caller cannot
|
||||
// tell apart from a command that printed nothing useful. Forking a `dumpsys` from anywhere else is
|
||||
// therefore pure waste, and for some fields `dumpsys` is the only route there is.
|
||||
//
|
||||
// The refusal is not always the permission, and the text differs per service and per release. On
|
||||
// an Android 16 device an app UID gets `Permission Denial: can't dump DisplayManagerService ... due to
|
||||
// missing android.permission.DUMP permission` for `display`, but `Can't find service: battery` for
|
||||
// `battery` -- that service is not in the list `dumpsys -l` shows an app at all, while `display` is.
|
||||
// The UID is what decides, so one check covers both.
|
||||
//
|
||||
// android.os.Process.ROOT_UID and SHELL_UID. The shell UID has a second use: it owns exactly one
|
||||
// package name, which the system services accept, so a process that cannot derive its own package
|
||||
// can answer with that one instead.
|
||||
typedef enum FFAndroidPrivilegedUid : uint32_t {
|
||||
FF_ANDROID_PRIVILEGED_UID_ROOT = 0,
|
||||
FF_ANDROID_PRIVILEGED_UID_SHELL = 2000,
|
||||
} FFAndroidPrivilegedUid;
|
||||
|
||||
// Whether the given UID is allowed to run `dumpsys`. The UID is passed in rather than read here, so
|
||||
// that the caller can take it from `instance.state.platform.uid` -- which is what makes the
|
||||
// privileged path reachable from a test that does not run as root or as the shell.
|
||||
static inline bool ffAndroidIsRootOrShell(uint32_t uid) {
|
||||
return uid == FF_ANDROID_PRIVILEGED_UID_ROOT || uid == FF_ANDROID_PRIVILEGED_UID_SHELL;
|
||||
}
|
||||
|
||||
@@ -29,24 +29,29 @@
|
||||
#define FF_BATTERY_ANDROID_SERVICE "batteryproperties"
|
||||
#define FF_BATTERY_ANDROID_DESCRIPTOR "android.os.IBatteryPropertiesRegistrar"
|
||||
|
||||
// android.os.Process.ROOT_UID and SHELL_UID. These two are the only UIDs that hold
|
||||
// android.permission.DUMP, which `dumpsys` checks before it prints anything. See ffDetectBattery.
|
||||
#define FF_BATTERY_ANDROID_ROOT_UID 0
|
||||
#define FF_BATTERY_ANDROID_SHELL_UID 2000
|
||||
|
||||
// BatteryManager.BATTERY_PROPERTY_*
|
||||
#define FF_BATTERY_ANDROID_PROPERTY_CAPACITY 4u
|
||||
#define FF_BATTERY_ANDROID_PROPERTY_STATUS 6u
|
||||
typedef enum FFBatteryAndroidProperty : uint32_t {
|
||||
FF_BATTERY_ANDROID_PROPERTY_CAPACITY = 4,
|
||||
FF_BATTERY_ANDROID_PROPERTY_STATUS = 6,
|
||||
} FFBatteryAndroidProperty;
|
||||
|
||||
// BatteryManager.BATTERY_STATUS_*, which the health HAL's `BatteryStatus` mirrors value for value.
|
||||
#define FF_BATTERY_ANDROID_STATUS_UNKNOWN 1u
|
||||
#define FF_BATTERY_ANDROID_STATUS_CHARGING 2u
|
||||
#define FF_BATTERY_ANDROID_STATUS_DISCHARGING 3u
|
||||
// Only the three the module has something to say about are carried: NOT_CHARGING (4) and FULL (5)
|
||||
// both mean a charger is attached, which the powered bits already say.
|
||||
typedef enum FFBatteryAndroidStatus : uint32_t {
|
||||
FF_BATTERY_ANDROID_STATUS_UNKNOWN = 1,
|
||||
FF_BATTERY_ANDROID_STATUS_CHARGING = 2,
|
||||
FF_BATTERY_ANDROID_STATUS_DISCHARGING = 3,
|
||||
} FFBatteryAndroidStatus;
|
||||
|
||||
// BatteryCapacityLevel.BATTERY_CAPACITY_LEVEL_CRITICAL, the level the framework itself shuts the
|
||||
// device down on. This is the health HAL's numbering, not the one the older
|
||||
// `BatteryManager.BATTERY_CAPACITY_LEVEL_*` constants used, which had CRITICAL at 4.
|
||||
#define FF_BATTERY_ANDROID_CAPACITY_LEVEL_CRITICAL 1
|
||||
// `BatteryManager.BATTERY_CAPACITY_LEVEL_*` constants used, which had CRITICAL at 4. The rest of the
|
||||
// enum -- -1 UNSUPPORTED, 0 UNKNOWN, 2 LOW, 3 NORMAL, 4 HIGH, 5 FULL -- only restates the
|
||||
// percentage, so only this value is carried.
|
||||
typedef enum FFBatteryAndroidCapacityLevel : int32_t {
|
||||
FF_BATTERY_ANDROID_CAPACITY_LEVEL_CRITICAL = 1,
|
||||
} FFBatteryAndroidCapacityLevel;
|
||||
|
||||
// BatteryProperty starts with [int64 mValueLong], behind the usual [exception code][return value]
|
||||
// [out-param non-null marker] prefix of an AIDL reply. API 35 appended [string8 mValueString] after
|
||||
@@ -71,9 +76,9 @@ static void initResult(FFBatteryResult* battery) {
|
||||
static const char* getProperty(FFBinder* binder, uint32_t handle, uint32_t property, uint64_t* value) {
|
||||
// `getProperty` is the third method declared in `IBatteryPropertiesRegistrar` up to Android 9 and
|
||||
// the first one from Android 10 on. Written as an `if` because clang rejects `__builtin_available`
|
||||
// -- which is what FF_API_AT_LEAST expands to -- in any other position.
|
||||
// -- which is what FF_ANDROID_API_AT_LEAST expands to -- in any other position.
|
||||
uint32_t transaction = 3u;
|
||||
if (FF_API_AT_LEAST(29)) {
|
||||
if (FF_ANDROID_API_AT_LEAST(29)) {
|
||||
transaction = 1u;
|
||||
}
|
||||
|
||||
@@ -146,7 +151,7 @@ static const char* parseBinder(FFlist* results) {
|
||||
// `dumpsys battery` prints what `BatteryService.dumpInternal()` prints: the whole of the health HAL's
|
||||
// `HealthInfo`, plus the timestamps the service keeps for its own broadcast rate limiter. It is a flat
|
||||
// `key: value` list read by name, which is what makes it the sturdier of the two routes -- a vendor
|
||||
// that prints extra keys (vivo does: `engine`, `soc decimal`, `adapter power`, `board temp status`,
|
||||
// that prints extra keys (some vendors do: `engine`, `soc decimal`, `adapter power`, `board temp status`,
|
||||
// `low bat status`, `reverse wl chg status`, `reverse wl chg exception`, `chg shut vbat`,
|
||||
// `last mode flag` and `last mode keep time`, in the middle of the list) only adds keys nobody asks
|
||||
// for, and a release that appends a field costs nothing. The binder reply is positional and breaks
|
||||
@@ -206,8 +211,9 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results) {
|
||||
}
|
||||
|
||||
if (!ffStrbufStartsWithS(&buf, "Current Battery Service state:\n")) {
|
||||
// The only other thing the command prints is `Permission Denial: can't dump Battery service`,
|
||||
// which ffDetectBattery has already ruled out by looking at the UID.
|
||||
// A refusal, which ffDetectBattery has already ruled out by looking at the UID: an app UID gets
|
||||
// `Can't find service: battery` on Android 16, and the permission denial some other
|
||||
// release would print lands here too. Both exit 0, so this is the only place that notices.
|
||||
return "Invalid `/system/bin/dumpsys battery` result";
|
||||
}
|
||||
|
||||
@@ -299,15 +305,14 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results) {
|
||||
}
|
||||
|
||||
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) {
|
||||
// `dumpsys battery` is gated behind android.permission.DUMP, which only the shell UID and root
|
||||
// hold: any other UID is answered with `Permission Denial: can't dump Battery service` on stdout
|
||||
// and a zero exit status, so forking it there costs a child process and cannot succeed. It is also
|
||||
// the richer of the two routes -- the registrar answers neither temperature nor technology, and
|
||||
// the dump is what spells out the status enum -- so it is tried first wherever it is allowed to
|
||||
// run, and the binder route covers everything else at ~0.3 ms. termux-api was dropped: it returns
|
||||
// nothing on this device and can hang for minutes, the same reason its camera path was removed.
|
||||
const uint32_t uid = instance.state.platform.uid;
|
||||
if (uid == FF_BATTERY_ANDROID_ROOT_UID || uid == FF_BATTERY_ANDROID_SHELL_UID) {
|
||||
// `dumpsys battery` needs android.permission.DUMP, which only the shell UID and root hold: an app
|
||||
// UID is answered with `Can't find service: battery` on stdout and a zero exit status, so forking
|
||||
// it there costs a child process and cannot succeed. It is also the richer of the two routes --
|
||||
// the registrar answers neither temperature nor technology, and the dump is what spells out the
|
||||
// status enum -- so it is tried first wherever it is allowed to run, and the binder route covers
|
||||
// everything else at ~0.3 ms. termux-api was dropped: it returns nothing on this device and can
|
||||
// hang for minutes, the same reason its camera path was removed.
|
||||
if (ffAndroidIsRootOrShell(instance.state.platform.uid)) {
|
||||
const char* error = parseDumpsys(options, results);
|
||||
if (error == nullptr) {
|
||||
return nullptr;
|
||||
|
||||
@@ -35,7 +35,7 @@ static bool ffCodecIsLikelySoftware(const char* codecName) {
|
||||
|
||||
// Only the name query is newer than the API level this build targets: AMediaCodec_getName and
|
||||
// AMediaCodec_releaseName are API 28, while creating and deleting a codec is API 21.
|
||||
FF_REQUIRES_API(28) static bool ffCodecIsHardwareAccelerated(AMediaCodec* codec) {
|
||||
FF_ANDROID_REQUIRES_API(28) static bool ffCodecIsHardwareAccelerated(AMediaCodec* codec) {
|
||||
if (!codec) {
|
||||
return false;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ FF_REQUIRES_API(28) static bool ffCodecIsHardwareAccelerated(AMediaCodec* codec)
|
||||
return isHardware;
|
||||
}
|
||||
|
||||
FF_REQUIRES_API(28) static const char* ffDetectCodecNativeImpl(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/) {
|
||||
FF_ANDROID_REQUIRES_API(28) static const char* ffDetectCodecNativeImpl(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/) {
|
||||
FFCodecType decoders = FF_CODEC_TYPE_NONE;
|
||||
FFCodecType encoders = FF_CODEC_TYPE_NONE;
|
||||
|
||||
@@ -92,7 +92,7 @@ FF_REQUIRES_API(28) static const char* ffDetectCodecNativeImpl(FFCodecOptions* o
|
||||
}
|
||||
|
||||
const char* ffDetectCodecNative(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/) {
|
||||
if (FF_API_AT_LEAST(28)) {
|
||||
if (FF_ANDROID_API_AT_LEAST(28)) {
|
||||
return ffDetectCodecNativeImpl(options, result);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "displayserver.h"
|
||||
#include "common/android/api.h"
|
||||
#include "common/arrutil.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/strutil.h"
|
||||
@@ -314,6 +315,16 @@ static bool detectWithCmd(FFDisplayServerResult* ds) {
|
||||
}
|
||||
|
||||
static bool detectWithDumpsys(FFDisplayServerResult* ds) {
|
||||
// `dumpsys` needs android.permission.DUMP, which only the shell UID and root hold. Every other UID
|
||||
// is answered with `Permission Denial: can't dump DisplayManagerService from from pid=..., uid=...
|
||||
// due to missing android.permission.DUMP permission` on stdout and a zero exit status, so the fork
|
||||
// buys a child process and the record loop then finds no `DisplayInfo` -- the same "no display" the
|
||||
// caller reads as "try the next route". Not forking is the only difference this makes, but it is
|
||||
// the difference between the fallback chain describing what is available and it guessing.
|
||||
if (!ffAndroidIsRootOrShell(instance.state.platform.uid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return detectWithCommand(ds,
|
||||
(char*[]) { "/system/bin/dumpsys", "display", nullptr },
|
||||
"mBaseDisplayInfo=",
|
||||
@@ -356,7 +367,7 @@ static bool detectDE(FFDisplayServerResult* ds) {
|
||||
|
||||
// vivo reports the marketing name and version in `ro.vivo.os.build.display.id`,
|
||||
// separated by an underscore (`Funtouch OS_10`, `OriginOS 5`), and the build number
|
||||
// in `ro.vivo.product.version` (`PD2505D_A_9.16.42`).
|
||||
// in `ro.vivo.product.version`.
|
||||
if (ffSettingsGetAndroidProperty("ro.vivo.os.build.display.id", &ds->dePrettyName)) {
|
||||
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
|
||||
if (ffSettingsGetAndroidProperty("ro.vivo.product.version", &buffer)) {
|
||||
@@ -663,7 +674,8 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) {
|
||||
|
||||
// `cmd` comes first because it needs no permission and therefore also works for an app UID.
|
||||
// `dumpsys` is the only route that answers on Android 12 and older, and only for `adb shell` and
|
||||
// root, and `getprop` is MiUI specific and the last resort.
|
||||
// root -- it is skipped without a fork for every other UID, see detectWithDumpsys -- and `getprop`
|
||||
// is MiUI specific and the last resort.
|
||||
if (!detectWithCmd(ds) && !detectWithDumpsys(ds)) {
|
||||
detectWithGetprop(ds);
|
||||
}
|
||||
|
||||
@@ -16,9 +16,40 @@
|
||||
// it -- the call comes back as a silent exit status 255 with nothing on either stream, which is
|
||||
// what makes it look like it worked. `dumpsys wifi` needs android.permission.DUMP, and
|
||||
// /proc/net/wireless, /proc/net/dev and /sys/class/net/wlan0 are all EACCES. That leaves
|
||||
// `IWifiManager.getConnectionInfo`, which needs only ACCESS_WIFI_STATE -- a normal permission,
|
||||
// granted without asking. There is no Termux:API fallback: it returned less than this does, and
|
||||
// needed a separate app installed.
|
||||
// `IWifiManager.getConnectionInfo`, which needs ACCESS_WIFI_STATE.
|
||||
//
|
||||
// ACCESS_WIFI_STATE is a normal permission, so the user is never asked, but it is granted to a UID
|
||||
// rather than to a package: the service checks the caller's UID, and a UID holds the permission as
|
||||
// soon as any one of the packages sharing it requests it. The question is therefore not whether the
|
||||
// host app declares it, but whether its UID ends up with it.
|
||||
//
|
||||
// Under Termux it does, and only because of a second app. `com.termux` requests no Wi-Fi permission at
|
||||
// all, while `com.termux.api` -- Termux:API -- requests ACCESS_WIFI_STATE and shares `sharedUserId
|
||||
// com.termux`, so it lands on the app's own UID. Uninstalling Termux:API takes the permission off
|
||||
// the UID with it, and this module then reports "The Wifi service raised an exception" where it had
|
||||
// reported the connection; reinstalling it brings the connection back. Both states were measured on
|
||||
// the test device with Android 16 and the same binary. So Termux:API is still needed for this module -- as
|
||||
// the thing that carries the permission, not as the `termux-api` command that used to be spawned. That
|
||||
// command is gone from here because it could not do better than this does: whenever it was available,
|
||||
// so is the permission, and whenever the permission is missing the command is missing too.
|
||||
//
|
||||
// What a UID that does not hold the permission is told, for both calls below. The service names the
|
||||
// reason itself, and the message is 93 characters:
|
||||
//
|
||||
// WifiService: Neither user <uid> nor current process has android.permission.ACCESS_WIFI_STATE.
|
||||
// at com.android.server.wifi.WifiServiceImpl.enforceAccessPermission(WifiServiceImpl.java:1541)
|
||||
// at com.android.server.wifi.WifiServiceImpl.getConnectionInfo(WifiServiceImpl.java:5537)
|
||||
//
|
||||
// It is the calling UID that is checked, not the package name in the request: the same call made as
|
||||
// the shell, asking about a package it does not own, is refused with `Package com.termux does not
|
||||
// belong to <shell uid>` instead. Nothing in this file can work around the permission, since the calling UID
|
||||
// is the process itself.
|
||||
//
|
||||
// The second call sits behind the same check, which is what makes the permission a gate on the module
|
||||
// rather than on one field: `getWifiEnabledState` from a UID that lacks it comes back with the
|
||||
// identical exception and the same message, where a UID that holds it reads the state. Such a host has
|
||||
// no route left in this file at all, and the error the caller sees is raised by the first call, before
|
||||
// the second one could have answered anything.
|
||||
//
|
||||
// The transaction code is read out of the device's own jar rather than carried as a table, so the
|
||||
// module is not tied to a list of releases. It does need the jar to exist, and Android 11 is the
|
||||
@@ -41,24 +72,24 @@
|
||||
// more accurate -- pays nothing for it.
|
||||
//
|
||||
// Three things about the call are positional rather than negotiated, and all three were measured
|
||||
// on a vivo V2505A (Android 16) and a Redmi 9A (Android 11):
|
||||
// on an Android 16 device and an Android 11 device:
|
||||
//
|
||||
// * The transaction code is a build-time constant of the `.aidl` (`getConnectionInfo` is 29 on
|
||||
// Android 11 and 41 on Android 16) -- see common/android/dex.h.
|
||||
// * The reply layout drifts, and not only by release. Android 12 dropped the duplicate length word
|
||||
// that Android 11 and 10 wrote in front of the SSID octets, and the vivo's Android 16 build
|
||||
// fills an int between the transmit and the receive speed that the Redmi's Android 11 build
|
||||
// leaves out -- while AOSP's own Android 16 writes the head exactly the Android 11 way, so that
|
||||
// one int is a vendor addition rather than a release difference. Frequency is the only channel
|
||||
// centre in the head, so it is located by value, with the whole result validated before it is
|
||||
// used. A wrong guess shows up as a rejected layout, not as a plausible wrong number.
|
||||
// that Android 11 and 10 wrote in front of the SSID octets, and the Android 16 device fills an
|
||||
// int between the transmit and the receive speed that the Android 11 device leaves out -- while
|
||||
// AOSP's own Android 16 writes the head exactly the Android 11 way, so that one int is a vendor
|
||||
// addition rather than a release difference. Frequency is the only channel centre in the head, so
|
||||
// it is located by value, with the whole result validated before it is used. A wrong guess shows
|
||||
// up as a rejected layout, not as a plausible wrong number.
|
||||
// * The tail of the parcel, which holds the Wi-Fi standard, is not laid out the same way by every
|
||||
// vendor: the Android 11 build on the Redmi carries three words there that AOSP's own Android 11
|
||||
// vendor: the Android 11 device carries three words there that AOSP's own Android 11
|
||||
// does not. It is located by shape for that reason.
|
||||
//
|
||||
// BSSID and MAC are the one place the binder route beats `cmd wifi status`: MAC addresses are
|
||||
// redacted for a caller targeting a recent SDK, and the shell targets the current one, so `cmd wifi
|
||||
// status` prints 24:**:**:**:70:2c where this reads 24:a4:87:3c:70:2c.
|
||||
// status` prints an address with its middle octets redacted where this reads the whole one.
|
||||
//
|
||||
// What is deliberately left empty:
|
||||
//
|
||||
@@ -91,8 +122,8 @@
|
||||
#define FF_WIFI_ANDROID_WIFI_STATE_UNKNOWN 4
|
||||
|
||||
// The other argument is the caller's package name. The shell UID owns exactly one package and the
|
||||
// service accepts that name, which is what `cmd` and `dumpsys` pass. See getOwnPackage().
|
||||
#define FF_WIFI_ANDROID_SHELL_UID 2000
|
||||
// service accepts that name, which is what `cmd` and `dumpsys` pass. The UID itself is
|
||||
// FF_ANDROID_PRIVILEGED_UID_SHELL, see common/android/api.h. See getOwnPackage().
|
||||
#define FF_WIFI_ANDROID_SHELL_PACKAGE "com.android.shell"
|
||||
|
||||
// An AIDL reply opens with the exception code and, for a Parcelable return, a non-null marker;
|
||||
@@ -106,7 +137,7 @@
|
||||
|
||||
// A `WifiInfo` with no connection to describe carries the sentinels for the two fields that are
|
||||
// always at a fixed offset: -1 for a network id it does not have and -127 for a signal it cannot
|
||||
// measure. Both were measured on the vivo's Android 16 reply, which is what the service answers
|
||||
// measure. Both were measured on the Android 16 device's reply, which is what the service answers
|
||||
// while Wi-Fi is on with nothing associated -- the head holds no frequency and no SSID at all, only
|
||||
// the "02:00:00:00:00:00" placeholder that `WifiInfo` writes in place of a BSSID.
|
||||
#define FF_WIFI_ANDROID_NET_ID_NONE (-1)
|
||||
@@ -132,10 +163,10 @@
|
||||
#define FF_WIFI_ANDROID_SSID_LENGTH 0x04
|
||||
|
||||
// The receive speed is the word in front of Frequency; the transmit speed is the word after the link
|
||||
// speed. The vivo writes one extra word between those two that AOSP does not -- it repeats the
|
||||
// speed. The Android 16 device writes one extra word between those two that AOSP does not -- it repeats the
|
||||
// transmit speed -- so the receive speed can only be reached from Frequency, and Frequency cannot be
|
||||
// reached by stepping over the two link speeds. Both were checked against `cmd wifi status` at the
|
||||
// same moment: 131/219 on the vivo, 86/-1 on the Redmi.
|
||||
// same moment: 131/219 on the Android 16 device, 86/-1 on the Android 11 device.
|
||||
#define FF_WIFI_ANDROID_FREQ_RX_LINK_SPEED 0x04
|
||||
#define FF_WIFI_ANDROID_SSID 0x08 // Android 12 and later
|
||||
#define FF_WIFI_ANDROID_SSID_LEGACY 0x0c // Android 11 and older
|
||||
@@ -170,15 +201,16 @@
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
// `getConnectionInfo` takes the caller's package name, and whether it is checked depends on the
|
||||
// release: the vivo answered "Package com.termux does not belong to 2000" for a name the shell does
|
||||
// not own, while the Redmi accepted the same name. Passing the real one is what works on both. There
|
||||
// is no way for a process to ask for its own package name -- it is not in /proc/self/status, and an
|
||||
// app cannot list /data/data -- but the executable path carries it: an app's binaries live under
|
||||
// /data/data/<package>/ or /data/user/<user>/<package>/, and /proc/self/exe resolves there.
|
||||
// release: the Android 16 device answered "Package com.termux does not belong to <shell uid>" for a name
|
||||
// the shell does not own, while the Android 11 device accepted the same name. Passing the real one is
|
||||
// what works on both. There is no way for a process to ask for its own package name -- it is not in
|
||||
// /proc/self/status, and an app cannot list /data/data -- but the executable path carries it: an app's
|
||||
// binaries live under /data/data/<package>/ or /data/user/<user>/<package>/, and /proc/self/exe
|
||||
// resolves there.
|
||||
//
|
||||
// A binary outside those directories has no package of its own, and that is not an edge case: a
|
||||
// static build pushed to /data/local/tmp is how this runs on a device without Termux, and the Redmi
|
||||
// reported "Cannot determine the package name of this process" for exactly that. The shell UID can
|
||||
// static build pushed to /data/local/tmp is how this runs on a device without Termux, and the Android
|
||||
// 11 device reported "Cannot determine the package name of this process" for exactly that. The shell UID can
|
||||
// be answered instead, because it has one name the service accepts. No other UID reaches here: an
|
||||
// app's own binaries are always under its data directory, so a failure there is a real failure.
|
||||
static bool getOwnPackage(char* buffer, size_t capacity) {
|
||||
@@ -202,8 +234,9 @@ static bool getOwnPackage(char* buffer, size_t capacity) {
|
||||
}
|
||||
}
|
||||
if (rest == nullptr) {
|
||||
FF_DEBUG("The executable is not in an app data directory (\"%s\"), uid %u", path, (unsigned) getuid());
|
||||
if (getuid() != FF_WIFI_ANDROID_SHELL_UID) {
|
||||
const uint32_t uid = instance.state.platform.uid;
|
||||
FF_DEBUG("The executable is not in an app data directory (\"%s\"), uid %u", path, uid);
|
||||
if (uid != FF_ANDROID_PRIVILEGED_UID_SHELL) {
|
||||
return false;
|
||||
}
|
||||
static const char shellPackage[] = FF_WIFI_ANDROID_SHELL_PACKAGE;
|
||||
@@ -281,7 +314,7 @@ typedef struct FFWifiAndroidConnection {
|
||||
//
|
||||
// What bionic's `getifaddrs` will not do is list an interface that has no address: the list is
|
||||
// built out of the addresses, so `wlan0` leaves it as soon as nothing is associated -- measured on
|
||||
// the vivo, and the Redmi behaves the same. That is exactly the state its flags are wanted in, so
|
||||
// the Android 16 device, and the Android 11 device behaves the same. That is exactly the state its flags are wanted in, so
|
||||
// when the name is missing the radio state stands in for them. See detectWithBinder().
|
||||
static void detectInterface(FFWifiAndroidConnection* connection) {
|
||||
struct ifaddrs* addrs = nullptr;
|
||||
@@ -374,8 +407,8 @@ static void findStrings(const uint8_t* data, size_t size, size_t from, FFWifiAnd
|
||||
}
|
||||
|
||||
// AOSP writes the Wi-Fi standard between three nullable strings and the two maximum-supported link
|
||||
// speeds, all of them ints. Vendors insert fields around that run -- the Android 11 build on the
|
||||
// Redmi carries three words before the standard that AOSP's own Android 11 does not -- so a fixed
|
||||
// speeds, all of them ints. Vendors insert fields around that run -- the Android 11 device carries
|
||||
// three words before the standard that AOSP's own Android 11 does not -- so a fixed
|
||||
// offset would be a guess. The run is recognised by shape instead, searching forward from the end of
|
||||
// the supplicant state: an int naming a standard, followed by two rates, in a band the standard can
|
||||
// live in.
|
||||
@@ -452,7 +485,7 @@ static const char* parseConnectionInfo(const uint8_t* data, size_t size, FFWifiA
|
||||
// older, not at all on Android 12 and later, which leaves the octets one word earlier. The one
|
||||
// that matches this release is tried first.
|
||||
uint32_t ssidOffsets[2] = { FF_WIFI_ANDROID_SSID_LEGACY, FF_WIFI_ANDROID_SSID };
|
||||
if (FF_API_AT_LEAST(31)) {
|
||||
if (FF_ANDROID_API_AT_LEAST(31)) {
|
||||
ssidOffsets[0] = FF_WIFI_ANDROID_SSID;
|
||||
ssidOffsets[1] = FF_WIFI_ANDROID_SSID_LEGACY;
|
||||
}
|
||||
@@ -540,10 +573,11 @@ static const char* parseConnectionInfo(const uint8_t* data, size_t size, FFWifiA
|
||||
}
|
||||
|
||||
// Which of the speeds the service wrote becomes a rate. It writes a negative one when it does not
|
||||
// know, which is what the Redmi does for the receive side, and a link speed of 0 when the link is
|
||||
// down. `generic` is the fallback for a direction the service left unset: the transmit side has one
|
||||
// -- the generic link speed is a real negotiated rate, just not direction-specific -- the way the
|
||||
// Linux backend falls back to SIOCGIWRATE when the station info carries no bitrate. There is no
|
||||
// know, which is what the Android 11 device does for the receive side, and a link speed of 0 when
|
||||
// the link is down. `generic` is the fallback for a direction the service left unset: the transmit
|
||||
// side has one -- the generic link speed is a real negotiated rate, just not direction-specific --
|
||||
// the way the Linux backend falls back to SIOCGIWRATE when the station info carries no bitrate.
|
||||
// There is no
|
||||
// generic receive speed, so 0 is passed there.
|
||||
static double wifiAndroidRate(int32_t specific, int32_t generic) {
|
||||
const int32_t rate = specific > 0 ? specific : generic;
|
||||
|
||||
@@ -66,7 +66,7 @@ static uint8_t* androidPackFrame(const uint8_t* src, size_t stride, uint32_t wid
|
||||
|
||||
// Everything the still and the animation path share: read the source size, resolve the requested
|
||||
// pixel size, ask for RGBA8888 and let the decoder do the scaling.
|
||||
FF_REQUIRES_API(30) static bool androidResolveDecoder(AImageDecoder* decoder, FFLogoRequestData* requestData, size_t* outStride, bool* outPremultiplied, const char** error) {
|
||||
FF_ANDROID_REQUIRES_API(30) static bool androidResolveDecoder(AImageDecoder* decoder, FFLogoRequestData* requestData, size_t* outStride, bool* outPremultiplied, const char** error) {
|
||||
const AImageDecoderHeaderInfo* header = AImageDecoder_getHeaderInfo(decoder);
|
||||
// Both are int32_t, and anything <= 0 is not a usable source
|
||||
const int32_t sourceWidth = AImageDecoderHeaderInfo_getWidth(header);
|
||||
@@ -123,7 +123,7 @@ FF_REQUIRES_API(30) static bool androidResolveDecoder(AImageDecoder* decoder, FF
|
||||
bool ffImageCreateAID(FFLogoRequestData* requestData, FFImageBuffer* out, const char** error) {
|
||||
// AImageDecoder is API 30, and fastfetch still runs on devices below that, so this is a real
|
||||
// run-time check and not a compile-time constant. See common/android/api.h for why.
|
||||
if (FF_API_AT_LEAST(30)) {
|
||||
if (FF_ANDROID_API_AT_LEAST(30)) {
|
||||
FF_AUTO_CLOSE_FD int fd = open(instance.config.logo.source.chars, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return androidImageDecoderError(error, "failed to open the image source");
|
||||
@@ -217,7 +217,7 @@ typedef struct FFAndroidAnimation {
|
||||
int32_t* delaysCs; // one per frame, in centiseconds
|
||||
} FFAndroidAnimation;
|
||||
|
||||
FF_REQUIRES_API(31) static bool androidAnimationGetFrame(FFImageAnimation* animation, uint32_t index, FFImageFrame* out, const char** error) {
|
||||
FF_ANDROID_REQUIRES_API(31) static bool androidAnimationGetFrame(FFImageAnimation* animation, uint32_t index, FFImageFrame* out, const char** error) {
|
||||
FFAndroidAnimation* session = (FFAndroidAnimation*) ffImageAnimationGetImpl(animation);
|
||||
|
||||
// Frames come out of the decoder in order, so anything before the current one means starting
|
||||
@@ -265,7 +265,7 @@ FF_REQUIRES_API(31) static bool androidAnimationGetFrame(FFImageAnimation* anima
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_REQUIRES_API(31) static void androidAnimationDestroy(FFImageAnimation* animation) {
|
||||
FF_ANDROID_REQUIRES_API(31) static void androidAnimationDestroy(FFImageAnimation* animation) {
|
||||
FFAndroidAnimation* session = (FFAndroidAnimation*) ffImageAnimationGetImpl(animation);
|
||||
if (session == nullptr) {
|
||||
return;
|
||||
@@ -281,7 +281,7 @@ bool ffImageAnimationOpenAID(FFLogoRequestData* requestData, FFImageAnimation**
|
||||
// Decoding past the first frame needs API 31: AImageDecoder_advanceFrame and
|
||||
// AImageDecoderFrameInfo are both introduced there, and AImageDecoder_decodeImage only
|
||||
// documents decoding "all of the frames" from that level on.
|
||||
if (FF_API_AT_LEAST(31)) {
|
||||
if (FF_ANDROID_API_AT_LEAST(31)) {
|
||||
FF_AUTO_CLOSE_FD int fd = open(instance.config.logo.source.chars, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return androidImageDecoderError(error, "failed to open the image source");
|
||||
|
||||
Reference in New Issue
Block a user