Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d0a1c2c0 authored by Priyanka Advani's avatar Priyanka Advani Committed by Android (Google) Code Review
Browse files

Merge "Revert "Remove Unused parts of LauncherPrefs causing cyclical dependency."" into main

parents f3fe3b47 a80997d9
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
/*
 * 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.quickstep

import android.content.Context
import android.util.Log
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.moveStartupDataToDeviceProtectedStorageIsEnabled
import com.android.launcher3.util.LockedUserState

/**
 * Loads expensive objects in memory before the user is unlocked. This decreases experienced latency
 * when starting the launcher for the first time after a reboot.
 */
object BootAwarePreloader {
    private const val TAG = "BootAwarePreloader"

    @JvmStatic
    fun start(context: Context) {
        val lp = LauncherPrefs.get(context)
        when {
            LockedUserState.get(context).isUserUnlocked ||
                !moveStartupDataToDeviceProtectedStorageIsEnabled -> {
                /* No-Op */
            }
            lp.isStartupDataMigrated -> {
                Log.d(TAG, "preloading start up data")
                LauncherAppState.INSTANCE.get(context)
            }
            else -> {
                Log.d(TAG, "queuing start up data migration to boot aware prefs")
                LockedUserState.get(context).runOnUserUnlocked {
                    lp.migrateStartupDataToDeviceProtectedStorage()
                }
            }
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -501,6 +501,7 @@ public class TouchInteractionService extends Service {
        mTaskbarManager = new TaskbarManager(this);
        mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
        mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
        BootAwarePreloader.start(this);

        // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
        LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
+124 −23
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.util.Log
import android.view.ViewConfiguration
import androidx.annotation.VisibleForTesting
import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
@@ -37,6 +38,8 @@ import com.android.launcher3.util.Themes
 * Use same context for shared preferences, so that we use a single cached instance
 *
 * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
 * TODO(b/274501660): Fix ReorderWidgets#simpleReorder test before enabling
 *   isBootAwareStartupDataEnabled
 */
class LauncherPrefs(private val encryptedContext: Context) {
    private val deviceProtectedStorageContext =
@@ -49,8 +52,22 @@ class LauncherPrefs(private val encryptedContext: Context) {
    private val Item.encryptedPrefs
        get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)

    // This call to `SharedPreferences` needs to be explicit rather than using `get` since doing so
    // would result in a circular dependency between `isStartupDataMigrated` and `choosePreferences`
    val isStartupDataMigrated: Boolean
        get() =
            bootAwarePrefs.getBoolean(
                IS_STARTUP_DATA_MIGRATED.sharedPrefKey,
                IS_STARTUP_DATA_MIGRATED.defaultValue
            )

    private fun chooseSharedPreferences(item: Item): SharedPreferences =
        if (item.encryptionType == EncryptionType.DEVICE_PROTECTED) bootAwarePrefs
        if (
            (moveStartupDataToDeviceProtectedStorageIsEnabled &&
                item.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
                isStartupDataMigrated) || item.encryptionType == EncryptionType.DEVICE_PROTECTED
        )
            bootAwarePrefs
        else item.encryptedPrefs

    /** Wrapper around `getInner` for a `ContextualItem` */
@@ -130,7 +147,11 @@ class LauncherPrefs(private val encryptedContext: Context) {
                .toMutableMap()

        val bootAwareUpdates =
            updates.filter { it.first.encryptionType == EncryptionType.DEVICE_PROTECTED }
            updates.filter {
                (it.first.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
                    moveStartupDataToDeviceProtectedStorageIsEnabled) ||
                    it.first.encryptionType == EncryptionType.DEVICE_PROTECTED
            }
        if (bootAwareUpdates.isNotEmpty()) {
            updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
        }
@@ -231,7 +252,12 @@ class LauncherPrefs(private val encryptedContext: Context) {
                .groupBy { it.encryptedPrefs }
                .toMutableMap()

        val bootAwareUpdates = items.filter { it.encryptionType == EncryptionType.DEVICE_PROTECTED }
        val bootAwareUpdates =
            items.filter {
                (it.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
                    moveStartupDataToDeviceProtectedStorageIsEnabled) ||
                    it.encryptionType == EncryptionType.DEVICE_PROTECTED
            }
        if (bootAwareUpdates.isNotEmpty()) {
            itemsPerFile[bootAwarePrefs] = bootAwareUpdates
        }
@@ -243,7 +269,24 @@ class LauncherPrefs(private val encryptedContext: Context) {
        }
    }

    fun migrateStartupDataToDeviceProtectedStorage() {
        if (!moveStartupDataToDeviceProtectedStorageIsEnabled) return

        Log.d(
            TAG,
            "Migrating data to unencrypted shared preferences to enable preloading " +
                "while the user is locked the next time the device reboots."
        )

        with(bootAwarePrefs.edit()) {
            ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.forEach { putValue(it, get(it)) }
            putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
            apply()
        }
    }

    companion object {
        private const val TAG = "LauncherPrefs"
        @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"

        @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
@@ -253,50 +296,80 @@ class LauncherPrefs(private val encryptedContext: Context) {
        const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
        const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
        @JvmField
        val ICON_STATE = nonRestorableItem("pref_icon_shape_path", "", EncryptionType.ENCRYPTED)
        val ICON_STATE =
            nonRestorableItem("pref_icon_shape_path", "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
        @JvmField
        val ALL_APPS_OVERVIEW_THRESHOLD =
            nonRestorableItem("pref_all_apps_overview_threshold", 180, EncryptionType.ENCRYPTED)
            nonRestorableItem(
                "pref_all_apps_overview_threshold",
                180,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
            nonRestorableItem("LPNH_SLOP_PERCENTAGE", 100, EncryptionType.ENCRYPTED)
            nonRestorableItem("LPNH_SLOP_PERCENTAGE", 100, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
        @JvmField
        val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
            nonRestorableItem(
                "LPNH_TIMEOUT_MS",
                ViewConfiguration.getLongPressTimeout(),
                EncryptionType.ENCRYPTED
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT =
            nonRestorableItem("LPNH_HAPTIC_HINT_START_SCALE_PERCENT", 0, EncryptionType.ENCRYPTED)
            nonRestorableItem(
                "LPNH_HAPTIC_HINT_START_SCALE_PERCENT",
                0,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT =
            nonRestorableItem("LPNH_HAPTIC_HINT_END_SCALE_PERCENT", 100, EncryptionType.ENCRYPTED)
            nonRestorableItem(
                "LPNH_HAPTIC_HINT_END_SCALE_PERCENT",
                100,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT =
            nonRestorableItem("LPNH_HAPTIC_HINT_SCALE_EXPONENT", 1, EncryptionType.ENCRYPTED)
            nonRestorableItem(
                "LPNH_HAPTIC_HINT_SCALE_EXPONENT",
                1,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS =
            nonRestorableItem("LPNH_HAPTIC_HINT_ITERATIONS", 50, EncryptionType.ENCRYPTED)
            nonRestorableItem(
                "LPNH_HAPTIC_HINT_ITERATIONS",
                50,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY =
            nonRestorableItem("LPNH_HAPTIC_HINT_DELAY", 0, EncryptionType.ENCRYPTED)
            nonRestorableItem("LPNH_HAPTIC_HINT_DELAY", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
        @JvmField
        val PRIVATE_SPACE_APPS =
            nonRestorableItem("pref_private_space_apps", 0, EncryptionType.ENCRYPTED)
        @JvmField
        val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false)
            nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
        @JvmField val ENABLE_TWOLINE_ALLAPPS_TOGGLE =
                backedUpItem("pref_enable_two_line_toggle", false)
        @JvmField
        val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.ENCRYPTED)
        val THEMED_ICONS =
            backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
        @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
        @JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
        @JvmField
        val WORKSPACE_SIZE =
            backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "", EncryptionType.ENCRYPTED)
            backedUpItem(
                DeviceGridState.KEY_WORKSPACE_SIZE,
                "",
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val HOTSEAT_COUNT =
            backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1, EncryptionType.ENCRYPTED)
            backedUpItem(
                DeviceGridState.KEY_HOTSEAT_COUNT,
                -1,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val TASKBAR_PINNING =
            backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
@@ -306,10 +379,11 @@ class LauncherPrefs(private val encryptedContext: Context) {
            backedUpItem(
                DeviceGridState.KEY_DEVICE_TYPE,
                InvariantDeviceProfile.TYPE_PHONE,
                EncryptionType.ENCRYPTED
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.ENCRYPTED)
        val DB_FILE =
            backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
        @JvmField
        val SHOULD_SHOW_SMARTSPACE =
            backedUpItem(
@@ -322,11 +396,15 @@ class LauncherPrefs(private val encryptedContext: Context) {
            backedUpItem(
                RestoreDbTask.RESTORED_DEVICE_TYPE,
                InvariantDeviceProfile.TYPE_PHONE,
                EncryptionType.ENCRYPTED
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField
        val IS_FIRST_LOAD_AFTER_RESTORE =
            nonRestorableItem(FIRST_LOAD_AFTER_RESTORE_KEY, false, EncryptionType.ENCRYPTED)
            nonRestorableItem(
                FIRST_LOAD_AFTER_RESTORE_KEY,
                false,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
        @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
        @JvmField
@@ -335,7 +413,7 @@ class LauncherPrefs(private val encryptedContext: Context) {
                "idp_grid_name",
                isBackedUp = true,
                defaultValue = null,
                encryptionType = EncryptionType.ENCRYPTED,
                encryptionType = EncryptionType.MOVE_TO_DEVICE_PROTECTED,
                type = String::class.java
            )
        @JvmField
@@ -343,6 +421,14 @@ class LauncherPrefs(private val encryptedContext: Context) {
            backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
                RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
            }
        @JvmField
        val IS_STARTUP_DATA_MIGRATED =
            ConstantItem(
                "is_startup_data_boot_aware",
                isBackedUp = false,
                defaultValue = false,
                encryptionType = EncryptionType.DEVICE_PROTECTED
            )

        // Preferences for widget configurations
        @JvmField
@@ -407,6 +493,12 @@ class LauncherPrefs(private val encryptedContext: Context) {
    }
}

// It is a var because the unit tests are setting this to true so they can run.
var moveStartupDataToDeviceProtectedStorageIsEnabled: Boolean =
    com.android.launcher3.config.FeatureFlags.MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE.get()

private val ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE: MutableSet<ConstantItem<*>> = mutableSetOf()

abstract class Item {
    abstract val sharedPrefKey: String
    abstract val isBackedUp: Boolean
@@ -426,6 +518,14 @@ data class ConstantItem<T>(
    // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
    override val type: Class<out T> = defaultValue!!::class.java
) : Item() {
    init {
        if (
            encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
                moveStartupDataToDeviceProtectedStorageIsEnabled
        ) {
            ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.add(this)
        }
    }

    fun get(c: Context): T = LauncherPrefs.get(c).get(this)
}
@@ -452,4 +552,5 @@ data class ContextualItem<T>(
enum class EncryptionType {
    ENCRYPTED,
    DEVICE_PROTECTED,
    MOVE_TO_DEVICE_PROTECTED
}
+7 −0
Original line number Diff line number Diff line
@@ -100,6 +100,13 @@ public final class FeatureFlags {
    public static final BooleanFlag ENABLE_DISMISS_PREDICTION_UNDO = getDebugFlag(270394476,
            "ENABLE_DISMISS_PREDICTION_UNDO", DISABLED,
            "Show an 'Undo' snackbar when users dismiss a predicted hotseat item");

    public static final BooleanFlag MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE = getDebugFlag(
            251502424, "ENABLE_BOOT_AWARE_STARTUP_DATA", DISABLED,
            "Marks LauncherPref data as (and allows it to) available while the device is"
                    + " locked. Enabling this causes a 1-time movement of certain SharedPreferences"
                    + " data. Improves startup latency.");

    public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(270395171,
            "CONTINUOUS_VIEW_TREE_CAPTURE", ENABLED, "Capture View tree every frame");

+122 −1
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ 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.AfterClass
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith

@@ -46,6 +48,20 @@ class LauncherPrefsTest {
    private val context by lazy { InstrumentationRegistry.getInstrumentation().targetContext }
    private val launcherPrefs by lazy { LauncherPrefs.get(context) }

    companion object {
        @BeforeClass
        @JvmStatic
        fun setup() {
            moveStartupDataToDeviceProtectedStorageIsEnabled = true
        }

        @AfterClass
        @JvmStatic
        fun teardown() {
            moveStartupDataToDeviceProtectedStorageIsEnabled = false
        }
    }

    @Test
    fun has_keyMissingFromLauncherPrefs_returnsFalse() {
        assertThat(launcherPrefs.has(TEST_BOOLEAN_ITEM)).isFalse()
@@ -206,13 +222,32 @@ class LauncherPrefsTest {
        launcherPrefs.removeSync(bootAwareItem)
    }

    @Test
    fun put_bootAwareItem_updatesEncryptedStorage() {
        val bootAwareItem =
            LauncherPrefs.backedUpItem(
                TEST_PREF_KEY,
                TEST_DEFAULT_VALUE,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )

        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,
                EncryptionType.DEVICE_PROTECTED
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )

        val bootAwarePrefs: SharedPreferences =
@@ -228,4 +263,90 @@ class LauncherPrefsTest {
        launcherPrefs.removeSync(bootAwareItem)
        assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isFalse()
    }

    @Test
    fun remove_bootAwareItem_removesFromEncryptedStorage() {
        val bootAwareItem =
            LauncherPrefs.backedUpItem(
                TEST_PREF_KEY,
                TEST_DEFAULT_VALUE,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )

        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,
                EncryptionType.MOVE_TO_DEVICE_PROTECTED
            )
        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 + "_",
                EncryptionType.ENCRYPTED
            )

        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()
    }
}