Project 1: Capability Navigator
Build a read-only-first control plane that explains what an Android automation app can do now, what requires user action, and why.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 1 — Beginner |
| Time Estimate | 10-14 hours |
| Language | Kotlin; Java is a viable alternative for platform checkers |
| Prerequisites | Kotlin basics, Android components, Compose state, basic testing |
| Key Topics | Capability modeling, permissions, special access, lifecycle, consent UX, compatibility |
1. Learning Objectives
By completing this project, you will:
- Distinguish platform support, declared authority, user consent, service health, and administrative mode.
- Model capability state as an exhaustive domain result rather than a Boolean permission flag.
- Build consent journeys that start with explanation and end with verification.
- Refresh capability truth after lifecycle changes, process recreation, and external Settings changes.
- Reuse one capability registry from both Compose UI and headless execution code.
- Explain safe degradation when an optional capability is denied or revoked.
- Test unsupported, denied, temporarily unavailable, and policy-managed states without broad access.
2. Theoretical Foundation
2.1 Core Concepts
Capability versus permission
A permission is one possible gate. A capability is the complete answer to whether a specific operation can be attempted safely. The answer can depend on API level, hardware feature, manifest declaration, runtime permission, app-op, special Settings access, enabled service, package role, one-session consent token, current connection health, or enterprise provisioning. The registry must retain this richer meaning.
Static support versus dynamic authority
Static checks answer whether the installed build and device could support a feature. Dynamic checks answer whether the app may use it now. Notification-listener support can exist while access is disabled. Exact-alarm APIs can exist while special access is absent. Device-policy APIs can exist while a normal consumer install has no owner authority.
Process lifetime and re-verification
An Android process is disposable. Cached capability truth can become false while the app is backgrounded. The user may revoke access in Settings, a service may disconnect, policy may change, or the package may be updated. A lifecycle return is therefore a signal to observe reality again, not proof that the previous result remains valid.
Consent journey
A trustworthy journey contains purpose, data scope, visible behavior, denial behavior, system transition, return verification, and revocation instructions. It requests authority only when the user enables a dependent feature. A consent journey is not a splash-screen checklist and does not repeatedly nag after denial.
Evidence-bearing state
Every state needs a reason code and evidence. Needs consent because notification access is disabled differs from temporarily unavailable because the listener is enabled but disconnected. Unsupported because the API is missing differs from misconfigured because the manifest component was omitted.
2.2 Why This Matters
Tasker-style products combine many unrelated Android authority systems. If every adapter invents its own permission check, the editor, executor, and support UI will disagree. Users see actions that cannot run, background code attempts forbidden work, and diagnostics reduce everything to permission denied.
A capability control plane prevents that drift. The rule editor can mark an action unavailable without deleting it. The executor can reject stale authority before a side effect. The support screen can show the exact failing gate. Product copy can promise only what the current installation mode supports.
This architecture also limits pressure to request everything during onboarding. Users can explore an automation in simulation, understand its requirement, and decide whether that single benefit justifies the access. Denial becomes a normal modeled state.
2.3 Historical Context / Background
Early Android applications often treated manifest declarations as the primary authority story. Runtime permissions, special app access, package roles, background limits, target-SDK behavior changes, and policy controls gradually made that model incomplete. Modern automation software must coordinate multiple Settings surfaces and service lifecycles while remaining truthful across API levels and OEM variations.
This progression is a security feature, not an obstacle to route around. Android separates high-impact authority from ordinary app installation so the user or administrator retains control. Capability-aware design accepts that authority can be absent or revoked.
2.4 Common Misconceptions
- A granted permission means the feature is healthy: a user-enabled service may be disconnected or unavailable in the current profile.
- An API existing means a normal app can use it: device owner, privileged permissions, shell, and root are separate installation identities.
- The app should request all access once: progressive journeys are clearer, safer, and easier to justify.
- Returning from Settings means success: the app must recheck current state.
- Unsupported and denied are equivalent: one may be resolvable by the user; the other may require a different device or product mode.
- A service runs forever: process and service lifetime remain system-controlled.
3. Project Specification
3.1 What You Will Build
Build a Compose application with a capability dashboard, detail sheet, consent-journey coordinator, debug-state simulator, and reusable registry. Include these capability families:
- Ordinary runtime permission.
- Notification-listener access.
- Usage access.
- Exact-alarm access.
- Accessibility-service enablement.
- MediaProjection session capability.
- Plugin/package availability.
- Managed-device authority.
The first release remains read-only for sensitive services. It observes and explains state; it does not implement notification collection, UI automation, capture, or policy changes.
3.2 Functional Requirements
- Capability catalog: Render every descriptor in a stable category and order.
- Exhaustive status: Show Available, Needs consent, Temporarily unavailable, Unsupported, Policy-managed, or Misconfigured.
- Reason evidence: Present a machine-stable reason code and human explanation.
- Resolution metadata: Offer a system journey only when a safe handler exists.
- Progressive disclosure: Explain purpose, observed data, visible indicator, degraded mode, and revocation before leaving the app.
- Return verification: Refresh after Settings or consent activity returns.
- Lifecycle refresh: Recheck on foreground return without creating a request loop.
- Debug simulation: Override checker results in debug builds only.
- Headless query: Allow later executors to ask for one capability without depending on Compose.
- Safe fallback: Show manual navigation instructions when an OEM has no matching Settings intent.
3.3 Non-Functional Requirements
- Privacy: No capability grant is requested at launch and no sensitive source data is collected.
- Reliability: A failed checker yields typed diagnostic state rather than crashing the dashboard.
- Usability: Every card answers what, why, current state, next step, and how to revoke.
- Accessibility: Status is communicated through text and semantics, not color alone.
- Compatibility: Checkers isolate API-level differences behind one domain port.
- Testability: Platform reads and resolution launchers are replaceable with fakes.
3.4 Example Usage / Output
Automation capabilities
Notification access
Status: NEEDS CONSENT
Evidence: notification listener is declared but not enabled
Data if enabled: package, channel, selected notification fields
Degraded behavior: notification-triggered rules remain disabled
[Review access]
Managed device
Status: POLICY-MANAGED
Evidence: app is not profile owner or device owner
Resolution: no consumer consent journey exists
3.5 Real World Outcome
Open the app and land on a dashboard grouped into Signals, Timing, Interaction, Integrations, and Administration. Each card has an icon, title, status label, one-line evidence, and disclosure arrow. The status remains understandable in grayscale and under TalkBack.
Expand Notification access. The sheet explains that future notification rules can observe only packages selected by the user, that access is enabled in system Settings, and that revocation disables dependent rules without deleting them. Press Review access. The system settings page opens. Return without enabling it; the card still says Needs consent and does not nag.
Use the debug drawer to simulate service disconnected. The same card changes to Temporarily unavailable and reports that authority exists but runtime health is absent. Simulate a missing manifest component; it becomes Misconfigured and offers no misleading Settings action.
Rotate the device, background the app, alter a grant in Settings, and return. The dashboard rechecks automatically and preserves only presentation state. Force-stop and reopen; the result is reconstructed from platform reality. Install on an older emulator or fake an unsupported API and confirm the card explains the missing platform requirement.
4. Solution Architecture
4.1 High-Level Design
+----------------------+
| Compose dashboard |
| cards + detail sheet |
+----------+-----------+
|
v
+----------------------+
| Capability registry |
| query / observe all |
+----+------------+----+
| |
static checkers dynamic checkers
| |
API / feature / grant / service /
manifest support role / owner state
| |
+------+-----+
|
v
CapabilityAssessment
state + reason + evidence
|
+---------------+----------------+
| |
consent journey coordinator headless executor gate
|
system surface -> return -> recheck
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| CapabilityDescriptor | Stable identity and product metadata | Contains no Android Context or live grant |
| CapabilityChecker | Reads one platform capability | Returns evidence-bearing assessment |
| CapabilityRegistry | Queries, combines, and observes checkers | Single source for UI and executor |
| ConsentJourney | Describes rationale, scope, resolution, denial, revocation | Separate from checker |
| JourneyCoordinator | Launches resolvable system surfaces and rechecks | Never assumes result from navigation |
| CapabilityViewModel | Converts registry stream into immutable screen state | Survives configuration changes |
| DebugOverrideSource | Produces deterministic fake states | Removed or disabled in release |
4.3 Data Structures
CapabilityDescriptor
id
category
title
required_platform
data_classes
degraded_behavior
CapabilityAssessment
capability_id
state
reason_code
evidence
checked_at
resolution_descriptor optional
CapabilityState
AVAILABLE
NEEDS_CONSENT
TEMPORARILY_UNAVAILABLE
UNSUPPORTED
POLICY_MANAGED
MISCONFIGURED
Do not serialize Android Intent objects into the domain result. A resolution descriptor names an abstract journey; the Android layer builds and verifies the actual system intent.
4.4 Algorithm Overview
Key Algorithm: Assess One Capability
- Load the stable descriptor.
- Check minimum API and required device features.
- Confirm build-time manifest declarations or components.
- Check current grant, role, service enablement, session state, or owner mode.
- Check transient health when the capability has a connection lifecycle.
- Select the most specific state and reason code.
- Attach a resolution only when user action can legitimately change the state.
- Timestamp and emit the immutable assessment.
Complexity Analysis:
- Time: O(C) for a full refresh across C capabilities, assuming each checker performs bounded platform queries.
- Space: O(C) for the current assessment snapshot.
5. Implementation Guide
5.1 Development Environment Setup
$ adb devices
List of devices attached
emulator-5554 device
$ adb shell getprop ro.build.version.sdk
36
Use the current stable Android Studio, SDK Platform 36 reference emulator, an older supported emulator, Compose, lifecycle libraries, and Android test tooling. Do not enable broad sensitive capabilities merely to complete setup.
5.2 Project Structure
capability-navigator/
├── app/
│ ├── ui/capabilities/
│ ├── navigation/
│ └── platform/journeys/
├── capability-domain/
│ ├── descriptor/
│ ├── assessment/
│ └── registry/
├── capability-android/
│ ├── runtime-permission/
│ ├── special-access/
│ ├── service-state/
│ └── managed-mode/
└── tests/
├── domain/
├── instrumented/
└── fixtures/
5.3 The Core Question You Are Answering
What does this app actually have the authority to do right now, and how can it explain that truth to both the user and the executor?
Before implementation, write one sentence distinguishing support, authority, and health. If those collapse into one Boolean, redesign the model.
5.4 Concepts You Must Understand First
Stop and research these before coding:
- Android process and component lifetime
- What survives configuration change but not process death?
- Why is a service not an immortal daemon?
- Book Reference: Operating Systems: Three Easy Pieces — Processes.
- Permission classes
- Which permissions use an in-app dialog?
- Which use Settings, roles, session consent, or provisioning?
- Official Reference: Android permissions overview.
- State machines and sealed results
- How does exhaustive rendering prevent hidden fallbacks?
- What evidence should accompany each state?
- Book Reference: Effective Java, 3rd Edition — Items 15-25.
- Lifecycle-aware observation
- When should the registry refresh?
- How will duplicate resume signals be coalesced?
- Consent UX
- Can the user understand purpose before leaving for Settings?
- Can they find revocation instructions later?
5.5 Questions to Guide Your Design
- Which checks are static and which can change while the app is backgrounded?
- Which state wins when the API is supported but the manifest is wrong?
- What distinguishes enabled service from connected service?
- Can a consumer user resolve a policy-managed state?
- How will OEM-specific missing handlers degrade?
- Does denial preserve the future rule configuration?
- Can headless execution obtain the same reason code shown by the UI?
- How will debug overrides remain impossible in release?
5.6 Thinking Exercise
Draw Four Authority State Machines
Draw runtime location, notification access, MediaProjection, and device owner. For each transition, label the actor: app, user, system, or administrator.
Questions to answer:
- Which state can the app request directly?
- Which requires leaving the app?
- Which exists only for one session?
- Which cannot be reached by a normal consumer install?
- What happens to a dependent rule after revocation?
5.7 The Interview Questions They Will Ask
- How are dangerous runtime permissions different from special app access?
- How do you detect revocation that occurs outside your activity?
- Why can a normal app not request device-owner status?
- How would you test a Settings-based consent journey?
- Why should a capability result contain evidence?
- What changes when target SDK increases?
5.8 Hints in Layers
Hint 1: Begin read-only
Render platform truth before adding any resolution action.
Hint 2: Separate descriptor from assessment
Product metadata is stable; current authority is observed.
Hint 3: Separate checker from journey
The checker never launches UI. The coordinator never invents truth.
Hint 4: Recheck, do not infer
Navigation back from Settings only means the user returned.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Immutable API state | Effective Java, 3rd Edition | Items 15-25 |
| Process lifetime | Operating Systems: Three Easy Pieces | Processes |
| Access control | Foundations of Information Security | Access control chapters |
| Boundary design | Clean Architecture | Component boundaries |
5.10 Implementation Phases
Phase 1: Read-Only Domain and Dashboard (3-4 hours)
Goals:
- Define descriptors, states, reasons, and evidence.
- Render fake assessments in Compose.
Tasks:
- Create seven or more stable capability descriptors.
- Build card, detail, empty, loading, and error states.
- Add fake registry scenarios for every state.
Checkpoint: A screenshot test renders every state without Android authority.
Phase 2: Platform Checkers and Journeys (4-6 hours)
Goals:
- Replace selected fakes with real read-only checks.
- Add one complete Settings journey.
Tasks:
- Implement bounded checkers behind ports.
- Verify resolution intent availability.
- Add rationale, system transition, and return recheck.
Checkpoint: Grant and revoke one capability externally; the same card updates correctly.
Phase 3: Lifecycle, Compatibility, and Polish (3-4 hours)
Goals:
- Survive recreation and handle unsupported paths.
- Make evidence accessible and testable.
Tasks:
- Add lifecycle refresh and duplicate-signal coalescing.
- Test old API and missing-handler fixtures.
- Add debug overrides, TalkBack semantics, and denial copy.
Checkpoint: The full matrix passes on two API levels and after process recreation.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| State shape | Boolean; nullable Boolean; sealed assessment | Sealed assessment | Preserves reason and resolution |
| Refresh | Cache forever; poll; lifecycle and explicit signals | Lifecycle plus explicit signals | Accurate without wasteful polling |
| Settings launch | Construct in domain; abstract resolution | Abstract resolution | Keeps Android objects at edge |
| Error behavior | Throw; mark denied; typed misconfigured/temporary | Typed state | Prevents misleading denial |
| Onboarding | Request all; progressive journey | Progressive | Matches user intent and least privilege |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Domain tests | Prove exhaustive mapping | Static unsupported, dynamic denial, transient disconnect |
| ViewModel tests | Prove state refresh | Resume signal replaces stale assessment |
| Compose tests | Prove understandable UI | Status text, rationale, disabled resolution |
| Instrumented tests | Prove Android checks | Settings handler, service declaration, owner mode |
| Compatibility tests | Prove API/OEM degradation | Missing intent handler, older API |
6.2 Critical Test Cases
- No launch requests: App launch causes zero permission or Settings prompts.
- Revocation: Available changes to Needs consent after external revocation and resume.
- Disconnected service: Enabled but disconnected maps to Temporarily unavailable.
- Missing manifest: Misconfigured wins over a misleading consent action.
- Unsupported API: Card explains minimum platform level.
- Policy mode: Normal install reports Policy-managed and no self-enrollment resolution.
- Process recreation: Registry reconstructs state; no stale in-memory grant survives.
- Missing handler: Manual instructions appear without crash.
- Accessibility semantics: Screen reader announces capability, status, evidence, and action.
6.3 Test Data
Scenario A
api_supported: true
manifest_declared: true
user_granted: false
expected: NEEDS_CONSENT / USER_GATE_DISABLED
Scenario B
api_supported: true
manifest_declared: true
user_granted: true
service_connected: false
expected: TEMPORARILY_UNAVAILABLE / SERVICE_DISCONNECTED
Scenario C
api_supported: true
owner_mode: NONE
expected: POLICY_MANAGED / ADMIN_PROVISIONING_REQUIRED
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Cached grant | Card remains Available after revoke | Recheck on lifecycle return |
| Boolean model | Unsupported appears as denied | Preserve typed reason |
| Journey in checker | Refresh unexpectedly launches Settings | Separate observation and resolution |
| Assumed handler | OEM crashes on intent launch | Resolve first and show fallback |
| UI-only registry | Worker uses different logic | Expose registry as headless domain service |
| Debug leakage | Release can fake authority | Build-time exclusion and release test |
7.2 Debugging Strategies
- Evidence first: Display reason code and checker timestamp in a debug panel.
- Compare layers: Inspect API support, manifest, user gate, and health separately.
- Use external mutation: Change grants in Settings while app is backgrounded.
- Test two APIs: Distinguish code defect from version behavior.
- Inspect manifest: Confirm the component and permission declaration actually merged.
7.3 Performance Traps
Avoid polling every capability continuously. Refresh on explicit events, foreground return, and bounded service callbacks. Run slow package or policy checks off the main thread, but preserve a single immutable screen update. Coalesce bursts of lifecycle signals.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add search and category filtering.
- Add a capability glossary inside each detail sheet.
- Add a copyable diagnostic reason without sensitive data.
8.2 Intermediate Extensions
- Add dependency previews showing which disabled rules need a capability.
- Record capability transition history with bounded retention.
- Add localized copy and screenshot tests for long text.
8.3 Advanced Extensions
- Generate a compatibility report across emulator API levels.
- Model consumer, enterprise, and developer distribution tracks.
- Add a policy fitness test that rejects any capability without revocation copy.
9. Real-World Connections
9.1 Industry Applications
- Automation platforms: Gate triggers and actions before authoring and execution.
- Password managers: Explain autofill, accessibility, and overlay authority distinctly.
- Enterprise agents: Separate normal install from profile/device-owner capabilities.
- VPN and capture tools: Represent session and foreground visibility accurately.
- Health and location apps: Use progressive permission scope and revocation handling.
9.2 Related Open Source Projects
- Android Settings samples: https://github.com/android/platform-samples — official platform behavior examples.
- Now in Android: https://github.com/android/nowinandroid — Compose architecture and state examples.
- Test DPC: https://github.com/googlesamples/android-testdpc — managed-device capability reference for later study.
9.3 Interview Relevance
- Permission architecture tests whether you understand the platform rather than only APIs.
- State modeling demonstrates product-safe handling of denial and revocation.
- Lifecycle refresh shows awareness of external mutation and process death.
- Capability boundaries reveal whether you distinguish consumer, privileged, and managed authority.
10. Resources
10.1 Essential Reading
- Permissions on Android: https://developer.android.com/guide/topics/permissions/overview — permission taxonomy and principles.
- Request special permissions: https://developer.android.com/training/permissions/requesting-special — Settings-mediated access.
- Activity Result APIs: https://developer.android.com/training/basics/intents/result — lifecycle-safe result handling.
- Processes and app lifecycle: https://developer.android.com/guide/components/activities/process-lifecycle — disposable-process model.
- Application fundamentals: https://developer.android.com/guide/components/fundamentals — components, sandbox, and manifest.
- Device policy controller: https://developer.android.com/work/dpc/build-dpc — why administrative authority is a separate track.
10.2 Video Resources
- Android Developers permission and privacy sessions: https://www.youtube.com/@AndroidDevelopers
- Android architecture learning pathway: https://developer.android.com/courses/pathways/android-architecture
10.3 Tools & Documentation
- ADB: https://developer.android.com/tools/adb — inspect device and package state.
- Compose testing: https://developer.android.com/develop/ui/compose/testing — semantic UI verification.
- Lifecycle: https://developer.android.com/topic/libraries/architecture/lifecycle — refresh and observation boundaries.
- App architecture: https://developer.android.com/topic/architecture — domain and platform separation.
10.4 Related Projects in This Series
- Next — Project 2: Context Signal Monitor: Uses capability health to decide whether a signal source is trustworthy.
- Project 5: Timekeeper Scheduler: Reuses exact-alarm assessment and degraded timing state.
- Projects 6-13: Add real high-authority adapters behind this control plane.
11. Self-Assessment Checklist
Before considering this project complete, verify:
11.1 Understanding
- I can distinguish platform support, authority, and health without notes.
- I can explain five different Android consent or provisioning mechanisms.
- I understand why returning from Settings does not imply success.
- I can explain why device owner is not a runtime permission.
- I can name the degraded behavior for every capability.
11.2 Implementation
- At least seven capability types render exhaustive states.
- Every assessment contains a stable reason and evidence.
- No capability is requested during launch.
- One complete journey includes rationale, launch, return recheck, denial, and revocation.
- UI and headless code share the same registry.
- Process recreation and unsupported-device tests pass.
- Debug overrides cannot enter the release build.
11.3 Growth
- I documented one assumption that a real device disproved.
- I can explain the project in a system-design interview.
- I identified one capability that should not ship in a consumer build.
- I wrote a short retrospective about consent copy and technical state.
12. Submission / Completion Criteria
Minimum Viable Completion:
- Seven capability descriptors and all required states are visible.
- At least three real read-only platform checkers work.
- One Settings journey rechecks denial and grant correctly.
- No launch-time permission request exists.
- Domain and UI tests cover every state.
Full Completion:
- All minimum criteria plus:
- Eight capability families include reason, evidence, degraded behavior, and revocation.
- Process recreation, missing-handler, unsupported API, and temporary health tests pass.
- TalkBack output and zero-grant onboarding are verified.
- The registry serves both Compose and a fake headless executor.
Excellence — Going Above & Beyond:
- Produce a two-API compatibility report with screenshots and state evidence.
- Add consumer, managed, and developer-track policy metadata.
- Add a build-time fitness test preventing undocumented capabilities.
- Demonstrate external revocation while the app is backgrounded with an immediate truthful refresh.
This guide was generated from ANDROID_AUTOMATION_APPS_MASTERY.md. For the complete learning path, see the parent guide and directory README.