Google is committed to advancing racial equity for Black communities. See how.

ART TI

In Android 8.0 and higher, the ART Tooling Interface (ART TI) exposes certain runtime internals, and enables profilers and debuggers to influence the runtime behavior of apps. This can be used to implement state-of-the-art performance tools that are provided for implementing native agents on other platforms.

Runtime internals are exposed to agents that have been loaded into the runtime process. These communicate with the ART through direct calls and callbacks. The runtime supports multiple agents so that different orthogonal-profiling concerns can be separated. Agents may be either supplied at runtime start (when dalvikvm or app_process are invoked), or attached to an already-running process.

Because the ability to instrument and modify app and runtime behavior is very powerful, two safety measures have been integrated into the ART TI:

  • First, the code exposing the agent interface, JVMTI, is implemented as a runtime plugin, not a core component of the runtime. Plugin loading may be restricted, so that agents can be blocked from finding any of the interface points.
  • Second, both the ActivityManager class and the runtime process only allow agents to attach to debuggable applications. Debuggable applications have been signed-off by their developers to be analyzed and instrumented, and aren’t distributed to end users. The Google Play store doesn’t allow the distribution of debuggable apps. This ensures normal applications (including core components) can’t be instrumented or manipulated.

The design

The general flow and interconnection in an instrumented app is shown in Figure 1.

Flow and interconnection in an instrumented app
Figure 1. Flow and interconnection of an instrumented app

The ART plugin libopenjdkjvmti exposes the ART TI, which is designed to accommodate the needs and constraints of the platform:

  • Class redefinition is based on Dex files, containing only a single class definition, instead of class files.
  • Java-language APIs for instrumentation and redefinition aren’t exposed.

The ART TI also supports Android Studio profilers.

Loading or attaching an agent

To attach an agent at runtime startup, use this command to load both the JVMTI plugin and the given agent:

dalvikvm -Xplugin:libopenjdkjvmti.so -agentpath:/path/to/agent/libagent.so …

There aren’t safety measures in place when an agent is loaded at runtime startup, so keep in mind that a manually started runtime allows full modification without safety measures. (This allows ART testing.)

Note: This is not applicable to normal applications (including the system server) on a device. Applications are forked from an already-running zygote, and a zygote process isn’t allowed to load agents.

To attach an agent to an app that’s already running, use this command:

adb shell cmd activity attach-agent [process]
/path/to/agent/libagent.so[=agent-options]

If the JVMTI plugin has not been loaded yet, attaching an agent loads both the plugin and the agent library.

An agent may only be attached to a running application that’s marked as debuggable (part of the application’s manifest, with attribute android:debuggable set to true on the application node). Both the ActivityManager class and the ART perform checks before allowing an agent to be attached. The ActivityManager class checks the current application information (derived from the PackageManager class data) for the debuggable status, and the runtime checks its current status, which was set when the app was started.

Agent locations

The runtime needs to load agents into the current process, so that the agent can directly bind to and communicate with it. The ART itself is agnostic regarding the specific location from which the agent comes. The string is used for a dlopen call. File system permissions and SELinux policies restrict the actual loading.

To deliver agents that can be run by a debuggable app, do the following:

  • Embed the agent in the library directory of the app’s APK.
  • Use run-as to copy the agent into the app’s data directory.

APIs

The following method was added to android.os.Debug.

/**
     * Attach a library as a jvmti agent to the current runtime, with the given classloader
     * determining the library search path.
     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
     * throw a SecurityException.
     *
     * @param library the library containing the agent.
     * @param options the options passed to the agent.
     * @param classLoader the classloader determining the library search path.
     *
     * @throws IOException if the agent could not be attached.
     * @throws a SecurityException if the app is not debuggable.
     */
    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
            @Nullable ClassLoader classLoader) throws IOException {

Other Android APIs

The attach-agent command is publicly visible. This command attaches a JVMTI agent to a running process:

adb shell 'am attach-agent com.example.android.displayingbitmaps
\'/data/data/com.example.android.displayingbitmaps/code_cache/libfieldnulls.so=Ljava/lang/Class;.name:Ljava/lang/String;\''

The am start -P and am start-profiler/stop-profiler commands are similar to the attach-agent command.

JVMTI

This feature exposes the JVMTI API to agents (native code). The important capabilities include:

  • Redefining a class.
  • Tracking object allocation and garbage collection.
  • Iterating over all objects in a heap, following the reference tree of objects.
  • Inspecting Java call stacks.
  • Suspending (and resuming) all threads.

Different capabilities may be available on different versions of Android.

Compatibility

This feature needs core runtime support that’s only available on Android 8.0 and higher. Device manufacturers don’t need to make any changes to implement this feature. It’s part of AOSP.

Validation

CTS tests the following on Android 8 and higher:

  • Tests that agents attach to debuggable apps, and fail to attach to non-debuggable apps.
  • Tests all implemented JVMTI APIs
  • Tests that the binary interface for agents is stable

Additional tests have been added to Android 9 and higher, and are included in the CTS tests for those releases.