行為變更:所有應用程式

Android 10 包含可能會影響應用程式的行為變更。無論應用程式的 targetSdkVersion 為何,本頁所列的變更皆適用於在 Android 10 上執行的應用程式。您應測試應用程式,並視需要修改,才能正確支援這些變更。

如果應用程式的 targetSdkVersion 為 29 以上版本,您還需要支援其他變更。詳情請參閱針對指定 29 為目標版本的應用程式行為變更

注意: 除了本頁列出的變更外,Android 10 也導入了大量以隱私權為基礎的變更和限制,包括:

  • 裝置位置資訊的背景存取權
  • 開始背景活動
  • 聯絡人興趣相似目標對象資訊
  • MAC 位址隨機化
  • 相機中繼資料
  • 權限模型

這些異動會影響所有應用程式,並強化使用者隱私。如要進一步瞭解如何支援這些變更,請參閱隱私權變更頁面。

非 SDK 介面限制

為了確保應用程式穩定性和相容性,平台已開始限制應用程式可在 Android 9 (API 級別 28) 中使用的非 SDK 介面。基於與 Android 開發人員合作及最新的內部測試,Android 10 包含最新的受限制非 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 位元變化版本,並替換為 lib64/lib/

為提高相容性,系統會在舊路徑中提供符號連結。例如,/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 感知資料路徑建立 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_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 導入了與應用程式使用情況相關的下列行為變更:

  • 改善使用統計資料 - 使用 Android 應用程式時的追蹤功能或應用程式使用情形 (使用 Android 應用程式時)。此外,Android 10 也會正確追蹤免安裝應用程式使用情形。

  • 個別應用程式灰階 - 每款應用程式可以設定一個灰階模式。

  • 個別應用程式分心狀態 - 能夠為應用程式顯示建議狀態,而不是顯示建議應用程式。」

  • 停權與播放 -

HTTPS 連線變更

如果執行 Android 10 的應用程式將 null 傳遞至 setSSLSocketFactory(),就會發生 IllegalArgumentException。在先前版本中,將 null 傳遞至 setSSLSocketFactory() 的效果與傳入目前預設工廠的效果相同。

android.preference 程式庫已淘汰

android.preference 程式庫已於 Android 10 淘汰。開發人員應改用 Android Jetpack 的 AndroidX 偏好設定程式庫。如需其他有助於遷移和開發的資源,請參閱新版設定指南,以及我們的公開範例應用程式參考說明文件

ZIP 檔案公用程式庫異動

Android 10 針對處理 ZIP 檔案的 java.util.zip 套件中的類別推出了下列變更。這些變更會讓 Android 和其他使用 java.util.zip 的平台之間,讓程式庫的行為更一致。

充氣機

在先前版本中,如果 Inflater 類別中的某些方法在呼叫 end() 後叫用,請擲回 IllegalStateException。在 Android 10 中,這些方法會改為擲回 NullPointerException

ZIP 檔案

在 Android 10 以上版本中,如果提供的 ZIP 檔案不含任何檔案,ZipFile 的建構函式 (採用 FileintCharset 類型引數) 不會擲回 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() 行為變更

如果輸入值為 0,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 結尾為 $,系統會擲回相同的例外狀況。先前,本情境不會擲回例外狀況。
  • replaceFirst(null)如果 Matcher 擲回 NullPointerException,則不會再呼叫 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 級別 23 以下版本中序列化,目標 24 或以上的應用程式就能正確讀取該物件,反之亦然。

java.io.FileChannel.map() 變更

自 Android 10 起,FileChannel.map() 不支援非標準檔案 (例如 /dev/zero),因為這類檔案的大小不能使用 truncate() 變更。先前的 Android 版本會接受 truncate() 傳回的 errno,但 Android 10 會擲回 IOException。如果需要舊行為,則必須使用原生程式碼。