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/
中的相關墓碑檔案,判斷這項行為是否導致當機。僅執行相關當機
包含下列中止訊息:
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 Socket。如要建立連線至 ServerSocket
的 TCP/UDP Socket,用戶端裝置必須知道伺服器的 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 5.1 (API 級別 22) 以下版本,搭載 Android 10 以上版本的裝置會在使用者首次執行該應用程式時發出警告。如果應用程式需要使用者授予權限,使用者也可以在首次執行應用程式前調整應用程式權限。
根據 Google Play 的目標 API 規定,使用者只會在執行近期未更新的應用程式時看到這些警告。透過其他商店發布的應用程式,也將在 2019 年開始適用類似的目標 API 級別規定。如要進一步瞭解這些規定,請參閱「2019 年擴大目標 API 級別規定」。
已移除 SHA-2 CBC 加密套件
平台已移除下列 SHA-2 CBC 密碼套件:
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
這些加密套件的安全性不如使用 GCM 的類似加密套件,而且大多數伺服器不是同時支援這些加密套件的 GCM 和 CBC 變體,就是兩者都不支援。
應用程式使用情況
Android 10 針對應用程式使用情形導入下列行為變更:
改善 UsageStats 應用程式使用情況 - <0x0此外,Android 10 可正確追蹤免安裝應用程式的使用情況。
個別應用程式灰階模式 - <
個別應用程式的干擾狀態 - <0x0A
暫停和播放 - <0x
HTTPS 連線變更
如果執行 Android 10 的應用程式將 null
傳遞至 setSSLSocketFactory()
,就會發生 IllegalArgumentException
。在先前版本中,將 null
傳遞至 setSSLSocketFactory()
的效果與傳入目前預設工廠的效果相同。
android.preference 程式庫已淘汰
Android 10 以上版本已淘汰 android.preference
程式庫。
開發人員應改用 AndroidX 偏好設定程式庫,這是 Android Jetpack 的一部分。如需其他有助於遷移及開發的資源,請參閱更新後的設定指南、公開範例應用程式和參考說明文件。
ZIP 檔案公用程式庫異動
Android 10 對 java.util.zip
套件中的類別進行了下列變更,這個套件會處理 ZIP 檔案。這些變更可讓程式庫在 Android 和使用 java.util.zip
的其他平台之間,行為更加一致。
充氣機
在舊版中,如果呼叫 end()
後叫用 Inflater
類別中的某些方法,這些方法會擲回 IllegalStateException
。在 Android 10 中,這些方法會改為擲回 NullPointerException
。
ZipFile
在 Android 10 以上版本中,如果提供的 ZIP 檔案不含任何檔案,採用 File
、int
和 Charset
型別引數的 ZipFile
建構函式不會擲回 ZipException
。
ZipOutputStream
在 Android 10 以上版本中,如果嘗試為不含任何檔案的 ZIP 檔案寫入輸出串流,ZipOutputStream
中的 finish()
方法不會擲回 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
以$
結尾,現在也會擲回相同的例外狀況。先前,這種情況不會擲回任何例外狀況。 - 如果
Matcher
擲回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
。 - 如果應用程式指定的 API 級別為 24 以上,則預設的
serialVersionUID
會與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。如需舊版行為,請使用原生程式碼。