5bd9a225a8
This change includes migrating data required for starting the launcher from encrypted storage into device protected storage. All of the data being moved has already been approved by the correct authorities. Bug: 251502424 Test: Performed latency testing using logs from user unlock until first workspace screen loaded. Change-Id: I58b0cd1c7bad260c2252f9e172ef85ab885c7fe9
320 lines
12 KiB
Kotlin
320 lines
12 KiB
Kotlin
/*
|
|
* Copyright (C) 2023 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.android.launcher3
|
|
|
|
import android.content.Context
|
|
import android.content.SharedPreferences
|
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
import androidx.test.filters.SmallTest
|
|
import androidx.test.platform.app.InstrumentationRegistry
|
|
import com.android.launcher3.LauncherPrefs.Companion.BOOT_AWARE_PREFS_KEY
|
|
import com.google.common.truth.Truth.assertThat
|
|
import java.util.concurrent.CountDownLatch
|
|
import java.util.concurrent.TimeUnit
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
|
|
private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
|
|
private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
|
|
private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
|
|
private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, false, Boolean::class.java)
|
|
|
|
private const val TEST_DEFAULT_VALUE = "default"
|
|
private const val TEST_PREF_KEY = "test_pref_key"
|
|
|
|
private const val WAIT_TIME_IN_SECONDS = 3L
|
|
|
|
@SmallTest
|
|
@RunWith(AndroidJUnit4::class)
|
|
class LauncherPrefsTest {
|
|
|
|
private val context by lazy { InstrumentationRegistry.getInstrumentation().targetContext }
|
|
private val launcherPrefs by lazy { LauncherPrefs.get(context) }
|
|
|
|
init {
|
|
isBootAwareStartupDataEnabled = true
|
|
}
|
|
|
|
@Test
|
|
fun has_keyMissingFromLauncherPrefs_returnsFalse() {
|
|
assertThat(launcherPrefs.has(TEST_BOOLEAN_ITEM)).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun has_keyPresentInLauncherPrefs_returnsTrue() {
|
|
with(launcherPrefs) {
|
|
putSync(TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue))
|
|
assertThat(has(TEST_BOOLEAN_ITEM)).isTrue()
|
|
remove(TEST_BOOLEAN_ITEM)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun addListener_listeningForStringItemUpdates_isCorrectlyNotifiedOfUpdates() {
|
|
val latch = CountDownLatch(1)
|
|
val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
|
|
|
|
with(launcherPrefs) {
|
|
putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue))
|
|
addListener(listener, TEST_STRING_ITEM)
|
|
putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"))
|
|
|
|
assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isTrue()
|
|
remove(TEST_STRING_ITEM)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun removeListener_previouslyListeningForStringItemUpdates_isNoLongerNotifiedOfUpdates() {
|
|
val latch = CountDownLatch(1)
|
|
val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
|
|
|
|
with(launcherPrefs) {
|
|
addListener(listener, TEST_STRING_ITEM)
|
|
removeListener(listener, TEST_STRING_ITEM)
|
|
putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "hello."))
|
|
|
|
// latch will be still be 1 (and await will return false) if the listener was not called
|
|
assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isFalse()
|
|
remove(TEST_STRING_ITEM)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun addListenerAndRemoveListener_forMultipleItems_bothWorkProperly() {
|
|
var latch = CountDownLatch(3)
|
|
val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
|
|
|
|
with(launcherPrefs) {
|
|
addListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
|
|
putSync(
|
|
TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue + 123),
|
|
TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"),
|
|
TEST_BOOLEAN_ITEM.to(!TEST_BOOLEAN_ITEM.defaultValue)
|
|
)
|
|
assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isTrue()
|
|
|
|
removeListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
|
|
latch = CountDownLatch(1)
|
|
putSync(
|
|
TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
|
|
TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
|
|
TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
|
|
)
|
|
remove(TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
|
|
|
|
assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isFalse()
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun get_booleanItemNotInLauncherprefs_returnsDefaultValue() {
|
|
assertThat(launcherPrefs.get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
|
|
}
|
|
|
|
@Test
|
|
fun get_stringItemNotInLauncherPrefs_returnsDefaultValue() {
|
|
assertThat(launcherPrefs.get(TEST_STRING_ITEM)).isEqualTo(TEST_STRING_ITEM.defaultValue)
|
|
}
|
|
|
|
@Test
|
|
fun get_intItemNotInLauncherprefs_returnsDefaultValue() {
|
|
assertThat(launcherPrefs.get(TEST_INT_ITEM)).isEqualTo(TEST_INT_ITEM.defaultValue)
|
|
}
|
|
|
|
@Test
|
|
fun put_storesItemInLauncherPrefs_successfully() {
|
|
val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
|
|
|
|
with(launcherPrefs) {
|
|
putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
|
|
assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(notDefaultValue)
|
|
remove(TEST_BOOLEAN_ITEM)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun put_storesListOfItemsInLauncherPrefs_successfully() {
|
|
with(launcherPrefs) {
|
|
putSync(
|
|
TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
|
|
TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
|
|
TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
|
|
)
|
|
assertThat(has(TEST_BOOLEAN_ITEM, TEST_INT_ITEM, TEST_STRING_ITEM)).isTrue()
|
|
remove(TEST_STRING_ITEM, TEST_INT_ITEM, TEST_BOOLEAN_ITEM)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun remove_deletesItemFromLauncherPrefs_successfully() {
|
|
val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
|
|
|
|
with(launcherPrefs) {
|
|
putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
|
|
remove(TEST_BOOLEAN_ITEM)
|
|
assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun get_contextualItem_returnsCorrectDefault() {
|
|
assertThat(launcherPrefs.get(TEST_CONTEXTUAL_ITEM)).isTrue()
|
|
}
|
|
|
|
@Test
|
|
fun getItemSharedPrefFile_forNonRestorableItem_isCorrect() {
|
|
val nonRestorableItem = LauncherPrefs.nonRestorableItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE)
|
|
assertThat(nonRestorableItem.sharedPrefFile).isEqualTo(LauncherFiles.DEVICE_PREFERENCES_KEY)
|
|
}
|
|
|
|
@Test
|
|
fun getItemSharedPrefFile_forBackedUpItem_isCorrect() {
|
|
val backedUpItem = LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE)
|
|
assertThat(backedUpItem.sharedPrefFile).isEqualTo(LauncherFiles.SHARED_PREFERENCES_KEY)
|
|
}
|
|
|
|
@Test
|
|
fun put_bootAwareItem_updatesDeviceProtectedStorage() {
|
|
val bootAwareItem =
|
|
LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
|
|
|
|
val bootAwarePrefs: SharedPreferences =
|
|
context
|
|
.createDeviceProtectedStorageContext()
|
|
.getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
|
|
bootAwarePrefs.edit().remove(bootAwareItem.sharedPrefKey).commit()
|
|
|
|
launcherPrefs.putSync(bootAwareItem.to(bootAwareItem.defaultValue))
|
|
assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isTrue()
|
|
|
|
launcherPrefs.removeSync(bootAwareItem)
|
|
}
|
|
|
|
@Test
|
|
fun put_bootAwareItem_updatesEncryptedStorage() {
|
|
val bootAwareItem =
|
|
LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
|
|
|
|
val encryptedPrefs: SharedPreferences =
|
|
context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
|
|
encryptedPrefs.edit().remove(bootAwareItem.sharedPrefKey).commit()
|
|
|
|
launcherPrefs.putSync(bootAwareItem.to(TEST_STRING_ITEM.defaultValue))
|
|
assertThat(encryptedPrefs.contains(bootAwareItem.sharedPrefKey)).isTrue()
|
|
|
|
launcherPrefs.removeSync(bootAwareItem)
|
|
}
|
|
|
|
@Test
|
|
fun remove_bootAwareItem_removesFromDeviceProtectedStorage() {
|
|
val bootAwareItem =
|
|
LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
|
|
|
|
val bootAwarePrefs: SharedPreferences =
|
|
context
|
|
.createDeviceProtectedStorageContext()
|
|
.getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
|
|
|
|
bootAwarePrefs
|
|
.edit()
|
|
.putString(bootAwareItem.sharedPrefKey, bootAwareItem.defaultValue)
|
|
.commit()
|
|
|
|
launcherPrefs.removeSync(bootAwareItem)
|
|
assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun remove_bootAwareItem_removesFromEncryptedStorage() {
|
|
val bootAwareItem =
|
|
LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
|
|
|
|
val encryptedPrefs: SharedPreferences =
|
|
context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
|
|
|
|
encryptedPrefs
|
|
.edit()
|
|
.putString(bootAwareItem.sharedPrefKey, bootAwareItem.defaultValue)
|
|
.commit()
|
|
|
|
launcherPrefs.removeSync(bootAwareItem)
|
|
assertThat(encryptedPrefs.contains(bootAwareItem.sharedPrefKey)).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun migrate_bootAwareItemsToDeviceProtectedStorage_worksAsIntended() {
|
|
val bootAwareItem =
|
|
LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
|
|
launcherPrefs.removeSync(bootAwareItem)
|
|
|
|
val bootAwarePrefs: SharedPreferences =
|
|
context
|
|
.createDeviceProtectedStorageContext()
|
|
.getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
|
|
|
|
if (bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)) {
|
|
bootAwarePrefs.edit().remove(bootAwareItem.sharedPrefKey).commit()
|
|
}
|
|
|
|
val encryptedPrefs: SharedPreferences =
|
|
context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
|
|
|
|
encryptedPrefs
|
|
.edit()
|
|
.putString(bootAwareItem.sharedPrefKey, bootAwareItem.defaultValue)
|
|
.commit()
|
|
|
|
launcherPrefs.migrateStartupDataToDeviceProtectedStorage()
|
|
assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isTrue()
|
|
|
|
launcherPrefs.removeSync(bootAwareItem)
|
|
}
|
|
|
|
@Test
|
|
fun migrate_onlyEncryptedItemsToDeviceProtectedStorage_doesNotHappen() {
|
|
val onlyEncryptedItem =
|
|
LauncherPrefs.backedUpItem(
|
|
TEST_PREF_KEY + "_",
|
|
TEST_DEFAULT_VALUE + "_",
|
|
isBootAware = false
|
|
)
|
|
|
|
val bootAwarePrefs: SharedPreferences =
|
|
context
|
|
.createDeviceProtectedStorageContext()
|
|
.getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
|
|
|
|
if (bootAwarePrefs.contains(onlyEncryptedItem.sharedPrefKey)) {
|
|
bootAwarePrefs.edit().remove(onlyEncryptedItem.sharedPrefKey).commit()
|
|
}
|
|
|
|
val encryptedPrefs: SharedPreferences =
|
|
context.getSharedPreferences(onlyEncryptedItem.sharedPrefFile, Context.MODE_PRIVATE)
|
|
|
|
encryptedPrefs
|
|
.edit()
|
|
.putString(onlyEncryptedItem.sharedPrefKey, onlyEncryptedItem.defaultValue)
|
|
.commit()
|
|
|
|
launcherPrefs.migrateStartupDataToDeviceProtectedStorage()
|
|
assertThat(bootAwarePrefs.contains(onlyEncryptedItem.sharedPrefKey)).isFalse()
|
|
|
|
encryptedPrefs.edit().remove(onlyEncryptedItem.sharedPrefKey).commit()
|
|
}
|
|
}
|