Android 9 (API 級別 28) 對 Android 系統做出了許多變更。無論指定的 API 級別為何,當所有應用程式在 Android 9 平台上執行時,下列行為變更都會套用至這些應用程式。所有開發人員都應查看這些變更,並視情況修改應用程式以便正確支援這些變更。
如要瞭解僅影響以 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()
方法,該方法會傳回 WifiInfo
物件,用於說明目前的 Wi-Fi 連線。只有在呼叫應用程式具備下列權限時,才能使用這個物件的各項方法來擷取 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 實作變更
在 Android 9 中,系統的 TLS 實作方式經歷了幾項變更:
- 如果
SSLSocket
的例項在建立時無法連線,系統會擲回IOException
,而非NullPointerException
。 SSLEngine
類別會妥善處理發生的任何close_notify
警報。
如要進一步瞭解如何在 Android 應用程式中提出安全的網頁要求,請參閱「HTTPS 範例」。
更嚴格的 SECCOMP 篩選器
Android 9 進一步限制應用程式可使用的系統呼叫。這項行為是 Android 8.0 (API 級別 26) 所包含的 SECCOMP 篩選器的擴充功能。
密碼學變更
Android 9 針對加密編譯演算法的實作和處理方式,推出了幾項變更。
參數和演算法的 Conscrypt 實作
Android 9 在 Conscrypt 中提供額外的演算法參數實作。這些參數包括 AES、DESEDE、OAEP 和 EC。自 Android 9 起,這些參數和許多演算法的 Bouncy Castle 版本已淘汰。
如果應用程式指定 Android 8.1 (API 級別 27) 以下版本,您會在要求 Bouncy Castle 實作其中一個已淘汰的演算法時收到警告。不過,如果您指定 Android 9,這些要求會各自擲回 NoSuchAlgorithmException
。
其他變更
Android 9 推出了幾項與密碼學相關的其他變更:
- 使用 PBE 金鑰時,如果 Bouncy Castle 預期初始化向量 (IV),但應用程式未提供,您會收到警告。
- 您可以使用 Conscrypt 實作 ARC4 密碼,指定 ARC4/ECB/NoPadding 或 ARC4/NONE/NoPadding。
- 已移除加密 Java 密碼編譯架構 (JCA) 提供者。因此,如果應用程式呼叫
SecureRandom.getInstance("SHA1PRNG", "Crypto")
,就會發生NoSuchProviderException
。 - 如果應用程式從大於金鑰結構的緩衝區解析 RSA 金鑰,就不會再發生例外狀況。
如要進一步瞭解如何使用 Android 的加密功能,請參閱「加密編譯」。
不再支援 Android 安全加密檔案
Android 9 已完全移除對 Android 安全加密檔案 (ASEC) 的支援。
在 Android 2.2 (API 級別 8) 中,Android 推出了 ASEC,以支援 SD 卡上的應用程式功能。在 Android 6.0 (API 級別 23) 中,平台推出了可採用儲存空間裝置技術,可供開發人員取代 ASEC。
ICU 程式庫更新
Android 9 使用 ICU 程式庫的 60 版。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 的發布說明。
本次更新的重大變更:
- 平台處理時區的方式已變更。
- 平台可更妥善地處理 GMT 和 UTC,UTC 不再是 GMT 的同義詞。
ICU 現在提供 GMT 和 UTC 的翻譯時區名稱。這項變更會影響
android.icu
格式和剖析行為,適用於「GMT」、「Etc/GMT」、「UTC」、「Etc/UTC」和「Zulu」等時區。 java.text.SimpleDateFormat
現在會使用 ICU 提供 UTC /GMT 的顯示名稱,也就是說:- 格式化
zzzz
會為許多語言代碼產生長的本地化字串。先前,系統會針對世界標準時間產生「UTC」,針對格林威治標準時間產生「GMT+00:00」之類的字串。 - 剖析
zzzz
會辨識「世界協調時」和「格林威治標準時間」等字串。 - 如果應用程式假設「UTC」或「GMT+00:00」是所有語言的
zzzz
輸出值,可能會發生相容性問題。
- 格式化
java.text.DateFormatSymbols.getZoneStrings()
的行為已變更:- 如同
SimpleDateFormat
,UTC 和 GMT 現在也有長名稱。世界標準時間 (UTC) 時區的 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
行為一致。
- 平台可更妥善地處理 GMT 和 UTC,UTC 不再是 GMT 的同義詞。
- 即使在剖析合法貨幣文字時,
android.icu.text.NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String)
方法也可能會擲回ParseException
。如要避免這個問題,請使用NumberFormat.parseCurrency
來顯示PLURALCURRENCYSTYLE
樣式的貨幣文字,此功能自 Android 7.0 (API 級別 24) 起便可使用。
Android 測試變更
Android 9 對 Android 測試架構的程式庫和類別結構做出了幾項變更。這些變更可協助開發人員使用架構支援的公開 API,但也讓開發人員在使用第三方程式庫或自訂邏輯時,能更靈活地建構及執行測試。
從架構中移除的程式庫
Android 9 會將以 JUnit 為基礎的類別重新整理為三個程式庫:android.test.base、android.test.runner 和 android.test.mock。這項異動可讓您針對與專案依附元件最相容的 JUnit 版本執行測試。這個 JUnit 版本可能與 android.jar
提供的版本不同。
如要進一步瞭解如何將以 JUnit 為基礎的類別組織到這些程式庫中,以及如何準備應用程式專案以編寫及執行測試,請參閱「為 Android 測試設定專案」。
測試套件建構作業異動
TestSuiteBuilder
類別中的 addRequirements()
方法已遭移除,而 TestSuiteBuilder
類別本身已淘汰。addRequirements()
方法要求開發人員提供類型為隱藏 API 的引數,導致 API 無效。
Java UTF 解碼器
UTF-8 是 Android 的預設字元集。String
建構函式 (例如 String(byte[] bytes)
) 可以解碼 UTF-8 位元組序列。
Android 9 中的 UTF-8 解碼器比先前版本更嚴格地遵循萬國碼標準。變更內容包括:
- 非最短形式的 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
)。
不過,RFC 2818 已淘汰 CN
的備用方案。因此,Android 不再改用 CN
。如要驗證主機名稱,伺服器必須提供憑證,且憑證中的 SAN
必須與主機名稱相符。系統不再信任不含與主機名稱相符的 SAN
憑證。
網路位址查詢可能會導致網路違規
需要名稱解析的網路位址查詢可能會涉及網路 I/O,因此視為阻斷作業。在主執行緒上阻斷作業可能會導致暫停或卡頓。
StrictMode
類別是開發工具,可協助開發人員偵測程式碼中的問題。
在 Android 9 以上版本中,StrictMode
會偵測需要名稱解析的網路位址查詢所造成的網路違規情形。
您不應發布啟用 StrictMode
的應用程式。如果您這麼做,應用程式可能會發生例外狀況,例如使用 detectNetwork()
或 detectAll()
方法取得可偵測網路違規的政策時,會發生 NetworkOnMainThreadException
。
解析數字 IP 位址不會視為封鎖作業。數字 IP 位址解析方式與 Android 9 以下版本相同。
通訊端標記
在低於 Android 9 的平台版本中,如果使用 setThreadStatsTag()
方法為一個 Socket 加上標記,當該 Socket 透過 ParcelFileDescriptor
容器搭配 binder IPC 傳送至另一個程序時,就會取消標記。
在 Android 9 以上版本中,當套接字標記透過 Binder IPC 傳送至其他程序時,會保留該標記。這項變更可能會影響網路流量統計資料,例如使用 queryDetailsForUidTag()
方法時。
如果您想保留先前版本的行為,也就是將傳送至其他程序的 Socket 取消標記,您可以在傳送 Socket 前呼叫 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_qtaguid 資料夾中的檔案
從 Android 9 開始,應用程式不得直接讀取 /proc/net/xt_qtaguid
資料夾中的檔案。原因是為了確保與完全沒有這些檔案的裝置保持一致。
依賴這些檔案 (TrafficStats
和 NetworkStatsManager
) 的公用 API 仍可正常運作。不過,未支援的 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()。
旋轉鎖定模式會設定使用者旋轉偏好設定,WindowManager 在處理活動旋轉時會使用此偏好設定。在下列情況下,使用者旋轉偏好設定可能會變更。請注意,系統會偏向將畫面旋轉回裝置的自然方向,通常是直向,適用於手機板型規格的裝置:
- 使用者接受旋轉建議後,旋轉偏好設定就會變更為建議值。
- 當使用者切換至強制直向的應用程式 (包括鎖定畫面或啟動器) 時,旋轉偏好設定會變更為直向。
下表概略說明常見螢幕方向的旋轉行為:
螢幕方向 | 行為 |
---|---|
未指定、使用者 | 在自動旋轉和旋轉鎖定模式中,Activity 可以直向或橫向 (以及反向) 呈現。應同時支援直向和橫向版面配置。 |
userLandscape | 在自動旋轉和旋轉鎖定模式下,活動可以以橫向或反向橫向模式轉譯。預期僅支援橫向版面配置。 |
userPortrait | 在自動旋轉和旋轉鎖定模式下,活動可以以直向或反向直向呈現。預期僅支援直向版面配置。 |
fullUser | 在自動旋轉和旋轉鎖定模式中,Activity 可以直向或橫向 (以及反向) 呈現。請同時支援直向和橫向版面配置。 旋轉鎖定功能的使用者可以選擇鎖定反向直向 (通常為 180º)。 |
sensor、fullSensor、sensorPortrait、sensorLandscape | 系統會忽略旋轉鎖定模式偏好設定,並視為自動旋轉功能已啟用。請只在非常特殊的情況下使用,並仔細考量使用者體驗。 |
Apache HTTP 用戶端淘汰作業會影響使用非標準 ClassLoader 的應用程式
在 Android 6.0 中,我們已移除對 Apache HTTP 用戶端的支援。這項變更不會影響大多數未指定 Android 9 以上版本的應用程式。不過,即使應用程式未指定 Android 9 以上版本,這項變更仍可能影響使用非標準 ClassLoader
結構的特定應用程式。
如果應用程式使用明確委派至系統 ClassLoader
的非標準 ClassLoader
,就可能受到影響。這些應用程式在 org.apache.http.*
中尋找類別時,需要改為委派至 app
ClassLoader
。如果委派至系統 ClassLoader
,應用程式將在 Android 9 以上版本中失敗,並顯示 NoClassDefFoundError
,因為系統 ClassLoader
不再認識這些類別。為避免日後發生類似問題,應用程式通常應透過應用程式 ClassLoader
載入類別,而非直接存取系統 ClassLoader
。
列舉攝影機
在 Android 9 裝置上執行的應用程式可以呼叫 getCameraIdList()
,找出所有可用的相機。應用程式不應假設裝置只有一個後置鏡頭或一個前置鏡頭。
舉例來說,如果應用程式有切換前後置鏡頭的按鈕,可能會有多個前置或後置鏡頭可供選擇。您應瀏覽攝影機清單、檢查每部攝影機的特性,並決定要向使用者顯示哪些攝影機。