diff --git a/app/src/main/java/helium314/keyboard/latin/App.kt b/app/src/main/java/helium314/keyboard/latin/App.kt index 9377ae712..e9af21340 100644 --- a/app/src/main/java/helium314/keyboard/latin/App.kt +++ b/app/src/main/java/helium314/keyboard/latin/App.kt @@ -135,9 +135,13 @@ private fun upgradesWhenComingFromOldAppName(context: Context) { additionalSubtypes.add(it.replace(localeString, localeString.constructLocale().toLanguageTag())) } Settings.writePrefAdditionalSubtypes(prefs, additionalSubtypes.joinToString(";")) - // move pinned clips to credential protected storage + // move pinned clips to credential protected storage if device is not locked (should never happen) if (!prefs.contains(Settings.PREF_PINNED_CLIPS)) return - val defaultPrefs = PreferenceManager.getDefaultSharedPreferences(context) - defaultPrefs.edit { putString(Settings.PREF_PINNED_CLIPS, prefs.getString(Settings.PREF_PINNED_CLIPS, "")) } - prefs.edit { remove(Settings.PREF_PINNED_CLIPS) } + try { + val defaultPrefs = PreferenceManager.getDefaultSharedPreferences(context) + defaultPrefs.edit { putString(Settings.PREF_PINNED_CLIPS, prefs.getString(Settings.PREF_PINNED_CLIPS, "")) } + prefs.edit { remove(Settings.PREF_PINNED_CLIPS) } + } catch (_: IllegalStateException) { + // SharedPreferences in credential encrypted storage are not available until after user is unlocked + } } diff --git a/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt b/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt index cc92b5e2b..ea4ea3c30 100644 --- a/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt +++ b/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt @@ -133,7 +133,7 @@ class ClipboardHistoryManager( // pinned clips are stored in default shared preferences, not in device protected preferences! private fun loadPinnedClips() { - val pinnedClipString = Settings.readPinnedClipString(PreferenceManager.getDefaultSharedPreferences(latinIME)) + val pinnedClipString = Settings.readPinnedClipString(latinIME) if (pinnedClipString.isEmpty()) return val pinnedClips: List = Json.decodeFromString(pinnedClipString) latinIME.mHandler.postUpdateClipboardPinnedClips(pinnedClips) @@ -141,7 +141,7 @@ class ClipboardHistoryManager( private fun savePinnedClips() { val pinnedClips = Json.encodeToString(historyEntries.filter { it.isPinned }) - Settings.writePinnedClipString(PreferenceManager.getDefaultSharedPreferences(latinIME), pinnedClips) + Settings.writePinnedClipString(latinIME, pinnedClips) } interface OnHistoryChangeListener { diff --git a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt index 651905c43..7abbbe850 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt @@ -304,7 +304,7 @@ class AdvancedSettingsFragment : SubScreenFragment() { if (backupFilePatterns.any { path.matches(it) }) files.add(file) } - val protectedFilesDir = DeviceProtectedUtils.getDeviceProtectedContext(requireContext()).filesDir + val protectedFilesDir = DeviceProtectedUtils.getFilesDir(requireContext()) val protectedFilesPath = protectedFilesDir.path + File.separator val protectedFiles = mutableListOf() protectedFilesDir.walk().forEach { file -> @@ -351,7 +351,7 @@ class AdvancedSettingsFragment : SubScreenFragment() { ZipInputStream(inputStream).use { zip -> var entry: ZipEntry? = zip.nextEntry val filesDir = requireContext().filesDir?.path ?: return - val deviceProtectedFilesDir = DeviceProtectedUtils.getDeviceProtectedContext(requireContext()).filesDir?.path ?: return + val deviceProtectedFilesDir = DeviceProtectedUtils.getFilesDir(requireContext()).path Settings.getInstance().stopListener() while (entry != null) { if (entry.name.startsWith("unprotected${File.separator}")) { diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 67a3b3bf8..e4ce1768f 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -24,6 +24,7 @@ import android.view.Gravity; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; +import androidx.preference.PreferenceManager; import helium314.keyboard.keyboard.KeyboardTheme; import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyTextsKt; @@ -477,12 +478,23 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID, defValue); } - public static String readPinnedClipString(final SharedPreferences prefs) { - return prefs.getString(PREF_PINNED_CLIPS, ""); + public static String readPinnedClipString(final Context context) { + try { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getString(PREF_PINNED_CLIPS, ""); + } catch (final IllegalStateException e) { + // SharedPreferences in credential encrypted storage are not available until after user is unlocked + return ""; + } } - public static void writePinnedClipString(final SharedPreferences prefs, final String clips) { - prefs.edit().putString(PREF_PINNED_CLIPS, clips).apply(); + public static void writePinnedClipString(final Context context, final String clips) { + try { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit().putString(PREF_PINNED_CLIPS, clips).apply(); + } catch (final IllegalStateException e) { + // SharedPreferences in credential encrypted storage are not available until after user is unlocked + } } public static ArrayList readPinnedKeys(final SharedPreferences prefs) { @@ -539,7 +551,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang } public static File getLayoutsDir(final Context context) { - return new File(DeviceProtectedUtils.getDeviceProtectedContext(context).getFilesDir(), "layouts"); + return new File(DeviceProtectedUtils.getFilesDir(context), "layouts"); } @Nullable public static Drawable readUserBackgroundImage(final Context context, final boolean night) { @@ -561,8 +573,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang } public static File getCustomBackgroundFile(final Context context, final boolean night) { - return new File(DeviceProtectedUtils.getDeviceProtectedContext(context).getFilesDir(), - "custom_background_image" + (night ? "_night" : "")); + return new File(DeviceProtectedUtils.getFilesDir(context), "custom_background_image" + (night ? "_night" : "")); } public static boolean readDayNightPref(final SharedPreferences prefs, final Resources res) { diff --git a/app/src/main/java/helium314/keyboard/latin/utils/DeviceProtectedUtils.java b/app/src/main/java/helium314/keyboard/latin/utils/DeviceProtectedUtils.java index 439029e9a..81b302fd7 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/DeviceProtectedUtils.java +++ b/app/src/main/java/helium314/keyboard/latin/utils/DeviceProtectedUtils.java @@ -12,6 +12,8 @@ import android.os.Build; import androidx.preference.PreferenceManager; +import java.io.File; + public final class DeviceProtectedUtils { static final String TAG = DeviceProtectedUtils.class.getSimpleName(); @@ -33,11 +35,16 @@ public final class DeviceProtectedUtils { return prefs; } - public static Context getDeviceProtectedContext(final Context context) { + // keep this private to avoid accidental use of device protected context anywhere in the app + private static Context getDeviceProtectedContext(final Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return context; return context.isDeviceProtectedStorage() ? context : context.createDeviceProtectedStorageContext(); } + public static File getFilesDir(final Context context) { + return getDeviceProtectedContext(context).getFilesDir(); + } + private DeviceProtectedUtils() { // This utility class is not publicly instantiable. } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/JniUtils.java b/app/src/main/java/helium314/keyboard/latin/utils/JniUtils.java index a47f96644..f531033e6 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/JniUtils.java +++ b/app/src/main/java/helium314/keyboard/latin/utils/JniUtils.java @@ -63,8 +63,7 @@ public final class JniUtils { if (app != null) { // we want the default preferences, because storing the checksum in device protected storage is discouraged // see https://developer.android.com/reference/android/content/Context#createDeviceProtectedStorageContext() - // todo: test what happens on an encrypted phone after reboot (don't have one...) - // does the app restart after unlocking, or is gesture typing unavailable? + // if device is locked, this will throw an IllegalStateException wantedChecksum = PreferenceManager.getDefaultSharedPreferences(app).getString(Settings.PREF_LIBRARY_CHECKSUM, wantedChecksum); } final String checksum = ChecksumCalculator.INSTANCE.checksum(new FileInputStream(userSuppliedLibrary)); @@ -79,7 +78,9 @@ public final class JniUtils { sHaveGestureLib = false; } } catch (Throwable t) { // catch everything, maybe provided library simply doesn't work - Log.w(TAG, "Could not load user-supplied library", t); + if (!(t instanceof IllegalStateException) || !"SharedPreferences in credential encrypted storage are not available until after user is unlocked".equals(t.getMessage())) + // but don't log if device is locked, here we expect the exception and only load system library, if possible + Log.w(TAG, "Could not load user-supplied library", t); } }