針對非 SDK 介面的限制

自 Android 9 (API 級別 28) 起,平台會限制應用程式可使用的非 SDK 介面。每當應用程式參照非 SDK 介面,或嘗試透過反映或 JNI 取得其控制代碼時,就適用這些限制。這些限制旨在改善使用者和開發人員的體驗,減少使用者遇到當機問題的風險,並降低開發人員為此緊急發布更新的風險。如要進一步瞭解這項決定,請參閱「透過減少使用非 SDK 介面來提升穩定性」。

區分 SDK 和非 SDK 介面

一般而言,公用 SDK 介面是指列載於 Android 架構「套件索引」的介面。API 不會處理非 SDK 介面的實作細節,因此這些介面如有變更,恕不另行通知。

為避免出現當機問題和非預期行為,應用程式應只使用 SDK 中正式記錄的類別。這也表示您透過反映等機制與類別互動時,不應存取 SDK 中未列出的方法或欄位。

非 SDK API 清單

每個 Android 版本都會有額外的非 SDK 介面受到限制。我們瞭解這些限制可能會影響版本發布的作業流程,但我們也希望確保您擁有適當的工具來偵測非 SDK 介面的使用情形、有機會提供意見回饋,並且有時間規劃及適應新政策。

為盡量減少非 SDK 限制對開發工作流程的影響,非 SDK 介面會分成多份清單,並根據指定的 API 級別,定義其使用限制程度。下表逐一說明這些清單:

清單 程式碼代碼 說明
封鎖清單
  • blocked
  • 已淘汰:blacklist
無論應用程式的目標 API 級別為何,都無法使用的非 SDK 介面。如果應用程式試圖存取此類介面,系統會擲回錯誤
有條件封鎖
  • max-target-x
  • 已淘汰:greylist-max-x

自 Android 9 (API 級別 28) 起,應用程式指定的每個 API 級別皆各有受限制的非 SDK 介面。

在應用程式再也無法存取該清單中的非 SDK 介面之前,這些清單會依應用程式可指定的最高 API 級別 (max-target-x) 標示。例如,過去在 Android Pie 中未遭到封鎖,現在卻在 Android 10 中遭到封鎖的非 SDK 介面,是因為受列於 max-target-p (greylist-max-p) 清單中,其中「p」代表 Pie 或 Android 9 (API 級別 28)。

如果應用程式嘗試存取因目標 API 級別而受限制的介面,系統運作時會將該 API 視為封鎖清單的一部分

不支援
  • unsupported
  • 已淘汰:greylist
未受限且應用程式可使用的非 SDK 介面。不過請注意,這些介面不在支援範圍內,如有變更,恕不另行通知。請預期這些介面日後會在 max-target-x 清單中的 Android 版本遭到有條件封鎖。
SDK
  • public-apisdk
  • 已淘汰:public-apiwhitelist
可自由使用的介面,現已列入 Android 架構「套件索引」中正式記錄的項目並獲得支援。
測試 API
  • test-api
可供內部系統測試使用的介面,例如透過 Compatibility Test Suite (CTS) 進行測試的輔助 API。測試 API 並不隸屬於 SDK。自 Android 11 (API 級別 30) 起,測試 API 會列入封鎖清單中,因此無論應用程式的目標 API 級別為何,都不得使用這類 API。請注意,不論平台 API 級別為何,所有測試 API 皆無法獲得支援,且隨時可能變更,恕不另行通知。

儘管您目前可使用某些非 SDK 介面 (視應用程式的目標 API 級別而定),但使用任何非 SDK 方法或欄位時,應用程式停止運作的風險極高。如果應用程式仰賴非 SDK 介面,則應開始規劃遷移至 SDK 介面或其他替代方案。如果您除了為應用程式中的某個功能使用非 SDK 介面外,已別無他法,則應要求新的公用 API

判定介面所屬的清單

這個平台已內建非 SDK 介面清單。如要進一步瞭解每個 Android 版本,請參閱以下各節。

Android 15 (開發人員預覽版)

若為 Android 15,您可以下載以下檔案,瞭解所有非 SDK 介面和介面對應清單:

檔案:hiddenapi-flags.csv

SHA-256 核對和: 7aa0987aea4b25f5371b7e377c9f37375ada3b7e30465c0e2d910a5b646c10c1

如要進一步瞭解 Android 15 的非 SDK API 清單異動情形,請參閱「Android 15 的非 SDK 介面限制更新內容」。

Android 14

若為 Android 14 (API 級別 34),您可下載下列檔案,瞭解所有非 SDK 介面及其對應清單:

檔案:hiddenapi-flags.csv

SHA-256 核對和: 7e00db074cbe51c51ff4b411f7b48e98692951395c5c17d069c822cc1d0eae0f

如要進一步瞭解 Android 14 的非 SDK API 清單異動情形,請參閱「Android 14 的非 SDK 介面限制更新內容」。

Android 13

若為 Android 13 (API 級別 33),您可下載以下檔案,瞭解所有非 SDK 介面及其對應清單:

檔案:hiddenapi-flags.csv

SHA-256 核對和: 233a277aa8ac475b6df61bffd95665d86aac6eb2ad187b90bf42a98f5f2a11a3

如要進一步瞭解 Android 13 中的非 SDK API 清單變更情形,包括 Android 13 內遭到有條件封鎖的 API 建議公用 API 替代方案,請參閱「Android 13 的非 SDK 介面限制更新內容」。

Android 12

若為 Android 12 (API 級別 31),您可下載以下檔案,瞭解所有非 SDK 介面及其對應清單:

檔案:hiddenapi-flags.csv

SHA-256 核對和: 40674ff4291eb268f86561bf687e69dbd013df9ec9531a460404532a4ac9a761

如要進一步瞭解 Android 12 中非 SDK API 清單的變動情形,包括在 Android 12 中遭到有條件封鎖的 API 建議公用 API 替代方案,請參閱「Android 12 的清單變動情形」。

Android 11

對於 Android 11 (API 級別 30),您可下載下列檔案,其中說明所有非 SDK 介面及其對應清單:

檔案:hiddenapi-flags.csv

SHA-256 核對和: a19d839f4f61dc9c94960ae977b2e0f3eb30f880ba1ffe5108e790010b477a56

如要進一步瞭解 Android 11 的非 SDK API 清單異動情形,包括針對 Android 11 中遭有條件封鎖的 API 所建議採用的公用 API 替代方案,請參閱「Android 11 清單異動情形」。

Android 10

若為 Android 10 (API 級別 29),您可下載下列檔案,瞭解所有非 SDK 介面及其對應清單:

檔案:hiddenapi-flags.csv

SHA-256 核對和: f22a59c215e752777a114bd9b07b0b6b4aedfc8e49e6efca0f99681771c5bfeb

如要進一步瞭解 Android 10 的非 SDK API 清單異動情形,包括針對 Android 10 中遭有條件封鎖的 API 所建議採用的公用 API 替代方案,請參閱「Android 10 清單異動情形」。

Android 9

若為 Android 9 (API 級別 28),以下文字檔包含未受限制的非 SDK API 清單 (列入可疑項目清單):hiddenapi-light-greylist.txt

封鎖清單 (blacklist) 和有條件封鎖的 API 清單 (極可疑項目清單) 會在建構期間產生。

透過 Android 開放原始碼計畫產生清單

處理 Android 開放原始碼計畫時,您可以產生 hiddenapi-flags.csv 檔案,內含所有非 SDK 介面及其對應的清單。為此,請下載 Android 開放原始碼計畫來源,然後執行下列指令:

m out/soong/hiddenapi/hiddenapi-flags.csv

您可以在下列位置找到檔案:

out/soong/hiddenapi/hiddenapi-flags.csv

存取受限制非 SDK 介面時的預期行為

下表說明當應用程式試圖存取封鎖清單中的非 SDK 介面時,系統可能會出現的行為。

存取方式 結果
參照欄位的 Dalvik 指示 已擲回 NoSuchFieldError
參照方法的 Dalvik 指示 已擲回 NoSuchMethodError
使用 Class.getDeclaredField()Class.getField() 進行反映 已擲回 NoSuchFieldException
使用 Class.getDeclaredMethod()Class.getMethod() 進行反映 已擲回 NoSuchMethodException
使用 Class.getDeclaredFields()Class.getFields() 進行反映 非 SDK 成員不在結果中
使用 Class.getDeclaredMethods()Class.getMethods() 進行反映 非 SDK 成員不在結果中
使用 env->GetFieldID() 的 JNI 已回傳 NULL,已擲回 NoSuchFieldError
使用 env->GetMethodID() 的 JNI 已回傳 NULL,已擲回 NoSuchMethodError

測試應用程式的非 SDK 介面

有多種方法可用來測試應用程式中的非 SDK 介面。

使用可進行偵錯的應用程式進行測試

您可以在搭載 Android 9 (API 級別 28) 以上版本的裝置或模擬器上,建構並執行可偵錯的應用程式,藉此測試非 SDK 介面。請確認您使用的裝置或模擬器與應用程式的目標 API 級別相符。

在應用程式上執行測試時,如果應用程式存取特定非 SDK 介面,系統會顯示記錄訊息。您可以檢查應用程式的記錄訊息,查看下列詳細資料:

  • 宣告類別、名稱和類型 (採用 Android 執行階段所用的格式)。
  • 存取方式:透過連結、反映或 JNI。
  • 非 SDK 介面所屬的清單。

您可以使用 adb logcat 存取這些記錄訊息,這些訊息會顯示在執行中應用程式的 PID 下方。例如,記錄中的項目可能如下:

Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)

使用 StrictMode API 進行測試

您也可以使用 StrictMode API 測試非 SDK 介面。請使用 detectNonSdkApiUsage 方法啟用這項功能。啟用 StrictMode API 後,只要使用可實作自訂使用方式的 penaltyListener,就能接收每次使用非 SDK 介面時的回呼。回呼中提供的 Violation 物件繼承自 Throwable,而封閉式堆疊追蹤可提供使用情況的背景資訊。

使用 veridex 工具進行測試

您也可以在 APK 上執行 veridex 靜態分析工具。veridex 工具會掃描 APK 的整個程式碼集 (包括任何第三方程式庫),並回報偵測到的任何非 SDK 介面使用情形。

veridex 工具的限制包括:

  • 無法透過 JNI 偵測叫用。
  • 只能透過反映偵測叫用子集。
  • 對閒置程式碼路徑的分析僅限於 API 級別檢查。
  • 只能在支援 SSE4.2 和 POPCNT 指示的機器上執行。

Windows

不提供原生 Windows 二進位檔,但您可透過執行使用 Windows Subsystem for Linux (WSL) 的 Linux 二進位檔,在 Windows 上執行 veridex 工具。在依本節所述步驟操作前,請先安裝 WSL,並選擇 Ubuntu 做為 Linux 發行版。

安裝 Ubuntu 後,請啟動 Ubuntu 終端機,然後依照下列步驟操作:

  1. 從 Android 執行階段預建存放區下載 veridex 工具
  2. 擷取 appcompat.tar.gz 檔案的內容。
  3. 在擷取的資料夾中,找到 veridex-linux.zip 檔案並將其解壓縮。
  4. 前往解壓縮後的資料夾,然後執行下列指令,其中 your-app.apk 是您要測試的 APK:

    ./appcompat.sh --dex-file=your-app.apk
    

macOS

如要在 macOS 上執行 veridex 工具,請依照下列步驟操作:

  1. 從 Android 執行階段預建存放區下載 veridex 工具
  2. 擷取 appcompat.tar.gz 檔案的內容。
  3. 在擷取的資料夾中,找到 veridex-mac.zip 檔案並將其解壓縮。
  4. 前往解壓縮後的資料夾,然後執行下列指令,其中 /path-from-root/your-app.apk 是您要測試的 APK 路徑 (從系統根目錄開始):

    ./appcompat.sh --dex-file=/path-from-root/your-app.apk
    

Linux

如要在 Linux 上執行 veridex 工具,請依照下列步驟操作:

  1. 從 Android 執行階段預建存放區下載 veridex 工具
  2. 擷取 appcompat.tar.gz 檔案的內容。
  3. 在擷取的資料夾中,找到 veridex-linux.zip 檔案並將其解壓縮。
  4. 前往解壓縮後的資料夾,然後執行下列指令,其中 your-app.apk 是您要測試的 APK:

    ./appcompat.sh --dex-file=your-app.apk
    

使用 Android Studio Lint 工具進行測試

在 Android Studio 中建構應用程式時,Lint 工具會檢查程式碼是否有潛在問題。如果應用程式使用非 SDK 介面,則視這些介面所屬的清單而定,系統可能會顯示建構錯誤或警告。

您也可以從指令列執行 Lint 工具,或針對特定專案、資料夾或檔案以手動方式執行檢查

使用 Play 管理中心進行測試

當您將應用程式上傳至 Play 管理中心的測試群組時,系統會自動測試應用程式是否有潛在問題,並產生正式發布前測試報告。如果應用程式使用非 SDK 介面,視這些介面所屬的清單而定,正式發布前測試報告可能會顯示錯誤或警告。

詳情請參閱「透過正式發布前測試報告找出問題」中的「Android 相容性」一節。

要求新的公用 API

如果您除了為應用程式中的某個功能使用非 SDK 介面外,已別無他法,則可在 Issue Tracker 中建立功能要求,藉此要求新的公用 API。

建立功能要求時,請提供下列資訊:

  • 使用哪些不支援的 API,並寫出 Accessing hidden ... Logcat 訊息中的完整描述元。
  • 需要使用這類 API 的原因,包括需使用該 API 的高階功能詳細資料,而非只列入低階詳細資料。
  • 任何相關公用 SDK API 不敷使用的原因。
  • 先前嘗試過的其他替代方案,以及失敗的原因。

在功能要求中提供這些詳細資料,即可提高取得新公用 API 的機會。

其他問題

本節另外解答一些開發人員常見問題:

一般問題

Google 如何確保透過 Issue Tracker 掌握所有應用程式的需求?

我們會藉由使用下列方法附加的應用程式靜態分析,建立 Android 9 (API 級別 28) 的初始清單:

  • 手動測試熱門 Google Play 和非 Google Play 應用程式
  • 內部報告
  • 自動收集內部使用者的資料
  • 開發人員預覽報告
  • 旨在保守地納入更多偽陽性的其他靜態分析

在評估每個新版本的清單時,我們會透過 Issue Tracker,將帳戶 API 使用情形和開發人員意見回饋納入考量。

如何啟用非 SDK 介面的存取權?

您可以使用 ADB 指令變更 API 強制執行政策,在開發裝置上啟用非 SDK 介面的存取權。您使用的指令會因 API 級別而有所不同。這些指令不需要使用已解鎖裝置即可執行。

Android 10 (API 級別 29) 以上版本

如要啟用存取權,請使用下列 ADB

指令:

adb shell settings put global hidden_api_policy  1

如要將 API 強制執行政策重設為預設設定,請使用下列指令:

adb shell settings delete global hidden_api_policy
Android 9 (API 級別 28)

如要啟用存取權,請使用下列 ADB 指令:

adb shell settings put global hidden_api_policy_pre_p_apps  1
adb shell settings put global hidden_api_policy_p_apps 1

如要將 API 強制執行政策重設為預設設定,請使用下列指令:

adb shell settings delete global hidden_api_policy_pre_p_apps
adb shell settings delete global hidden_api_policy_p_apps

您可以將 API 強制執行政策中的整數設為下列其中一個值:

  • 0:停用所有非 SDK 介面的偵測作業。這項設定會停用所有非 SDK 介面使用情形的記錄訊息,您也將無法使用 StrictMode API 測試應用程式。我們不建議採用此設定。
  • 1:啟用所有非 SDK 介面的存取權,但若偵測到使用非 SDK 介面時,將會顯示記錄訊息並附上警告。這項設定也可讓您使用 StrictMode API 測試應用程式。
  • 2:禁止使用封鎖清單中的非 SDK 介面,或視目標 API 級別進行條件式封鎖。

非 SDK 介面清單相關問題

哪裡可以找到系統映像檔中的非 SDK API 清單?

您可以在平台 dex 檔案內的欄位和方法存取旗標位元中,找到這些經過編碼的清單。包含這些清單的系統映像檔中不具備獨立存在的檔案。

在 Android 版本相同、但原始設備製造商 (OEM) 不同的裝置上,是否有同樣的非 SDK API 清單?

原始設備製造商 (OEM) 可將自己的介面新增至封鎖清單 (黑名單),但無法從 Android 開放原始碼計畫的非 SDK API 清單中移除介面。CDD 不允許此類變更,且會透過 CTS 測試,確保 Android 執行階段強制執行清單。

原生程式碼中的非 NDK 介面是否有任何相關限制?

Android SDK 包含 Java 介面。平台已開始在 Android 7 (API 級別 26) 中,限制使用原生 C/C++ 程式碼存取非 NDK 介面。詳情請參閱「在 Android N 中使用私人 C/C++ 符號限制以提升穩定性」。

有辦法限制 dex2oat 或 DEX 檔案操控嗎?

我們目前沒有限制 dex2oat 二進位檔存取權的計畫,但不打算讓 DEX 成為固定的檔案格式,且除了在「Dalvik 可執行格式」中公開指定的部分之外,我們也無意讓其成為公用介面。我們保留隨時修改或移除 dex2oat 和 DEX 格式未指定部分的權利。另請注意,dex2oat 產生的衍生檔案,例如 ODEX (又稱 OAT)、VDEX 和 CDEX,都是未指定的格式。

如果重要的第三方 SDK (例如模糊處理工具) 無法避免使用非 SDK 介面,但會保證與日後的 Android 版本的相容性,此時該怎麼辦?在此情況下,Android 可以免除其相容性需求嗎?

我們不打算根據個別 SDK 免除相容性需求。如果 SDK 開發人員只能仰賴不支援項目 (先前稱為「可疑項目」) 清單中的介面維持相容性,則應開始規劃遷移至 SDK 介面或其他替代方案,如果除了使用非 SDK 介面外已別無他法,則應申請新的公用 API

非 SDK 介面限制是否適用於所有應用程式,包括系統和第一方應用程式,而非僅限於第三方應用程式?

是,但使用平台金鑰簽署的應用程式,以及部分系統映像檔應用程式不在此限。請注意,只有屬於系統映像檔的應用程式 (或更新後的系統映像檔應用程式) 不受限制。此清單僅適用於以私人平台 API (而非 SDK API) 建構的應用程式 (其中 LOCAL_PRIVATE_PLATFORM_APIS := true)。