There are a few issues that I encounter with integrating EncryptedSharePreferences with AndroidX Preferences GUI.
- PreferenceManager is not able to set the default preference data store globally, because it is only possible to retrieve the default shared preferences (un-encrypted variant and tied to application package name) and not set the default shared preferences to the encrypted variant.
PreferenceManager.getDefaultSharedPreferences(context);
does not have a corresponding set method.
- PreferenceManager can only be created by packages with the same library group.
The solution I have created is to not depend on SharedPreferences, but to utilise the PreferenceFragmentCompat to write to EncryptedPreferenceDataStore. However, this still comes with the issue that the default values are not initialised until the user enters the Preference screen.
Dependencies
dependencies {
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.security:security-crypto:1.0.0-rc01'
}
PreferencesFragment
import android.app.Activity;
import android.app.ActivityManager;
import android.os.Bundle;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
public class PreferencesFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
PreferenceManager preferenceManager = getPreferenceManager();
preferenceManager.setPreferenceDataStore(EncryptedPreferenceDataStore.getInstance(getContext()));
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.preferences, rootKey);
}
EncryptedPreferenceDataStore
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceDataStore;
import androidx.security.crypto.EncryptedSharedPreferences;
import java.util.Set;
public class EncryptedPreferenceDataStore extends PreferenceDataStore {
private static final String CONFIG_FILE_NAME = "FileName";
private static final String CONFIG_MASTER_KEY_ALIAS = "KeyAlias";
private static EncryptedPreferenceDataStore mInstance;
private SharedPreferences mSharedPreferences;
private Context mContext;
private EncryptedPreferenceDataStore(Context context) {
try {
mContext = context;
mSharedPreferences = EncryptedSharedPreferences.create(
CONFIG_FILE_NAME,
CONFIG_MASTER_KEY_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, //for encrypting Keys
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ////for encrypting Values
);
} catch (Exception e) {
// Fallback
mSharedPreferences = context.getSharedPreferences(CONFIG_FILE_NAME, Context.MODE_PRIVATE);
}
}
@Override
public void putString(String key, @Nullable String value) {
mSharedPreferences.edit().putString(key, value).apply();
}
@Override
public void putStringSet(String key, @Nullable Set<String> values) {
mSharedPreferences.edit().putStringSet(key, values).apply();
}
@Override
public void putInt(String key, int value) {
mSharedPreferences.edit().putInt(key, value).apply();
}
@Override
public void putLong(String key, long value) {
mSharedPreferences.edit().putLong(key, value).apply();
}
@Override
public void putFloat(String key, float value) {
mSharedPreferences.edit().putFloat(key, value).apply();
}
@Override
public void putBoolean(String key, boolean value) {
mSharedPreferences.edit().putBoolean(key, value).apply();
}
@Nullable
@Override
public String getString(String key, @Nullable String defValue) {
return mSharedPreferences.getString(key, defValue);
}
@Nullable
@Override
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
return mSharedPreferences.getStringSet(key, defValues);
}
@Override
public int getInt(String key, int defValue) {
return mSharedPreferences.getInt(key, defValue);
}
@Override
public long getLong(String key, long defValue) {
return mSharedPreferences.getLong(key, defValue);
}
@Override
public float getFloat(String key, float defValue) {
return mSharedPreferences.getFloat(key, defValue);
}
@Override
public boolean getBoolean(String key, boolean defValue) {
return mSharedPreferences.getBoolean(key, defValue);
}
}
Usage
EncryptedPreferenceDataStore prefs = EncryptedPreferenceDataStore.getInstance(getContext());
boolean bIsXXX = prefs.getBoolean(getString(R.string.pref_access_xxx), true);