Android 10 包含可能影響應用程式的行為變更。無論應用程式的 targetSdkVersion 為何,當應用程式在 Android 10 上執行時,本頁列出的變更都會套用至應用程式。您應測試應用程式,並視需要修改,以便妥善支援這些變更。
如果應用程式的 targetSdkVersion 為 29 以上版本,您也需要支援其他變更。請務必詳閱指定 29 的應用程式行為變更。
注意: 除了本頁列出的變更外,Android 10 還推出許多以隱私權為基礎的變更和限制,包括:
- 在背景存取裝置位置資訊
- 背景活動啟動
- 聯絡人親和度資訊
- MAC 位址隨機化
- 攝影機中繼資料
- 權限模型
這些異動會影響所有應用程式,並強化使用者隱私。如要進一步瞭解如何支援這些變更,請參閱「隱私權異動」頁面。
非 SDK 介面限制
為確保應用程式穩定性和相容性,平台已開始在 Android 9 (API 級別 28) 中,限制應用程式可使用的非 SDK 介面。Android 10 根據與 Android 開發人員合作及最新的內部測試,提供最新的受限制非 SDK 介面清單。我們的目標是確保在限制非 SDK 介面之前,有公開替代方案可用。
如果您的應用程式並非以 Android 10 (API 級別 29) 為目標版本,則此處所述的某些變更可能不會立即對您造成影響。雖然您目前可以使用某些非 SDK 介面 (視應用程式的目標 API 級別而定),但使用任何非 SDK 方法或欄位時,均可能面臨應用程式故障的高度風險。
如果不確定應用程式是否使用非 SDK 介面,可測試應用程式來確認。如果您的應用程式仰賴非 SDK 介面,則建議您開始規劃遷移至 SDK 替代方案。不過,我們瞭解有些應用程式有使用非 SDK 介面的正當理由。如果您除了為應用程式中的某個功能使用非 SDK 介面外,已別無他法,則應要求新的公用 API。
詳情請參閱「Android 10 的非 SDK 介面限制更新」和「非 SDK 介面的限制」。
手勢操作
自 Android 10 起,使用者可以在整部裝置上啟用手勢操作。如果使用者啟用手勢操作,這會影響裝置上的所有應用程式,無論應用程式是否指定目標 API 級別 29。舉例來說,如果使用者從螢幕邊緣向內滑動,系統會將該手勢解讀為返回操作,除非應用程式明確覆寫該手勢,用於螢幕的某些部分。
如要讓應用程式與手勢操作功能相容,請將應用程式內容延伸至無邊框,並妥善處理衝突的手勢。詳情請參閱手勢操作說明文件。
NDK
Android 10 包含下列 NDK 異動。
共用物件不得包含文字重新定位
Android 6.0 (API 級別 23) 禁止使用共用物件中的文字再定位。程式碼必須依原樣載入,不得修改。這項異動可縮短應用程式載入時間,並提升安全性。
SELinux 會對指定 Android 10 以上版本的應用程式強制執行這項限制。如果這些應用程式繼續使用含有文字重新定位的共用物件,則很有可能發生故障。
Bionic 程式庫和動態連結器路徑異動
從 Android 10 開始,有幾個路徑是符號連結,而非一般檔案。如果應用程式依賴路徑做為一般檔案,可能會中斷:
/system/lib/libc.so->/apex/com.android.runtime/lib/bionic/libc.so/system/lib/libm.so->/apex/com.android.runtime/lib/bionic/libm.so/system/lib/libdl.so->/apex/com.android.runtime/lib/bionic/libdl.so/system/bin/linker->/apex/com.android.runtime/bin/linker
這些變更也適用於檔案的 64 位元變化版本,其中 lib/ 會替換為 lib64/。
為確保相容性,系統會在舊路徑提供符號連結。例如,/system/lib/libc.so 是 /apex/com.android.runtime/lib/bionic/libc.so 的符號連結。因此,dlopen(“/system/lib/libc.so”) 會繼續運作,但應用程式在實際嘗試透過讀取 /proc/self/maps 或類似方式檢查載入的程式庫時,會發現差異,這並不常見,但我們發現有些應用程式會將此做為防駭程序的一部分。如果是,則應將 /apex/… 路徑新增為 Bionic 檔案的有效路徑。
系統二進位檔/程式庫對應至僅限執行的記憶體
自 Android 10 起,系統二進位檔和程式庫的可執行區隔會對應至記憶體,並設為僅限執行 (不可讀取),做為防範程式碼重複使用攻擊的強化技術。如果應用程式對標示為僅限執行的記憶體區段執行讀取作業 (無論是因錯誤、安全漏洞或刻意檢查記憶體),系統都會向應用程式傳送 SIGSEGV 信號。
您可以檢查 /data/tombstones/ 中的相關 Tombstone 檔案,判斷這項行為是否導致當機。與僅限執行相關的當機包含下列中止訊息:
Cause: execute-only (no-read) memory access error; likely due to data in .text.
如要解決這個問題,以便執行記憶體檢查工具等作業,可以呼叫 mprotect(),將僅限執行的區段標示為讀取 + 執行。不過,我們強烈建議您在完成後將其設回僅限執行,因為這項存取權限設定可為應用程式和使用者提供更完善的保護。
安全性
Android 10 包含下列安全性異動。
預設啟用 TLS 1.3
在 Android 10 以上版本中,所有 TLS 連線均預設啟用 TLS 1.3。以下是有關實作 TLS 1.3 的幾項重要細節:
- TLS 1.3 加密套件不開放自訂。啟用 TLS 1.3 時,一律會啟用支援的 TLS 1.3 加密套件。若嘗試呼叫
setEnabledCipherSuites()來停用這些功能,系統會予以忽略。 - 協商 TLS 1.3 時,系統會先呼叫
HandshakeCompletedListener物件,再將工作階段加入工作階段快取。(在 TLS 1.2 和其他舊版本中,這些物件是在工作階段新增至工作階段快取「後」才呼叫。) - 在某些情況下,如果
SSLEngine例項在舊版 Android 上擲回SSLHandshakeException,這些例項會在 Android 10 以上版本擲回SSLProtocolException。 - 不支援 0-RTT 模式。
如有需要,您可以呼叫 SSLContext.getInstance("TLSv1.2"),取得已停用 TLS 1.3 的 SSLContext。您也可以對適當物件呼叫 setEnabledProtocols(),根據個別連線啟用或停用通訊協定版本。
使用 SHA-1 簽署的憑證不受 TLS 信任
在 Android 10 中,TLS 連線不會信任使用 SHA-1 雜湊演算法的憑證。根 CA 自 2016 年起就未曾核發這類憑證,Chrome 或其他主要瀏覽器也不再信任這類憑證。
如果連線目標為使用 SHA-1 憑證的網站,則任何連線嘗試都會失敗。
KeyChain 行為變更和強化措施
Google Chrome 等部分瀏覽器會在 TLS 握手期間,趁著 TLS 伺服器傳送憑證要求訊息時,允許使用者選擇憑證。在 Android 10,KeyChain 物件會在呼叫 KeyChain.choosePrivateKeyAlias() 來向使用者顯示憑證選擇提示時,遵循頒發單位和金鑰規格參數。具體來說,此提示含有的選項皆符合伺服器規格。
如果沒有可供使用者選取的憑證,例如在憑證不符合伺服器規格,或裝置未安裝任何憑證的情況下,則系統根本不會顯示憑證選取提示。
此外,Android 10 以上版本不必設定裝置螢幕鎖定,即可將金鑰或 CA 憑證匯入 KeyChain 物件。
其他 TLS 和密碼學變更
TLS 和密碼學程式庫有幾項小幅變更,會在 Android 10 生效:
- AES/GCM/NoPadding 和 ChaCha20/Poly1305/NoPadding 加密演算法會從
getOutputSize()傳回更準確的緩衝區空間。 - 如果嘗試連線時使用的最高通訊協定版本為 TLS 1.2 以上版本,系統會省略
TLS_FALLBACK_SCSV加密套件。TLS 伺服器實作程序已經過提升,因此不建議嘗試採用 TLS 外部備用機制,而應採用 TLS 版本協商。 - ChaCha20-Poly1305 是 ChaCha20/Poly1305/NoPadding 的別名。
- 以點結尾的主機名稱並非有效的 SNI 主機名稱。
- 選擇憑證回應的簽署金鑰時,系統會依循
CertificateRequest中的 supported_signature_algorithms 擴充功能。 - 在 TLS 中,Android KeyStore 金鑰等不透明的簽署金鑰可以與 RSA-PSS 簽署搭配使用。
Wi-Fi Direct 廣播
在 Android 10 上,下列與 Wi-Fi Direct 相關的廣播並非黏性廣播:
如果應用程式在註冊時依賴接收這些廣播,因為它們是黏性廣播,請在初始化時使用適當的 get() 方法來取得資訊。
Wi-Fi Aware 功能
Android 10 新增支援功能,可簡化使用 Wi-Fi Aware 資料路徑建立 TCP/UDP 通訊端的程序。如要建立連至ServerSocket的 TCP/UDP 通訊端,用戶端裝置必須知道伺服器的 IPv6 位址和通訊埠。這項資訊先前必須透過頻帶外通訊 (例如使用 BT 或 Wi-Fi Aware 第 2 層訊息) 傳達,或使用其他通訊協定 (例如 mDNS) 透過頻帶內通訊探索。在 Android 10 中,這項資訊可在網路設定過程中傳達。
伺服器可以執行下列任一操作:
- 初始化
ServerSocket,並設定或取得要使用的通訊埠。 - 在 Wi-Fi Aware 網路要求中指定通訊埠資訊。
下列程式碼範例說明如何在網路要求中指定通訊埠資訊:
Kotlin
val ss = ServerSocket() val ns = WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("some-password") .setPort(ss.localPort) .build() val myNetworkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(ns) .build()
Java
ServerSocket ss = new ServerSocket(); WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier .Builder(discoverySession, peerHandle) .setPskPassphrase(“some-password”) .setPort(ss.getLocalPort()) .build(); NetworkRequest myNetworkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(ns) .build();
接著,用戶端會執行 Wi-Fi Aware 網路要求,取得伺服器提供的 IPv6 和通訊埠:
Kotlin
val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { ... } override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) { ... } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { ... val ti = networkCapabilities.transportInfo if (ti is WifiAwareNetworkInfo) { val peerAddress = ti.peerIpv6Addr val peerPort = ti.port } } override fun onLost(network: Network) { ... } }; connMgr.requestNetwork(networkRequest, callback)
Java
callback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { ... } @Override public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { ... } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { ... TransportInfo ti = networkCapabilities.getTransportInfo(); if (ti instanceof WifiAwareNetworkInfo) { WifiAwareNetworkInfo info = (WifiAwareNetworkInfo) ti; Inet6Address peerAddress = info.getPeerIpv6Addr(); int peerPort = info.getPort(); } } @Override public void onLost(Network network) { ... } }; connMgr.requestNetwork(networkRequest, callback);
在 Go 裝置上操作 SYSTEM_ALERT_WINDOW
在 Android 10 (Go 版本) 裝置上執行的應用程式無法接收 SYSTEM_ALERT_WINDOW 權限。這是因為繪製疊加視窗會耗用過多記憶體,對記憶體不足的 Android 裝置效能特別有害。
如果搭載 Android 9 以下版本的 Go 版本裝置上執行的應用程式收到 SYSTEM_ALERT_WINDOW 權限,即使裝置升級至 Android 10,應用程式仍會保留該權限。不過,如果應用程式尚未取得該權限,則在裝置升級後無法取得該權限。
如果 Go 裝置上的應用程式傳送含有 ACTION_MANAGE_OVERLAY_PERMISSION 動作的意圖,系統會自動拒絕要求,並將使用者導向「設定」畫面,說明系統不允許該權限,因為這會降低裝置速度。如果 Go 裝置上的應用程式呼叫 Settings.canDrawOverlays(),該方法一律會傳回 false。再次提醒,如果應用程式在裝置升級至 Android 10 之前已取得 SYSTEM_ALERT_WINDOW 權限,則不受這些限制影響。
針對指定舊版 Android 的應用程式顯示警告
搭載 Android 10 以上版本的裝置,會在使用者首次執行任何指定 Android 5.1 (API 級別 22) 以下版本的應用程式時,向使用者發出警告。如果應用程式需要使用者授予權限,使用者也可以在應用程式首次執行前調整應用程式的權限。
根據 Google Play 的目標 API 規定,只有在使用者執行近期未更新的應用程式時,才會看到這些警告。對於透過其他商店發行的應用程式,類似的目標 API 規定將於 2019 年生效。如要進一步瞭解這些規定,請參閱「2019 年擴大目標 API 級別規定」。
移除 SHA-2 CBC 加密套件
下列 SHA-2 CBC 加密套件已從平台移除:
TLS_RSA_WITH_AES_128_CBC_SHA256TLS_RSA_WITH_AES_256_CBC_SHA256TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
這些加密套件的安全性低於使用 GCM 的類似加密套件,且大多數伺服器都支援這些加密套件的 GCM 和 CBC 變體,或兩者都不支援。
應用程式使用情況
Android 10 針對應用程式使用情形,推出下列行為變更:
UsageStats 應用程式用量改善 - 當應用程式在分割畫面或子母畫面模式下使用時,Android 10 會使用 UsageStats 準確追蹤應用程式用量。此外,Android 10 可正確追蹤免安裝應用程式的使用情形。
個別應用程式灰階 - Android 10 可為個別應用程式設定灰階顯示模式。
個別應用程式干擾狀態 - Android 10 可選擇將應用程式設為「干擾狀態」,在這種狀態下,系統會隱藏應用程式的通知,且不會將其顯示為建議應用程式。
暫停和播放 - 在 Android 10 中,暫停的應用程式無法播放音訊。
HTTPS 連線變更
如果執行 Android 10 的應用程式將 null 傳遞至 setSSLSocketFactory(),就會發生 IllegalArgumentException。在先前版本中,將 null 傳遞至 setSSLSocketFactory() 的效果與傳入目前預設工廠的效果相同。
android.preference 程式庫已淘汰
android.preference 程式庫自 Android 10 起已淘汰。開發人員應改用 AndroidX 偏好設定程式庫,這是 Android Jetpack 的一部分。如需其他資源協助遷移和開發,請參閱更新後的設定指南、公開範例應用程式和參考文件。
ZIP 檔案公用程式庫異動
Android 10 對 java.util.zip 套件中的類別進行了下列變更,該套件會處理 ZIP 檔案。這些異動可讓程式庫在 Android 和其他使用 java.util.zip 的平台上的行為更加一致。
Inflater
在先前的版本中,如果在呼叫 end() 後叫用 Inflater 類別中的某些方法,會擲回 IllegalStateException。在 Android 10 中,這些方法會改為擲回 NullPointerException。
ZipFile
在 Android 10 以上版本中,如果提供的 ZIP 檔案不含任何檔案,則採用 File、int 和 Charset 類型引數的 ZipFile 建構函式不會擲回 ZipException。
ZipOutputStream
在 Android 10 以上版本中,如果 ZipOutputStream 中的 finish() 方法嘗試為不含任何檔案的 ZIP 檔案寫入輸出串流,則不會擲回 ZipException。
攝影機變更
許多使用相機的應用程式會假設,如果裝置處於直向設定,則實體裝置也處於直向,如「相機方向」一節所述。過去,這項假設是安全的,但隨著可用的板型規格 (例如折疊式裝置) 擴充,情況已有所改變。在這些裝置上,這項假設可能會導致相機觀景窗顯示不正確的旋轉或縮放 (或兩者皆有)。
如果應用程式指定的目標 API 級別為 24 以上,應明確設定 android:resizeableActivity,並提供必要功能來處理多視窗模式。
電池用量追蹤
從 Android 10 開始,每當裝置在重大充電事件後拔除電源,SystemHealthManager 就會重設電池用量統計資料。一般來說,重大充電事件是指:裝置已充飽電,或裝置從電量幾乎耗盡到電量幾乎充飽。
在 Android 10 之前,只要裝置拔除電源,電池用量統計資料就會重設,無論電池電量變化有多小。
Android Beam 淘汰
在 Android 10 中,我們正式淘汰 Android Beam,這是一項舊功能,可透過近距離無線通訊 (NFC) 啟動跨裝置資料共用。我們也將淘汰多個相關的 NFC API。Android Beam 仍可供想使用的裝置製造商合作夥伴選用,但已不再積極開發。不過,Android 會繼續支援其他 NFC 功能和 API,而讀取標記和付款等用途也會繼續正常運作。
java.math.BigDecimal.stripTrailingZeros() 行為變更
如果輸入值為零,BigDecimal.stripTrailingZeros() 不會再將結尾的零視為特殊情況保留。
java.util.regex.Matcher 和 Pattern 行為變更
split() 的結果已變更,當輸入內容開頭有零寬度相符項目時,不再以空白 String ("") 開頭。這也會影響 String.split()。舉例來說,"x".split("") 現在會傳回 {"x"},但在舊版 Android 上則會傳回 {"", "x"}。"aardvark".split("(?=a)" 現在會傳回 {"a", "ardv", "ark"},而非 {"", "a", "ardv", "ark"}。
無效引數的例外狀況行為也已改善:
- 如果替換
String的結尾是單一反斜線 (不合法),appendReplacement(StringBuffer, String)現在會擲回IllegalArgumentException,而不是IndexOutOfBoundsException。如果替換的String以$結尾,現在也會擲回相同的例外狀況。先前,系統在這種情況下不會擲回例外狀況。 - 如果 擲回
NullPointerException,replaceFirst(null)不會再對Matcher呼叫reset()。現在如果沒有相符項目,也會擲回NullPointerException。先前,只有在找到相符項目時,系統才會擲回這個例外狀況。 - 如果群組索引超出範圍,
start(int group)、end(int group)和group(int group)現在會擲回更通用的IndexOutOfBoundsException。先前,這些方法會擲回ArrayIndexOutOfBoundsException。
GradientDrawable 的預設角度現在是 TOP_BOTTOM
在 Android 10 中,如果您在 XML 中定義 GradientDrawable,但未提供角度測量值,漸層方向會預設為 TOP_BOTTOM。這與先前的 Android 版本不同,先前的預設值為 LEFT_RIGHT。
如要解決這個問題,請更新至最新版 AAPT2,這樣一來,如果未指定角度測量值,工具就會為舊版應用程式設定 0 的角度測量值。
使用預設 SUID 記錄序列化物件
自 Android 7.0 (API 級別 24) 起,平台修正了可序列化物件的預設 serialVersionUID。這項修正不會影響指定 API 級別 23 以下版本的應用程式。
自 Android 10 起,如果應用程式指定 API 級別 23 以下版本,且依賴舊版、不正確的預設 serialVersionUID,系統會記錄警告並建議修正程式碼。
具體來說,如果符合下列所有條件,系統就會記錄警告:
- 應用程式指定 API 級別 23 以下版本。
- 類別已序列化。
- 序列化類別使用預設
serialVersionUID,而非明確設定serialVersionUID。 - 預設的
serialVersionUID與應用程式指定 API 級別 24 以上時的serialVersionUID不同。
系統會針對每個受影響的類別記錄一次這項警告。
警告訊息會提供建議的修正方式,也就是將 serialVersionUID 明確設為預設值,如果應用程式指定目標 API 級別 24 以上,系統就會計算這個值。使用該修正程式,即可確保如果該類別的物件在指定目標 API 級別 23 以下的應用程式上序列化,則指定目標 API 級別 24 以上版本的應用程式會正確讀取該物件,反之亦然。
java.io.FileChannel.map() 變更
自 Android 10 起,FileChannel.map() 不支援非標準檔案 (例如 /dev/zero),其大小無法使用 truncate() 變更。舊版 Android 會吞下 truncate() 傳回的 errno,但 Android 10 會擲回 IOException。如需舊版行為,請使用原生程式碼。