Android 9 (API 級別 28) 導入了 Android 系統的一些變更。當應用程式在 Android 9 平台上執行時,無論指定 API 級別為何,下列行為變更都會套用至該應用程式。所有開發人員都應檢查這些變更,並修改應用程式,確保應用程式適當支援 (如適用)。
如需只影響目標 API 級別 28 以上應用程式的變更,請參閱「行為變更:指定 API 級別 28 以上的應用程式」。
電源管理
Android 9 推出了新功能,可提升裝置電源管理成效。這些變更和 Android 9 之前的版本已提供,協助確保將系統資源提供給最需要的應用程式。
詳情請參閱「電源管理」。
隱私權異動
為了強化使用者隱私,Android 9 推出了幾項行為變更,例如限制背景應用程式存取裝置感應器、限制從 Wi-Fi 掃描擷取的資訊,以及與通話、手機狀態和 Wi-Fi 掃描相關的新權限規則與權限群組。
無論目標 SDK 版本為何,這些變更都會影響在 Android 9 上執行的所有應用程式。
在背景存取感應器受限
Android 9 會限制背景應用程式存取使用者輸入內容和感應器資料的功能。如果應用程式在搭載 Android 9 的裝置背景中執行,系統將對應用程式套用以下限制:
如果應用程式需要在搭載 Android 9 的裝置上偵測感應器事件,請使用前景服務。
限制存取通話記錄
Android 9 導入 CALL_LOG
權限群組,並將 READ_CALL_LOG
、WRITE_CALL_LOG
和 PROCESS_OUTGOING_CALLS
權限移至這個群組。在先前的 Android 版本中,這些權限位於 PHONE
權限群組。
這個 CALL_LOG
權限群組可讓使用者針對需要存取通話機密資訊的應用程式 (例如讀取電話通話記錄和辨識電話號碼) 提供更完善的控管和瀏覽權限。
如果您的應用程式需要存取通話記錄,或需要處理撥出電話,您必須從 CALL_LOG
權限群組明確要求這些權限。否則,系統會發生 SecurityException
。
注意: 由於這些權限已變更群組,且會在執行階段授予,因此使用者可以拒絕您的應用程式存取電話通話記錄資訊。在這種情況下,您的應用程式應可妥善處理缺少資訊存取權的情況。
如果應用程式已經遵循執行階段權限最佳做法,即可處理權限群組的變更。
限制存取電話號碼
在 Android 9 上執行的應用程式,除了必須先取得 READ_CALL_LOG
權限,也無法讀取電話號碼或手機狀態,以及應用程式用途所需的其他權限。
與來電和撥出電話相關聯的電話號碼會顯示在手機狀態廣播中 (例如來電和撥出電話),而且可從 PhoneStateListener
類別存取。不過,如果沒有 READ_CALL_LOG
權限,在 PHONE_STATE_CHANGED
廣播和透過 PhoneStateListener
提供的電話號碼欄位會是空白的。
如要從手機狀態讀取電話號碼,請根據用途更新應用程式,並要求必要的權限:
- 如要從
PHONE_STATE
意圖動作讀取數字,您必須同時具備READ_CALL_LOG
權限和READ_PHONE_STATE
權限。 - 如要讀取
onCallStateChanged()
中的數字,您僅需要READ_CALL_LOG
權限。您不需要READ_PHONE_STATE
權限。
限制存取 Wi-Fi 的位置資訊和連線資訊
在 Android 9 中,應用程式執行 Wi-Fi 掃描的權限要求比先前版本嚴格。詳情請參閱「Wi-Fi 掃描限制」。
其他類似限制也適用於 getConnectionInfo()
方法,此方法會傳回描述目前 Wi-Fi 連線的 WifiInfo
物件。只有在呼叫應用程式具備下列權限時,您才能使用這個物件的方法擷取 SSID 和 BSSID 值:
- ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
- ACCESS_WIFI_STATE
您也必須在裝置上啟用定位服務 (依序前往「設定」>「位置」),才能擷取 SSID 或 BSSID。
從 Wi-Fi 服務方法移除的資訊
在 Android 9 中,下列事件和廣播不會接收有關使用者位置或個人識別資訊的資訊:
WifiManager
的getScanResults()
和getConnectionInfo()
方法。WifiP2pManager
的discoverServices()
和addServiceRequest()
方法。NETWORK_STATE_CHANGED_ACTION
廣播。
從 Wi-Fi 發出的 NETWORK_STATE_CHANGED_ACTION
系統廣播訊息不再包含 SSID (原為 EXTRA_SSID)、BSSID (原為 EXTRA_BSSID) 或連線資訊 (原為 EXTRA_NETWORK_INFO)。如果您的應用程式需要這項資訊,請改為呼叫 getConnectionInfo()
。
電話資訊現在會以裝置位置資訊設定做為依據
如果使用者已在搭載 Android 9 的裝置上停用裝置定位功能,下列方法就不會提供結果:
使用非 SDK 介面的相關限制
為確保應用程式穩定性和相容性,平台會限制使用部分非 SDK 方法和欄位;無論您嘗試透過反射或 JNI 直接存取這些方法和欄位,皆適用這些限制。在 Android 9 中,您的應用程式可以繼續存取這些受限制的介面;平台會使用浮動式訊息和記錄項目來特別注意。如果您的應用程式顯示這類浮動式訊息,請務必採用受限制介面以外的實作策略。如果您認為沒有可行的替代策略,可以提交錯誤,要求重新審查限制。
非 SDK 介面的限制一文提供更詳盡的重要資訊。建議您進行檢查,確保應用程式能繼續正常運作。
安全性行為變更
裝置安全性變更
無論應用程式的目標版本為何,Android 9 都新增了幾項功能,可提升應用程式的安全性。
TLS 實作變更
系統的 TLS 實作在 Android 9 中進行了幾項變更:
- 如果
SSLSocket
執行個體在建立期間無法連線,系統會擲回IOException
,而非NullPointerException
。 SSLEngine
類別會妥善處理任何發生的close_notify
快訊。
如要進一步瞭解如何在 Android 應用程式中提出安全網路要求,請參閱 HTTPS 範例。
更嚴格的 SECCOMP 篩選器
Android 9 進一步限制應用程式可用的系統呼叫。這個行為是 Android 8.0 (API 級別 26) 包含的 SECCOMP 篩選器擴充功能。
加密編譯變更
Android 9 導入有關加密編譯演算法的實作及處理方式。
混用參數和演算法
Android 9 在 Conscrypt 中提供了其他演算法參數實作。這些參數包括:AES、DESEDE、OAEP 和 EC。這些參數的 Bouncy Castle 版本已於 Android 9 淘汰,且許多演算法已淘汰。
如果應用程式指定 Android 8.1 (API 級別 27) 以下版本,則當您要求這類已淘汰演算法的 Bouncy Castle 實作時,您會收到警告訊息。不過,如果指定 Android 9 為目標,這些要求都會擲回 NoSuchAlgorithmException
。
其他異動
Android 9 推出了與密碼編譯相關的其他幾項變更:
- 使用 PBE 金鑰時,如果 Bouncy Castle 預期會使用初始化向量 (IV),但應用程式未提供此金鑰,您就會收到警告訊息。
- ARC4 加密的 Conscrypt 實作可讓您指定 ARC4/ECB/NoPadding 或 ARC4/NONE/NoPadding。
- 已移除 Crypto Java Cryptography Architecture (JCA) 提供者。因此,如果應用程式呼叫
SecureRandom.getInstance("SHA1PRNG", "Crypto")
,就會發生NoSuchProviderException
。 - 如果應用程式將 RSA 金鑰從大於金鑰結構的緩衝區剖析,就不會再發生例外狀況。
如要進一步瞭解如何使用 Android 的加密功能,請參閱「密碼編譯」。
我們不再支援 Android 安全加密檔案
Android 9 完全停止支援 Android 安全加密檔案 (ASEC)。
在 Android 2.2 (API 級別 8) 中,Android 導入了 ASEC 以支援應用程式對 SD-card 的功能。在 Android 6.0 (API 級別 23) 中,該平台推出了「可採用的儲存裝置」技術,可讓開發人員用於取代 ASEC。
ICU 程式庫更新
Android 9 使用 60 版的 ICU 程式庫。Android 8.0 (API 級別 26) 和 Android 8.1 (API 級別 27) 使用 ICU 58。
ICU 用於在 android.icu package
下方提供公用 API,並在 Android 平台內部用於提供國際化支援。例如,在 java.util
、java.text
和 android.text.format
中實作 Android 類別。
ICU 60 的更新版本包含許多小但實用的變更,包括表情符號 5.0 資料支援,以及改善的日期/時間格式,如 ICU 59 和 ICU 60 版本資訊所述。
本次更新的主要變更:
- 平台處理時區的方式已改變。
- YouTube 平台能有效處理 GMT 和 UTC,但不再是 GMT 的同義詞。
ICU 現在提供 GMT 和 UTC 區域名稱的翻譯。這項變更會影響「GMT」、「Etc/GMT」、「UTC」、「Etc/UTC」和「祖魯」等區域的
android.icu
格式及剖析行為。 java.text.SimpleDateFormat
現在會使用 ICU 提供世界標準時間 /格林威治標準時間的顯示名稱,這表示:- 格式化
zzzz
會產生多個語言代碼的長本地化字串。過去是在世界標準時間 (UTC) 產生「UTC」,以及「GMT+00:00」做為 GMT 字串。 - 剖析
zzzz
可辨識「世界座標時間」和「格林威治標準時間」等字串。 - 如果應用程式假設所有語言的
zzzz
輸出為「UTC」或「GMT+00:00」,就可能會發生相容性問題。
- 格式化
java.text.DateFormatSymbols.getZoneStrings()
的行為已變更:- 和
SimpleDateFormat
一樣,世界標準時間和格林威治標準時間現在有全名。世界標準時間時區的 DST 時區變體 (例如「UTC」、「Etc/UTC」和「Zulu」) 會變成 GMT+00:00,也就是在沒有可用的名稱時,也就是標準的備用字串UTC
。 - 某些區域 ID 可正確辨識為其他區域的同義詞,因此 Android 會找出先前無法解析的舊區域 ID 字串,例如
Eire
。
- 和
- Asia/Hanoi 已不再是認可的可用區。因此,
java.util.TimeZones.getAvailableIds()
不會傳回這個值,且java.util.TimeZone.getTimeZone()
無法辨識。這個行為與現有的android.icu
行為一致。
- YouTube 平台能有效處理 GMT 和 UTC,但不再是 GMT 的同義詞。
- 即使剖析合法的貨幣文字,
android.icu.text.NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String)
方法也可能會擲回ParseException
。如要避免這個問題,請使用 Android 7.0 (API 級別 24) 以上版本適用的NumberFormat.parseCurrency
,用於PLURALCURRENCYSTYLE
樣式的貨幣文字。
Android 測試變更
Android 9 變更了 Android Test 架構的程式庫和類別結構。這些變更可協助開發人員使用架構支援的公用 API,但這些變更也讓使用第三方程式庫或自訂邏輯更靈活地建構及執行測試。
已從架構中移除資料庫
Android 9 將 JUnit 類別重新整理成三個程式庫:android.test.base、android.test.runner 和 android.test.mock。這項變更可讓您針對最適合專案依附元件的 JUnit 版本執行測試。這個版本的 JUnit 可能與 android.jar
提供的版本不同。
如要進一步瞭解如何將 JUnit 類別整理成這些程式庫,以及為應用程式專案撰寫及執行測試的準備工作,請參閱「設定 Android Test 的專案」一文。
測試套件版本變更
TestSuiteBuilder
類別中的 addRequirements()
方法已移除,且 TestSuiteBuilder
類別本身已淘汰。addRequirements()
方法要求開發人員提供類型為隱藏 API 的引數,導致 API 無效。
Java UTF 解碼器
UTF-8 是 Android 中的預設字元集。UTF-8 位元組序列可由 String
建構函式 (例如 String(byte[] bytes)
) 解碼。
Android 9 的 UTF-8 解碼器比先前版本更嚴格遵循萬國碼 (Unicode) 標準。異動內容包括:
- 系統會將非最短形式的 UTF-8 (例如
<C0, AF>
) 視為格式錯誤。 - 系統會將 UTF-8 等代理格式 (例如
U+D800
..U+DFFF
) 視為格式錯誤。 - 最大子部分會由單一
U+FFFD
取代。例如,在位元組序列「41 C0 AF 41 F4 80 80 41
」中,最大子部分為「C0
」、「AF
」和「F4 80 80
」。「F4 80 80
」可以是「F4 80 80 80
」的初始序列,但「C0
」不能是任何格式正確程式碼單元序列的初始子序列。因此,輸出內容應為「A\ufffd\ufffdA\ufffdA
」。 - 如要在 Android 9 以上版本中解碼修改後的 UTF-8 / CESU-8 序列,請使用
DataInputStream.readUTF()
方法或NewStringUTF()
JNI 方法。
使用憑證驗證主機名稱
RFC 2818 說明將網域名稱與憑證比對的兩種方法:使用 subjectAltName
(SAN
) 副檔名中的可用名稱,或在沒有 SAN
副檔名的情況下,改回使用 commonName
(CN
)。
不過,CN
的備用方案已在 RFC 2818 中淘汰。因此,Android 不再改回使用 CN
。如要驗證主機名稱,伺服器必須提供具有相符 SAN
的憑證。如果憑證不含與主機名稱相符的 SAN
,就會不再受信任。
網路位址查詢可能會導致網路違反
需要名稱解析的網路位址查詢可能會涉及網路 I/O,因此被視為封鎖作業。主要執行緒上的封鎖作業可能會導致暫停執行或卡頓。
StrictMode
類別是一項開發工具,可協助開發人員偵測程式碼中的問題。
在 Android 9 以上版本中,StrictMode
可偵測網路位址查詢作業造成的網路違規事項。
請勿發布已啟用 StrictMode
的應用程式。如果加入,您的應用程式可能會在使用 detectNetwork()
或 detectAll()
方法取得會偵測網路違規的政策時發生例外狀況,例如 NetworkOnMainThreadException
。
解決數字 IP 位址並非封鎖作業。數字 IP 位址解析功能的運作方式與 Android 9 之前的版本相同。
通訊端標記
在 Android 9 以下版本的平台中,如果通訊端使用 setThreadStatsTag()
方法加上標記,當通訊端透過具有 ParcelFileDescriptor
容器的繫結器 IPC 傳送至另一個程序時,系統就會取消標記。
在 Android 9 以上版本中,當通訊端標記透過繫結器 IPC 傳送至其他程序時,系統會保留通訊端標記。這項變更可能會影響網路流量統計資料,例如使用 queryDetailsForUidTag()
方法時。
如要保留先前版本的行為,將傳送至其他程序的通訊端取消標記,您可以在傳送通訊端之前呼叫 untagSocket()
。
回報通訊端的可用位元組數
叫用 shutdownInput()
方法後呼叫 available()
方法時,會傳回 0
。
更詳細的 VPN 網路功能報告
在 Android 8.1 (API 級別 27) 以下版本中,NetworkCapabilities
類別僅回報了 VPN 這類的少數資訊 (例如 TRANSPORT_VPN
),但會省略 NET_CAPABILITY_NOT_VPN
。因為這類有限資訊,就很難判斷使用 VPN 是否會向應用程式使用者收費。舉例來說,檢查 NET_CAPABILITY_NOT_METERED
無法判定基礎網路是否為計量付費。
在 Android 9 以上版本中,當 VPN 呼叫 setUnderlyingNetworks()
方法時,Android 系統會合併任何基礎網路的傳輸和功能,並將結果傳回為 VPN 網路的有效網路功能。
在 Android 9 以上版本中,已檢查 NET_CAPABILITY_NOT_METERED
的應用程式會收到 VPN 和基礎網路的網路功能。
應用程式無法再使用 xt_qtaGui 資料夾中的檔案
從 Android 9 開始,應用程式無法直接讀取 /proc/net/xt_qtaguid
資料夾中的檔案。這是因為部分沒有這些檔案的裝置能保持一致。
需要使用這些檔案的公用 API (TrafficStats
和 NetworkStatsManager
) 將繼續正常運作。不過,不支援的 cutils 函式 (例如 qtaguid_tagSocket()
) 可能無法在不同裝置上正常運作。
現已強制執行 FLAG_ACTIVITY_NEW_TASK 規定
在 Android 9 中,您必須傳遞意圖旗標 FLAG_ACTIVITY_NEW_TASK
,才能從非活動結構定義啟動活動。如果您嘗試在未傳遞此旗標的情況下啟動活動,則活動不會啟動,系統也會在記錄中顯示訊息。
畫面旋轉變更
從 Android 9 開始,直向旋轉模式有重大變更。在 Android 8.0 (API 級別 26) 中,使用者可以透過快速設定方塊或顯示設定,在自動旋轉和直向旋轉模式之間切換。肖像模式已重新命名為旋轉鎖定,且在自動旋轉功能關閉時啟用。自動旋轉模式沒有任何變更。
當裝置處於旋轉鎖定模式時,使用者可以鎖定螢幕,使用頂端可見活動支援的任何旋轉功能。就不應假設 Activity 一律以直向顯示。如果頂端活動可在自動旋轉模式下以多個旋轉方式算繪,則應在旋轉鎖定模式下提供相同的選項,但根據活動的 screenOrientation
設定可能會有些許例外 (請參閱下表)。
要求特定螢幕方向的活動 (例如 screenOrientation=landscape
) 會忽略使用者鎖定偏好設定,且運作方式與 Android 8.0 中相同。
螢幕方向偏好設定可在 Android 資訊清單中的活動層級設定,或使用 setRequestedOrientation() 以程式輔助方式設定。
旋轉鎖定模式的運作方式是設定 WindowsManager 在處理活動旋轉時採用的使用者旋轉偏好設定。在下列情況中,使用者旋轉偏好設定可能會改變。請注意,裝置的自然旋轉方式 (通常是手機板型規格裝置通常是直向) 會出現偏誤:
- 使用者接受旋轉建議後,旋轉偏好設定就會變更。
- 使用者切換至強制直向應用程式 (包括螢幕鎖定或啟動器) 時,旋轉偏好設定會變更為直向。
下表匯總了一般螢幕方向的旋轉行為:
螢幕方向 | 行為 |
---|---|
未指定,使用者 | 在自動旋轉和旋轉鎖定中,活動會以直向或橫向 (以及反向) 呈現。請支援直向和橫向版面配置。 |
使用者景觀 | 在自動旋轉和旋轉鎖定時,活動會以橫向或反向橫向呈現。系統僅支援橫向版面配置。 |
使用者肖像 | 在自動旋轉和旋轉鎖定中,活動會以直向或反向直向呈現。僅支援直向版面配置。 |
完整使用者 | 在自動旋轉和旋轉鎖定中,活動會以直向或橫向 (以及反向) 呈現。預期會同時支援直向和橫向版面配置。 旋轉鎖定使用者可選擇鎖定反向直向的選項 (通常是 180o)。 |
感應器, 全感應器, 感應器肖像, 感應器 | 旋轉鎖定模式的偏好設定會被忽略,並視為自動旋轉功能啟用中。因此只有在特殊情況下,請務必謹慎考量使用者體驗。 |
淘汰 Apache HTTP 用戶端會影響含有非標準 ClassLoader 的應用程式
在 Android 6.0 中,我們已停止支援 Apache HTTP 用戶端。這項變更對於大多數未指定 Android 9 以上版本的應用程式都沒有影響。不過,即使應用程式並非指定 Android 9 以上版本,這項變更也會影響使用非標準 ClassLoader
結構的特定應用程式。
如果應用程式使用會明確委派給系統 ClassLoader
的非標準 ClassLoader
,就會受到影響。在 org.apache.http.*
中搜尋類別時,這些應用程式必須改為委派給應用程式
ClassLoader
。如果委派給系統 ClassLoader
,應用程式在 Android 9 以上版本會透過 NoClassDefFoundError
執行失敗,因為系統 ClassLoader
不再知道這些類別。為避免日後發生類似問題,應用程式通常應透過 ClassLoader
應用程式載入類別,而非直接存取系統 ClassLoader
。
列舉攝影機
在 Android 9 裝置上執行的應用程式可以呼叫 getCameraIdList()
,藉此探索所有可用的相機。應用程式不應假設裝置只有一個後置鏡頭或只有一個前置鏡頭。
舉例來說,如果應用程式提供切換前置與後置鏡頭的按鈕,則可能有多個前置或後置鏡頭可供選擇。您應該瀏覽攝影機清單、檢查每部攝影機的特性,並決定要向使用者公開哪些攝影機。