在 Android 8.0 以上版本中,ART 工具介面 (ART TI) 會公開特定的執行階段內部資訊,並讓剖析器和偵錯工具影響應用程式的執行階段行為。這可用於實作最新的效能工具,這些工具可在其他平台上實作原生代理程式。
執行階段內部會向已載入至執行階段程序的代理程式公開。這些類別會透過直接呼叫和回呼與 ART 通訊。這個執行階段支援多個代理程式,因此可分離不同的正交剖析問題。代理程式可在執行階段開始時 (呼叫 dalvikvm
或 app_process
) 提供,也可以附加至已執行的程序。
由於檢測及修改應用程式和執行階段行為的功能非常強大,因此 ART TI 已整合兩項安全措施:
- 首先,公開代理程式介面的程式碼 (JVMTI) 會實作為執行階段外掛程式,而非執行階段的核心元件。外掛程式載入作業可能會受到限制,因此代理程式可能會遭到封鎖,無法找到任何介面點。
- 其次,
ActivityManager
類別和執行階段程序都只允許代理程式附加可偵錯的應用程式。可偵錯的應用程式已由開發人員簽署,可供分析和檢測,但不會發布給最終使用者。Google Play 商店不允許發布可偵錯的應用程式。這可確保一般應用程式 (包括核心元件) 無法進行檢測或操控。
設計
圖 1 顯示檢測應用程式中的一般流程和連結。

ART 外掛程式 libopenjdkjvmti
會公開 ART TI,這是為了滿足平台的需求和限制而設計:
- 類別重新定義功能是以
Dex
檔案為基礎,只包含單一類別定義,而非類別檔案。 - 不會公開用於檢測和重新定義的 Java 語言 API。
ART TI 也支援 Android Studio 分析器。
載入或附加代理程式
如要在執行階段啟動時附加代理程式,請使用以下指令載入 JVMTI 外掛程式和指定的代理程式:
dalvikvm -Xplugin:libopenjdkjvmti.so -agentpath:/path/to/agent/libagent.so …
在執行階段啟動時載入的代理程式沒有安全措施,因此請注意,手動啟動的執行階段允許完全修改,但沒有安全措施。(這可進行 ART 測試)。
注意:這項規定不適用於裝置上的一般應用程式 (包括系統伺服器)。應用程式會從已執行的 zygote 中分支,且 zygote 程序不允許載入代理程式。
如要將代理程式附加至已執行的應用程式,請使用以下指令:
adb shell cmd activity attach-agent [process] /path/to/agent/libagent.so[=agent-options]
如果尚未載入 JVMTI 外掛程式,附加代理程式會同時載入外掛程式和代理程式程式庫。
代理程式只能附加至標示為可偵錯的執行中應用程式 (應用程式資訊清單的一部分,應用程式節點上的屬性 android:debuggable
設為 true
)。ActivityManager
類別和 ART 都會先執行檢查,再允許附加代理程式。ActivityManager 類別會檢查目前的應用程式資訊 (源自 PackageManager 類別資料),以便判斷是否可進行偵錯,而執行階段會檢查目前的狀態,也就是在啟動應用程式時設定的狀態。
服務專員位置
執行階段需要將代理程式載入至目前的程序,以便代理程式可直接繫結至程序並與之通訊。ART 本身不瞭解代理程式來自何處。字串用於 dlopen
呼叫。檔案系統權限和 SELinux 政策會限制實際載入作業。
如要提供可由可偵錯應用程式執行的代理程式,請執行下列操作:
- 將代理程式嵌入應用程式 APK 的程式庫目錄中。
- 使用
run-as
將代理程式複製到應用程式的資料目錄中。
API
已將下列方法新增至 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 {
其他 Android API
attach-agent 指令會公開顯示。這個指令會將 JVMTI 代理程式附加至執行中的程序:
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;\''
am start -P
和 am
start-profiler/stop-profiler
指令與 attach-agent 指令類似。
JVMTI
此功能會將 JVMTI API 公開給代理程式 (原生程式碼)。重要的功能包括:
- 重新定義類別。
- 追蹤物件配置和垃圾收集。
- 依循物件的參照樹狀結構,逐一處理堆疊中的所有物件。
- 檢查 Java 呼叫堆疊。
- 暫停 (和恢復) 所有執行緒。
不同版本的 Android 可能提供不同的功能。
相容性
這項功能需要核心執行階段支援,但這項支援僅適用於 Android 8.0 以上版本。裝置製造商不需要進行任何變更即可實作這項功能。這是 AOSP 的一部分。
驗證
CTS 會在 Android 8 以上版本測試以下項目:
- 測試代理程式是否已連結至可偵錯的應用程式,以及是否無法連結至無法偵錯的應用程式。
- 測試所有已實作的 JVMTI API
- 測試代理程式二進位介面是否穩定
我們已在 Android 9 以上版本中新增其他測試,並納入這些版本的 CTS 測試。