1. 程式人生 > >在Android M及更高版本中使用 Settings.System 丟擲異常"You cannot keep your settings in the secure settings. "

在Android M及更高版本中使用 Settings.System 丟擲異常"You cannot keep your settings in the secure settings. "

最近專案需要從Android L遷移到Android M。在升級的過程中,遇到了如下的問題。 
在專案中,有一些公共的資料是存放在存放在 系統資料庫SettingsProvider的System表中 
在Android L中的使用方法是: 
當需要寫資料時呼叫:

Settings.System.putStringForUser(ContentResolver cr, String name, int value, int userHandle)

當需要獲取資料時呼叫:

Settings.System.getStringForUser(ContentResolver cr, String name, final int userHandle)

將key-value存入系統公共的資料庫SettingsProvider的System的表中,在簡單的跨程序資料的儲存和共享的場景下十分簡單高效。

但是當專案執行在Android M版本以後當繼續使用該資料庫時丟擲瞭如下異常。 
IllegalArgumentException: You cannot keep your settings in the secure settings. 
從字面上理解,不能夠使用settings。

既然在L版本中可用,在M版本中不可用,那麼肯定是因為在M版本中引入了許可權檢查,並且丟擲異常。

從framework原始碼入手: 
呼叫Settings.System.putStringForUser最終都是對資料庫SettingsProvider進行操作。

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.Java

在梳理原始碼以後發現,在M版本中,在Settings.System表中執行更新或者刪除操作時,都要會呼叫函式warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk。該函式是M新增,而問題正在於該函式

private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(int targetSdkVersion, String name) {
    // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash.
    if (targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
        if (Settings.System.PRIVATE_SETTINGS.contains(name)) {
            Slog.w(LOG_TAG, "You shouldn't not change private system settings."
                    + " This will soon become an error.");
        } else {
            Slog.w(LOG_TAG, "You shouldn't keep your settings in the secure settings."
                    + " This will soon become an error.");
        }
    } else {
        if (Settings.System.PRIVATE_SETTINGS.contains(name)) {
            throw new IllegalArgumentException("You cannot change private secure settings.");
        } else {
            throw new IllegalArgumentException("You cannot keep your settings in"
                    + " the secure settings.");
        }
    }
}   

說明當app嘗試更新Settings.System的時候,會進行版本校驗,如果targetSDK 小於等於Build.VERSION_CODES.LOLLIPOP_MR1,即低版本的apk時,給出warning,保證程式碼的相容性。 
而當tagetSDK大於Build.VERSION_CODES.LOLLIPOP_MR1,即M版本及更高的版本的時候,則會丟擲異常,禁止apk寫或者刪除SettingsProvider資料庫中的System表。

分析到這裡,針對這樣的問題,由如下兩個解決方案。 
1. 如果對targetSDK沒有要求,則將targetSDK降為Build.VERSION_CODES.LOLLIPOP_MR1以下,利用Android程式向前相容性,規避問題,但是這樣的方法並不是最優方案,有可能在後續版本中,Android甚至可能放棄相容,直接丟擲異常。 
2. 放棄寫Settings.System,改用Settings.Global儲存共享資料。