Find your way around AOSP

Read the source tree without getting lost in it.

10 min readChapter 10 of 50Guided platform development
In this chapter

AOSP is not one gigantic Git repository. It is a workspace containing many Git projects, coordinated by Repo. This lesson uses the Android 14 tag android-14.0.0_r1 to practice orientation and source navigation without editing anything. You do not need a checkout or account: the first practice is a complete browser-only Gitiles investigation. Search results are clues: a matching filename or string alone does not prove that you found the code running on a device.

Before you start#

No checkout is required for this chapter’s first route. The optional terminal exercise is only for learners who already have a synced checkout; if you do not, complete the Gitiles worksheet and move to the next chapter’s host/setup gate.

Learning goals#

  • Explain why Repo status and Git status answer different questions.
  • Map a Repo project name to its checkout path.
  • Trace a symbol from a search candidate to its definition, callers, build module, and nearby tests.
  • Distinguish a command name, the process it starts, source code, tests, and generated outputs.

Practice#

Workspace, projects, and status

One workspace, many projects
AOSP workspace
Repo coordinates projects and revisions across the checkout.
.repo/ manifest metadata
Project names map to checkout paths and selected revisions.
frameworks/base and system/core
Distinct Git project histories, working trees, indices, and commits.
Conceptual model. Representative paths, not a complete manifest. The manifest defines project boundaries; indentation alone does not. repo status and git status have different scopes.

A workspace is the directory containing the checkout. Its .repo/ metadata contains the manifest: XML describing which Git projects Repo obtains and where each is placed. A folder is not necessarily a Git repository; for example, one nested app directory can be its own project while its parent is not. The manifest, not visual indentation, defines project boundaries.

In a local checkout, first make sure you are not inside somebody else’s work:

bash
pwd
ls -a | head
repo status
repo list | head

repo status summarizes projects in this workspace (Repo command reference: it compares each project's working tree, index, and HEAD). git status --short reports only the Git project containing the current directory. For example, from frameworks/base, Git does not report an unrelated edit in system/core. Do not stage, reset, clean, or switch branches in this orientation exercise.

To inspect a project’s local history:

bash
cd frameworks/base
git status --short
git log -5 --oneline
repo info

Repo handles workspace-wide operations; Git handles a project’s history and working tree. A cross-project patch usually means separate project changes and review/commit handling.

Search, then establish ownership

From search hit to evidence
  1. Definition and callers

    Identify the behavior and the conditions under which callers reach it.

  2. Build ownership

    Read the complete Android.bp module and dependencies; names may differ from directories.

  3. Relevant test

    Find its assertion, module, runner, and target prerequisites.

  4. Runtime identity, if observed

    Match process and image to source before assigning a live symptom to that code.

Conceptual model. These are investigation steps, not runtime calls. Each link needs surrounding, version-matched source; a search result is only a candidate.

Translate a report into a specific, observable behavior before searching: action, expected result, actual result, device/build, and reproduction steps. From the checkout root, a text search might be:

bash
rg -n --glob '*.java' --glob '*.kt' 'About phone' packages/apps/Settings

The result may be a translation, test, dead path, or UI label. Open the file, identify the containing function, search for callers, and inspect the nearby Android.bp and tests. A module is a named build unit; its name need not match its directory. Generated code is an output, not usually the edit location: look for the Android.bp generator rule and the source schema/interface/template that feeds it. For example, an AIDL interface generates communication code; edit and test the interface/implementation as appropriate, not an ephemeral generated file.

Keep these terms separate:

TermWhat it isEvidence to seek
CommandA program name or shell instruction used to start somethingShell script, init service declaration, or host tool
ProcessA running instance with a PID and runtime identityadb shell ps -A, logs, process/command line
ImplementationSource defining behaviorDefinition and call sites on the selected tag
TestCode that asserts behaviorTest source plus build module/runner declaration
Generated outputFile produced by a build ruleGenerator rule and its checked-in inputs

The process name can differ from the command name or source directory. Runtime evidence (such as a PID/command in a log) narrows ownership; source call chains and build definitions then explain it. Do not infer runtime ownership from a grep hit alone.

Browser-only investigation: Android init

Open these exact Gitiles pages in a browser. Gitiles shows the pinned project revision and breadcrumb path; no Repo account, source sync, shell, or local files are needed. Keep this tag fixed throughout the exercise:

  1. Manifest → project path. Open the Android 14 r1 manifest. Use the browser’s find command (Ctrl+F / Cmd+F) for path="system/core". Record the manifest project name and path. The manifest entry is the source fact that maps the checkout path system/core to a Repo project; do not infer it from the URL alone.
  2. Project path → caller. Open system/core/init/main.cpp. Use Ctrl+F for SecondStageMain. Read the enclosing conditional and call expression: this file calls the second-stage entry function when argv[1] is second_stage. Record the caller file and condition in your own words.
  3. Caller → definition. Open system/core/init/init.cpp. Find int SecondStageMain. Read the function body and Ctrl+F LoadBootScripts; record the definition file and one concrete operation in that function. A call in main.cpp and a definition in init.cpp are separate source facts.
  4. Definition → build ownership. Open system/core/init/Android.bp. Find init_device_sources, init.cpp, name: "libinit", name: "init_second_stage", and main.cpp. Follow the surrounding blocks: init.cpp is included in the init library sources; the executable module has main.cpp and depends on libinit. Record both the executable module and library relationship, not just the nearest name string.
  5. Build file → actual test. In that same Android.bp, find name: "CtsInitTestCases" and its srcs list. Open rlimit_parser_test.cpp, Ctrl+F TEST(rlimit, RlimitSuccess), then inspect its input table. Record the test module, test case, and method as separate values. This test covers the rlimit parser; it is evidence of a declared test in the same project, not a test of SecondStageMain itself.

Use the Gitiles breadcrumb and file path at every step. If Ctrl+F finds nothing, verify that the page URL still contains refs/tags/android-14.0.0_r1, then inspect the file path in the breadcrumb; do not silently switch to main, master, or another release. Search results and neighboring names can help you navigate, but the surrounding source is what establishes whether a hit is a definition, call, build declaration, or test.

For configuration-driven services, follow the init service declaration to its executable, then inspect that executable’s module and implementation. For framework behavior, the owner may instead be an app process or system_server. Use adb shell ps -A and timestamped logs on a matching running build to verify a runtime hypothesis; a source search alone cannot prove which process produced the symptom.

Optional local checkout exercise

This section is optional and does not block the browser route. It assumes a checkout created later using Build and Run AOSP. From its root, inspect local state before using Repo/Git commands:

bash
cd ~/src/aosp-android14 && repo status
cd ~/src/aosp-android14 && repo list | rg 'platform/(frameworks/base|system/core)'
cd ~/src/aosp-android14 && rg -n 'SecondStageMain|LoadBootScripts' system/core/init

Compare the local paths and source facts with your browser worksheet. These commands only inspect. Do not edit, stage, reset, clean, or switch branches as part of this exercise.

Investigation worksheet

Write source fact only when the tagged page itself supports it. Write unknown when a source-only browser cannot establish the claim; do not fill a gap with a guess.

QuestionYour recorded answerEvidence type
Which manifest project maps to checkout path system/core?Source fact: manifest entry
Which file calls SecondStageMain, under what condition?Source fact: caller and surrounding code
Which file defines SecondStageMain? Which function calls LoadBootScripts?Source fact: definition/body
Which executable module contains main.cpp, and which library provides init.cpp?Source fact: complete Android.bp blocks/dependency
What are the test module, GTest case, and test method?Source fact: Android.bp plus test declaration
Did this browser-only route prove which process produced a live device’s symptom?Unknown unless independently observed on a matching running image.Runtime claim not available from source browsing
Did a test pass or did an Android image boot?Unknown; no test/build/device was run by this exercise.Execution claim not available from Gitiles

Knowledge check#

  1. Why can a clean git status in frameworks/base coexist with an edit in system/core?
  2. Does finding a symbol definition prove it runs when a reported symptom occurs?
  3. Where do you look when a matching file is generated?
  4. Why inspect Android.bp instead of assuming the directory is the module name?

Answers#

  1. Git reports one project. Run repo status at the workspace root for a multi-project summary.
  2. No. Establish the process/runtime path, call sites, build ownership, and tests; a definition may be unused or only exercised in tests.
  3. Trace the generator rule to its checked-in input (for example, an AIDL or proto definition) and edit the source of truth, not a disposable output.
  4. Build modules have explicit names and can aggregate sources from multiple directories; filenames and paths are not reliable runner targets.

Browser worksheet key

  • Manifest: checkout path system/core, Repo project platform/system/core.
  • Caller: init/main.cpp; main() calls SecondStageMain(argc, argv) when argv[1] is second_stage.
  • Definition: init/init.cpp; SecondStageMain calls LoadBootScripts(am, sm).
  • Build: init_second_stage compiles main.cpp and links libinit; init.cpp is included in libinit through init_device_sources.
  • Test: module CtsInitTestCases; GTest case rlimit; method RlimitSuccess.
  • Unknown from browser source alone: whether a particular device symptom was caused by this code, whether the test passes in a given environment, and whether an image builds or boots.

Checkpoint#

Source-only pass: complete the worksheet with the tagged manifest project mapping, caller, definition, build modules, and test identifiers, and label the live-process/test/build outcomes unknown. This is a meaningful completed outcome and requires no checkout. Local pass: if you later complete the optional exercise, also compare the local paths and search results with the worksheet.

If stuck: reopen each exact r1 Gitiles URL, verify the breadcrumb/tag, and search within the file named in the route. Consult terminal and Git only for the optional local commands and process foundations for the source-versus-runtime distinction. Do not change source to make a search result fit your hypothesis.

Sources#

Next: Build and Run AOSP.

A little further beneath the surface.

Check your understanding with the checkpoint. Marking a chapter as read saves a bookmark, not a proficiency assessment.