Exercise independent judgment, remain evidence-driven, and avoid rigid or dogmatic application of this document. These guidelines are intended to improve engineering quality, not to replace technical reasoning. If an agent determines that a rule, workflow, reference requirement, or architectural recommendation in this document is inappropriate, excessive, outdated, or counterproductive for the specific development task, it may adapt, reinterpret, or revise it as necessary. Such adjustments should preserve the document's underlying positive intent: correctness, technical rigor, maintainability, modularity, security, and sound engineering judgment. Prefer practical truth over formal compliance, and do not follow a rule mechanically when doing so would clearly produce a worse technical result.
DuckDetector is an Android environment integrity diagnostics project.
All implementation work must prioritize:
- correctness,
- modularity,
- low coupling,
- traceable technical reasoning,
- version-aware behavior,
- reproducibility,
- and conclusions proportional to the evidence collected.
Security-sensitive behavior must not be implemented from assumptions, memory, copied snippets, community folklore, or undocumented expectations.
Research must precede design, and design must precede implementation.
DuckDetector follows strict principles of:
- modularity,
- separation of concerns,
- explicit ownership,
- low coupling,
- narrow interfaces,
- replaceable implementations,
- isolated platform-specific code,
- and minimal shared mutable state.
Each feature should own its own implementation wherever practical.
A feature should not depend on internal implementation details of another feature unless that dependency is explicitly part of the architecture.
Prefer:
- feature-oriented modules,
- small and explicit interfaces,
- stable domain models,
- dependency inversion where appropriate,
- dedicated platform adapters,
- dedicated JNI bridges,
- isolated native probes,
- explicit unsupported states,
- testable components.
Avoid:
- global state,
- hidden coupling,
- cross-feature implementation leakage,
- duplicated platform logic,
- unrelated shared utility classes,
- broad abstractions with unclear ownership,
- implicit dependencies,
- monolithic detector implementations.
Shared infrastructure must exist because multiple features genuinely share the same abstraction, not merely because moving code into a common package appears convenient.
Before implementing, modifying, or drafting a non-trivial detector or platform-dependent feature, identify the subsystem responsible for the observed behavior.
Do not begin from:
"How can DuckDetector detect this?"
Begin from:
"What subsystem produces this observable behavior, and what authoritative source establishes its semantics?"
The expected workflow is:
Observed behavior
↓
Responsible subsystem
↓
Authoritative documentation
↓
Actual implementation
↓
Version and device applicability
↓
Detection model
↓
Architecture
↓
Implementation
A detector must not be designed around an observation whose origin is not sufficiently understood.
The exact references depend on the subsystem being modified.
There is no single reference hierarchy that applies equally to every detector.
The following sources are mandatory when applicable.
Consult official Android documentation first for public platform behavior.
Primary sources include:
- Android Developers documentation
- Android platform documentation on
source.android.com - Android security documentation
- Android compatibility documentation where relevant
- Android API references
- official Android architecture documentation
Documentation is not always sufficient by itself.
When implementation details affect detector semantics, verify them against the corresponding source code.
For Android userspace and framework behavior, inspect the actual AOSP subsystem responsible for the behavior.
Relevant source trees may include:
frameworks/base
system/core
system/security
system/sepolicy
bionic
art
packages/modules/*
hardware/interfaces
system/libhidl
external/*
This list is illustrative, not exhaustive.
Do not cite or reason from "AOSP" generically.
Locate the exact subsystem, class, native component, service, HAL interface, daemon, policy, or runtime code that defines, exposes, or enforces the behavior.
For example:
- process behavior may require inspecting Zygote and ActivityManager implementation,
- property behavior may require Bionic and init/property service code,
- runtime behavior may require ART,
- linker behavior may require Bionic linker code,
- permissions may require PackageManager and permission-controller implementation,
- SELinux behavior may require both AOSP policy and kernel behavior.
AOSP source must be matched to the Android version being analyzed whenever behavior may have changed between releases.
For kernel-dependent Android behavior, the primary kernel reference is not generic upstream Linux by default.
Prefer the Android kernel sources applicable to the Android environment being analyzed.
For generic Android kernel behavior, inspect the applicable:
- Android Common Kernel,
- ACK branch,
- GKI branch,
- common kernel modules,
- Android kernel documentation.
The Android version and kernel version must be matched whenever possible.
Examples of relevant distinctions include:
android-mainline
android14-5.15
android14-6.1
android15-6.6
other applicable ACK/GKI branches
Do not assume that behavior observed in one ACK branch applies unchanged to another.
Where behavior depends on an actual device implementation, inspect the applicable:
- device kernel tree,
- vendor kernel tree,
- vendor kernel modules,
- Google-maintained device kernel sources,
- SoC-specific kernel components,
- device tree sources,
- kernel configuration,
- vendor drivers.
This is particularly important for:
- procfs behavior,
- sysfs interfaces,
- Binder extensions,
- TEE drivers,
- virtualization,
- boot state,
- vendor security modules,
- firmware interfaces,
- device-specific root indicators,
- hardware-backed security behavior.
Device-specific behavior must not be generalized into an Android-wide invariant without independent evidence.
Upstream Linux remains an important reference, but it is generally a provenance and semantics source, not automatically the source of truth for the kernel actually running on an Android device.
Consult upstream Linux when necessary to determine:
- original kernel semantics,
- upstream implementation behavior,
- provenance of Android kernel patches,
- backported behavior,
- subsystem design,
- historical implementation changes,
- behavior inherited by ACK.
When relevant, compare:
upstream Linux
↓
Android Common Kernel
↓
device/vendor kernel
↓
observable behavior on Android
Never assume that mainline Linux behavior is identical to the Android kernel shipped by a device.
Architecture-specific behavior must be verified against the official architecture specification.
For Arm and AArch64 behavior, consult the applicable official Arm references.
These may include:
- Arm Architecture Reference Manual
- system register documentation
- AAPCS / ABI documentation
- Generic Timer architecture
- exception model documentation
- memory model documentation
- virtualization architecture
- instruction semantics
- privilege-level behavior
- architectural feature documentation
If a probe uses an instruction or system register, determine:
- whether it is architecturally available,
- which exception levels may access it,
- whether access can trap,
- whether virtualization can alter behavior,
- whether implementation-defined behavior exists,
- and whether the assumption holds across supported Arm architecture versions.
Do not infer architectural guarantees from behavior observed on a single SoC.
Architecture-specific x86 probes must use the applicable:
- Intel architecture manuals,
- AMD architecture manuals,
- ABI documentation,
- virtualization documentation,
- architectural CPUID or MSR documentation.
Do not assume Arm-specific reasoning applies to x86 or x86_64.
When behavior depends on SoC, firmware, TEE, boot-chain, secure-world, or vendor implementation details, inspect the applicable vendor material.
Relevant vendors may include:
- Qualcomm
- MediaTek
- Samsung
- Arm
- Intel
- AMD
- other applicable silicon or platform vendors
Relevant material may include:
- official documentation,
- open-source kernel code,
- firmware interface definitions,
- HAL definitions,
- TEE interface documentation,
- secure monitor interfaces,
- boot-chain documentation,
- technical reference manuals,
- vendor kernel modules.
Vendor-specific references are mandatory only when the feature actually depends on vendor-specific behavior.
Do not require unrelated vendor documentation for generic Android behavior.
Use the following mapping as the minimum research expectation.
| Feature or subsystem | Required references |
|---|---|
| Android public APIs | Android Developers documentation |
| Android framework internals | Android docs + relevant AOSP implementation |
| Package visibility | Android docs + PackageManager implementation |
| Permissions | Android docs + permission framework implementation |
| Zygote / process startup | AOSP Zygote + related framework/native code |
| ART / runtime | AOSP ART |
| JNI behavior | JNI specification + relevant ART/native implementation |
| Bionic / libc | AOSP Bionic |
| Dynamic linker | AOSP Bionic linker |
| System properties | AOSP Bionic + init/property service |
| Binder | AOSP Binder userspace + applicable Android kernel Binder driver |
| SELinux | Android SELinux docs + AOSP sepolicy + applicable kernel LSM/SELinux implementation |
| Mount namespaces | AOSP process/init logic + applicable Android kernel |
/proc |
applicable Android kernel + process visibility context |
| sysfs | applicable Android/device/vendor kernel |
| GKI behavior | GKI docs + applicable ACK branch |
| Kernel detector | applicable ACK/GKI branch + device/vendor kernel when needed |
| Device-specific kernel behavior | device/vendor kernel and modules |
| KeyStore | Android security docs + AOSP Keystore |
| KeyMint | Android docs + KeyMint HAL/AIDL definitions and implementation where relevant |
| StrongBox | Android docs + applicable hardware/vendor implementation |
| Attestation | Android attestation docs + AOSP/KeyMint behavior |
| TEE | Android interface + applicable SoC/TEE documentation |
| Virtualization | Android virtualization implementation + kernel + architecture specification |
| Arm instruction probes | Arm Architecture Reference Manual |
| Arm system-register probes | Arm ARM + system-register documentation |
| Arm timer probes | Arm Generic Timer documentation |
| x86 architectural probes | Intel/AMD architecture manuals |
| Qualcomm-specific behavior | applicable Qualcomm documentation/source |
| MediaTek-specific behavior | applicable MediaTek documentation/source |
| Google Tensor-specific behavior | applicable Google kernel/device/vendor source |
| Samsung-specific behavior | applicable Samsung/vendor source |
This table is a minimum, not a substitute for engineering judgment.
Android internals are highly version-sensitive.
Before relying on platform behavior, determine as precisely as practical:
- Android version,
- API level,
- AOSP release or tag,
- kernel version,
- ACK/GKI branch,
- device kernel revision,
- vendor module revision,
- CPU architecture,
- SoC family,
- OEM implementation,
- relevant security patch level.
Do not infer that behavior from one Android version applies unchanged to another.
Do not infer that behavior from one kernel branch applies unchanged to another.
Do not infer that behavior from one OEM or SoC applies to another.
Version differences should be explicitly represented in the implementation where they affect correctness.
Prefer primary sources.
Use these as the primary basis for detector semantics:
- Android official documentation
- AOSP source code
- Android Common Kernel / GKI source
- applicable device or vendor kernel source
- official architecture specifications
- official SoC/vendor documentation
- official interface specifications
These may support or explain Tier 1 sources:
- upstream Linux
- upstream project source code
- standards documents
- academic papers
- security research papers
- conference presentations
- vendor technical papers
These may be used to discover behavior, bugs, bypasses, or implementation leads:
- GitHub issues
- mailing-list discussions
- blog posts
- Stack Overflow
- forums
- Telegram
- X
- other community discussions
Tier 3 sources must not be the sole technical basis for a security-sensitive detector when authoritative sources are available.
Every detector should be explainable through the following chain:
observable signal
↓
producing subsystem
↓
implementation mechanism
↓
authoritative source or specification
↓
applicability constraints
↓
DuckDetector interpretation
If this chain cannot be established with reasonable confidence, the detector must not present the result as authoritative.
A finding must represent the strength of the evidence actually collected.
Do not implement a detector merely because a heuristic appears to work on one test device.
Before accepting a signal, determine:
- what causes it,
- whether legitimate devices can also produce it,
- whether modified devices can hide it,
- whether Android sandboxing affects visibility,
- whether SELinux affects visibility,
- whether namespaces affect visibility,
- whether OEM modifications affect it,
- whether kernel configuration affects it,
- whether architecture affects it,
- whether permissions affect it,
- whether Android version affects it.
Possible result states should include, where appropriate:
- detected,
- not detected,
- unsupported,
- unavailable,
- permission-limited,
- inconclusive,
- partially evaluated,
- lower-confidence.
Absence of evidence must not automatically be interpreted as evidence of absence.
Prefer multiple independent signals over one fragile heuristic.
Where practical, correlate evidence across:
- Kotlin/framework APIs,
- native code,
- direct syscalls,
- procfs,
- kernel-exposed state,
- Binder,
- isolated processes,
- mount namespaces,
- KeyStore,
- attestation,
- timing behavior,
- architecture-specific observations.
Signals derived from the same underlying mechanism should not be falsely represented as independent evidence.
Native code must remain narrowly scoped.
JNI is an architectural boundary.
It must not become an unrestricted shared-state mechanism between Kotlin and native code.
Prefer:
feature
↓
feature-specific JNI bridge
↓
narrow native implementation
Avoid:
global JNI utility
↓
all native detectors
↓
shared mutable state
Native APIs should:
- use explicit data structures,
- expose narrow contracts,
- clearly represent failures,
- avoid unnecessary global state,
- document ABI-specific assumptions.
Architecture-specific code must be isolated by ABI.
Supported ABIs may include:
arm64-v8a
armeabi-v7a
x86
x86_64
Do not silently assume equivalent behavior between them.
An Arm-specific implementation must not be exposed as a generic Android invariant.
Architecture-specific probes should use compile-time or runtime dispatch where appropriate and must return an explicit unsupported state on unsupported architectures.
Vendor-specific behavior must be isolated behind explicit abstractions.
For example:
generic detector interface
├─ generic Android implementation
├─ Qualcomm-specific implementation
├─ MediaTek-specific implementation
└─ Google-specific implementation
Do not scatter vendor checks throughout unrelated code.
Vendor identification itself must also be treated carefully and must not rely on one unverified property when stronger evidence is available.
Android application sandboxing fundamentally limits observable state.
Before interpreting a failed or empty probe, determine whether the result means:
condition absent
or merely:
condition not observable from this process
Consider:
- package visibility,
- SELinux,
- procfs restrictions,
- namespace isolation,
- permission restrictions,
- app UID boundaries,
- isolated processes,
- target SDK behavior,
- Android release changes.
Visibility limitations must be represented in detector semantics.
DuckDetector provides diagnostic evidence.
Do not convert heuristic evidence into claims such as:
- "the device is secure",
- "the device is compromised",
- "root is impossible",
- "the environment is trusted",
- "the TEE is genuine",
unless the underlying mechanism actually provides such a guarantee.
Prefer descriptions such as:
- evidence detected,
- evidence not observed,
- inconsistent state observed,
- probe unavailable,
- unsupported environment,
- confidence reduced,
- multiple indicators correlated.
Security conclusions must remain proportional to the evidence.
Before implementing a substantial feature:
- Define the exact security or integrity question.
- Identify the subsystem responsible for the relevant behavior.
- Read the relevant Android documentation.
- Inspect the relevant AOSP implementation.
- Inspect the applicable Android kernel source if the feature crosses into kernel behavior.
- Inspect device/vendor kernel code when behavior is device-specific.
- Inspect architecture documentation when CPU behavior matters.
- Inspect SoC/vendor documentation when implementation-specific behavior matters.
- Determine Android/version/kernel applicability.
- Identify false-positive conditions.
- Identify false-negative conditions.
- Identify visibility limitations.
- Define unsupported states.
- Define the evidence model.
- Define module ownership and boundaries.
- Define Kotlin/native boundaries if native code is required.
- Only then implement the feature.
Comments should explain why a detector is technically valid.
Do not write comments that merely restate the code.
Useful comments may identify:
- the responsible Android subsystem,
- the relevant AOSP component,
- the applicable ACK/GKI behavior,
- the architecture rule involved,
- a vendor-specific dependency,
- an Android-version limitation,
- an OEM-specific exception,
- why a result is only heuristic,
- why a fallback exists.
Where practical, security-sensitive logic should retain enough context that another contributor can trace its assumptions back to authoritative material.
Low-level probes must fail explicitly.
Do not silently map:
- syscall failure,
- permission denial,
- unavailable file,
- inaccessible service,
- unsupported architecture,
- missing hardware support,
- parser failure,
- service timeout,
to:
not detected
These states have different meanings and must remain distinguishable when they affect interpretation.
Where practical, test:
- expected clean behavior,
- expected modified behavior,
- unsupported devices,
- unsupported architectures,
- permission-restricted behavior,
- malformed input,
- inaccessible system interfaces,
- version-specific branches,
- vendor-specific branches.
A detector validated only against one developer device is not sufficient evidence of general correctness.
Detector code should remain within the feature that owns it.
Do not introduce unrelated behavior into:
- global application classes,
- common UI packages,
- generic utilities,
- unrelated repositories,
- shared native initialization,
merely for convenience.
If multiple features require the same capability, extract a narrowly defined shared abstraction only after the common responsibility is clear.
Refactoring must preserve detector semantics unless the change explicitly intends to alter them.
When moving security-sensitive logic:
- preserve behavioral assumptions,
- preserve failure-state distinctions,
- preserve version checks,
- preserve evidence confidence,
- preserve architecture-specific behavior,
- preserve process-boundary behavior.
A cleaner abstraction is not an improvement if it erases important platform distinctions.
When tradeoffs exist, use the following priority:
Correctness
↓
Technical defensibility
↓
Evidence quality
↓
Architecture
↓
Maintainability
↓
Performance
↓
Implementation convenience
Performance may justify native or architecture-specific implementation, but it must not weaken correctness or evidence semantics.
The required sequence is:
Research
↓
Verify
↓
Model
↓
Design
↓
Implement
↓
Test
↓
Document
For DuckDetector:
Research first. Design second. Implement third.
Every substantial detector should have a technically defensible path from:
platform behavior
→ authoritative source
→ observable signal
→ implementation
→ interpretation
Modularity must be preserved, coupling must remain minimal, platform behavior must be verified against the correct Android version and implementation, and security conclusions must never exceed the evidence collected.