ohmu
·
2026-10-15
1#pragma once
2
3#include <stdbool.h>
4#include <stdint.h>
5
6// The NDK marks an API unavailable whenever it is newer than the API level the build targets, and
7// fastfetch keeps running on devices below the levels it uses: AImageDecoder is 30, decoding past
8// the first frame is 31, and AMediaCodec_getName is 28. CMake compiles the files that reach for
9// them with -D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ -Werror=unguarded-availability, which turns
10// those entry points into weak references and makes every unguarded use an error. Two things follow:
11//
12// * A weak reference resolves to null on an older device, so every use needs a run-time check --
13// that is FF_ANDROID_API_AT_LEAST. Unguarded, the call would jump to a null pointer.
14// * Annotating a helper with FF_ANDROID_REQUIRES_API keeps the check in one place: the compiler rejects
15// any call of that helper which is not itself inside a guard.
16//
17// https://developer.android.com/ndk/guides/using-newer-apis
18#define FF_ANDROID_REQUIRES_API(x) [[clang::availability(android, introduced = x)]]
19#define FF_ANDROID_API_AT_LEAST(x) __builtin_available(android x, *)
20
21// The UIDs that hold android.permission.DUMP, which is what `dumpsys` checks before it prints
22// anything: every other UID is refused on stdout and the command still exits 0, which a caller cannot
23// tell apart from a command that printed nothing useful. Forking a `dumpsys` from anywhere else is
24// therefore pure waste, and for some fields `dumpsys` is the only route there is.
25//
26// The refusal is not always the permission, and the text differs per service and per release. On
27// an Android 16 device an app UID gets `Permission Denial: can't dump DisplayManagerService ... due to
28// missing android.permission.DUMP permission` for `display`, but `Can't find service: battery` for
29// `battery` -- that service is not in the list `dumpsys -l` shows an app at all, while `display` is.
30// The UID is what decides, so one check covers both.
31//
32// android.os.Process.ROOT_UID and SHELL_UID. The shell UID has a second use: it owns exactly one
33// package name, which the system services accept, so a process that cannot derive its own package
34// can answer with that one instead.
35typedef enum FFAndroidPrivilegedUid : uint32_t {
36 FF_ANDROID_PRIVILEGED_UID_ROOT = 0,
37 FF_ANDROID_PRIVILEGED_UID_SHELL = 2000,
38} FFAndroidPrivilegedUid;
39
40// Whether the given UID is allowed to run `dumpsys`. The UID is passed in rather than read here, so
41// that the caller can take it from `instance.state.platform.uid` -- which is what makes the
42// privileged path reachable from a test that does not run as root or as the shell.
43static inline bool ffAndroidIsRootOrShell(uint32_t uid) {
44 return uid == FF_ANDROID_PRIVILEGED_UID_ROOT || uid == FF_ANDROID_PRIVILEGED_UID_SHELL;
45}