Merge "Trigger unfold animation without treshold while on keyguard" into tm-qpr-dev
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4be5e14..06399b9 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1129,6 +1129,17 @@
     public static final long OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN = 218959984L;
 
     /**
+     * Enables sending fake focus for unfocused apps in splitscreen. Some game engines
+     * wait to get focus before drawing the content of the app so fake focus helps them to avoid
+     * staying blacked out when they are resumed and do not have focus yet.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L;
+
+    /**
      * Compares activity window layout min width/height with require space for multi window to
      * determine if it can be put into multi window mode.
      */
diff --git a/core/java/android/service/chooser/OWNERS b/core/java/android/service/chooser/OWNERS
index a5acba5..dcd4a7b 100644
--- a/core/java/android/service/chooser/OWNERS
+++ b/core/java/android/service/chooser/OWNERS
@@ -1,4 +1,3 @@
-asc@google.com
-mpietal@google.com
-dsandler@android.com
-dsandler@google.com
+mrcasey@google.com
+ayepin@google.com
+joshtrask@google.com
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 3833976..970728f 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -2,6 +2,7 @@
 per-file *Resolver* = file:/packages/SystemUI/OWNERS
 per-file *Chooser* = file:/packages/SystemUI/OWNERS
 per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
+per-file AbstractMultiProfilePagerAdapter.java = file:/packages/SystemUI/OWNERS
 per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
 per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
 
@@ -11,4 +12,4 @@
 per-file *Voice* = file:/core/java/android/service/voice/OWNERS
 
 # System language settings
-per-file *Locale* = file:platform/packages/apps/Settings:/src/com/android/settings/localepicker/OWNERS
\ No newline at end of file
+per-file *Locale* = file:platform/packages/apps/Settings:/src/com/android/settings/localepicker/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
index c6537c0..b6ea9dd 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.app;
 
+import static android.util.PollingCheck.waitFor;
+
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.action.ViewActions.swipeUp;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
@@ -49,6 +52,8 @@
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab;
 
+import junit.framework.AssertionFailedError;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -331,8 +336,17 @@
         final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
                 : R.string.resolver_personal_tab;
 
-        onView(withText(stringId)).perform(click());
-        waitForIdle();
+        waitFor(() -> {
+            onView(withText(stringId)).perform(click());
+            waitForIdle();
+
+            try {
+                onView(withText(stringId)).check(matches(isSelected()));
+                return true;
+            } catch (AssertionFailedError e) {
+                return false;
+            }
+        });
 
         onView(withId(R.id.contentPanel))
                 .perform(swipeUp());
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
index ce68906..4a79f5e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.app;
 
+import static android.util.PollingCheck.waitFor;
+
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.action.ViewActions.swipeUp;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
@@ -48,6 +51,8 @@
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab;
 
+import junit.framework.AssertionFailedError;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -309,8 +314,17 @@
         final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
                 : R.string.resolver_personal_tab;
 
-        onView(withText(stringId)).perform(click());
-        waitForIdle();
+        waitFor(() -> {
+            onView(withText(stringId)).perform(click());
+            waitForIdle();
+
+            try {
+                onView(withText(stringId)).check(matches(isSelected()));
+                return true;
+            } catch (AssertionFailedError e) {
+                return false;
+            }
+        });
 
         onView(withId(R.id.contentPanel))
                 .perform(swipeUp());
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index dc4b563..a5b192c 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -63,6 +63,12 @@
     sdk_version: "current",
 }
 
+android_library_import {
+    name: "window-extensions-core",
+    aars: ["window-extensions-core-release.aar"],
+    sdk_version: "current",
+}
+
 java_library {
     name: "androidx.window.extensions",
     srcs: [
@@ -70,7 +76,10 @@
         "src/androidx/window/util/**/*.java",
         "src/androidx/window/common/**/*.java",
     ],
-    static_libs: ["window-extensions"],
+    static_libs: [
+        "window-extensions",
+        "window-extensions-core",
+    ],
     installable: true,
     sdk_version: "core_platform",
     system_ext_specific: true,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 1af1313..85812ea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -75,7 +75,10 @@
 import androidx.annotation.Nullable;
 import androidx.window.common.CommonFoldingFeature;
 import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+import androidx.window.extensions.WindowExtensionsImpl;
 import androidx.window.extensions.WindowExtensionsProvider;
+import androidx.window.extensions.core.util.function.Consumer;
+import androidx.window.extensions.core.util.function.Function;
 import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
 import androidx.window.extensions.layout.WindowLayoutComponentImpl;
 
@@ -86,7 +89,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 
 /**
  * Main controller class that manages split states and presentation.
@@ -112,7 +114,7 @@
     /**
      * A developer-defined {@link SplitAttributes} calculator to compute the current
      * {@link SplitAttributes} with the current device and window states.
-     * It is registered via {@link #setSplitAttributesCalculator(SplitAttributesCalculator)}
+     * It is registered via {@link #setSplitAttributesCalculator(Function)}
      * and unregistered via {@link #clearSplitAttributesCalculator()}.
      * This is called when:
      * <ul>
@@ -125,7 +127,7 @@
      */
     @GuardedBy("mLock")
     @Nullable
-    private SplitAttributesCalculator mSplitAttributesCalculator;
+    private Function<SplitAttributesCalculatorParams, SplitAttributes> mSplitAttributesCalculator;
 
     /**
      * Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
@@ -138,6 +140,7 @@
     final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
 
     /** Callback to Jetpack to notify about changes to split states. */
+    @GuardedBy("mLock")
     @Nullable
     private Consumer<List<SplitInfo>> mEmbeddingCallback;
     private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
@@ -171,7 +174,8 @@
         mWindowLayoutComponent.addFoldingStateChangedCallback(new FoldingFeatureListener());
     }
 
-    private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
+    private class FoldingFeatureListener
+            implements java.util.function.Consumer<List<CommonFoldingFeature>> {
         @Override
         public void accept(List<CommonFoldingFeature> foldingFeatures) {
             synchronized (mLock) {
@@ -212,7 +216,8 @@
     }
 
     @Override
-    public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
+    public void setSplitAttributesCalculator(
+            @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) {
         synchronized (mLock) {
             mSplitAttributesCalculator = calculator;
         }
@@ -227,7 +232,7 @@
 
     @GuardedBy("mLock")
     @Nullable
-    SplitAttributesCalculator getSplitAttributesCalculator() {
+    Function<SplitAttributesCalculatorParams, SplitAttributes> getSplitAttributesCalculator() {
         return mSplitAttributesCalculator;
     }
 
@@ -240,9 +245,22 @@
 
     /**
      * Registers the split organizer callback to notify about changes to active splits.
+     * @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
+     * {@link WindowExtensionsImpl#getVendorApiLevel()} 2.
      */
+    @Deprecated
     @Override
-    public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+    public void setSplitInfoCallback(
+            @NonNull java.util.function.Consumer<List<SplitInfo>> callback) {
+        Consumer<List<SplitInfo>> oemConsumer = callback::accept;
+        setSplitInfoCallback(oemConsumer);
+    }
+
+    /**
+     * Registers the split organizer callback to notify about changes to active splits.
+     * @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2
+     */
+    public void setSplitInfoCallback(Consumer<List<SplitInfo>> callback) {
         synchronized (mLock) {
             mEmbeddingCallback = callback;
             updateCallbackIfNecessary();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 9db9f87..41580a0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -42,11 +42,11 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.window.extensions.core.util.function.Function;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
-import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttributesCalculatorParams;
 import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
 import androidx.window.extensions.layout.DisplayFeature;
 import androidx.window.extensions.layout.FoldingFeature;
@@ -522,7 +522,8 @@
             @NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
         final Configuration taskConfiguration = taskProperties.getConfiguration();
         final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
-        final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator();
+        final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
+                mController.getSplitAttributesCalculator();
         final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
         final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics);
         if (calculator == null) {
@@ -538,7 +539,7 @@
         final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
                 taskWindowMetrics, taskConfiguration, defaultSplitAttributes,
                 isDefaultMinSizeSatisfied, windowLayoutInfo, rule.getTag());
-        final SplitAttributes splitAttributes = calculator.computeSplitAttributesForParams(params);
+        final SplitAttributes splitAttributes = calculator.apply(params);
         return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair);
     }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 84b2bfc..32b915c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -44,6 +44,7 @@
 import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
 import androidx.window.common.EmptyLifecycleCallbacksAdapter;
 import androidx.window.common.RawFoldingFeatureProducer;
+import androidx.window.extensions.core.util.function.Consumer;
 import androidx.window.util.DataProducer;
 
 import java.util.ArrayList;
@@ -51,7 +52,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Consumer;
 
 /**
  * Reference implementation of androidx.window.extensions.layout OEM interface for use with
@@ -80,6 +80,10 @@
     private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
             new ArrayMap<>();
 
+    @GuardedBy("mLock")
+    private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
+            mJavaToExtConsumers = new ArrayMap<>();
+
     public WindowLayoutComponentImpl(@NonNull Context context) {
         ((Application) context.getApplicationContext())
                 .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
@@ -90,7 +94,8 @@
     }
 
     /** Registers to listen to {@link CommonFoldingFeature} changes */
-    public void addFoldingStateChangedCallback(Consumer<List<CommonFoldingFeature>> consumer) {
+    public void addFoldingStateChangedCallback(
+            java.util.function.Consumer<List<CommonFoldingFeature>> consumer) {
         synchronized (mLock) {
             mFoldingFeatureProducer.addDataChangedCallback(consumer);
         }
@@ -104,13 +109,17 @@
      */
     @Override
     public void addWindowLayoutInfoListener(@NonNull Activity activity,
-            @NonNull Consumer<WindowLayoutInfo> consumer) {
-        addWindowLayoutInfoListener((Context) activity, consumer);
+            @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
+        final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
+        synchronized (mLock) {
+            mJavaToExtConsumers.put(consumer, extConsumer);
+        }
+        addWindowLayoutInfoListener(activity, extConsumer);
     }
 
     /**
-     * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context
-     * as a parameter.
+     * Similar to {@link #addWindowLayoutInfoListener(Activity, java.util.function.Consumer)}, but
+     * takes a UI Context as a parameter.
      *
      * Jetpack {@link androidx.window.layout.ExtensionWindowLayoutInfoBackend} makes sure all
      * consumers related to the same {@link Context} gets updated {@link WindowLayoutInfo}
@@ -151,6 +160,18 @@
         }
     }
 
+    @Override
+    public void removeWindowLayoutInfoListener(
+            @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
+        final Consumer<WindowLayoutInfo> extConsumer;
+        synchronized (mLock) {
+            extConsumer = mJavaToExtConsumers.remove(consumer);
+        }
+        if (extConsumer != null) {
+            removeWindowLayoutInfoListener(extConsumer);
+        }
+    }
+
     /**
      * Removes a listener no longer interested in receiving updates.
      *
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index 2f92a57..459ec9f 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -34,9 +34,11 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Pair;
+import android.view.WindowMetrics;
 import android.window.TaskFragmentInfo;
 import android.window.WindowContainerToken;
 
+import androidx.window.extensions.core.util.function.Predicate;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType;
 import androidx.window.extensions.layout.DisplayFeature;
 import androidx.window.extensions.layout.FoldingFeature;
@@ -107,7 +109,7 @@
     static SplitRule createSplitRule(@NonNull Activity primaryActivity,
             @NonNull Intent secondaryIntent, boolean clearTop) {
         final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent);
-        return new SplitPairRule.Builder(
+        return createSplitPairRuleBuilder(
                 activityPair -> false,
                 targetPair::equals,
                 w -> true)
@@ -144,7 +146,7 @@
             @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
             int finishSecondaryWithPrimary, boolean clearTop) {
         final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
-        return new SplitPairRule.Builder(
+        return createSplitPairRuleBuilder(
                 targetPair::equals,
                 activityIntentPair -> false,
                 w -> true)
@@ -223,4 +225,26 @@
         displayFeatures.add(foldingFeature);
         return new WindowLayoutInfo(displayFeatures);
     }
+
+    static ActivityRule.Builder createActivityBuilder(
+            @NonNull Predicate<Activity> activityPredicate,
+            @NonNull Predicate<Intent> intentPredicate) {
+        return new ActivityRule.Builder(activityPredicate, intentPredicate);
+    }
+
+    static SplitPairRule.Builder createSplitPairRuleBuilder(
+            @NonNull Predicate<Pair<Activity, Activity>> activitiesPairPredicate,
+            @NonNull Predicate<Pair<Activity, Intent>> activityIntentPairPredicate,
+            @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
+        return new SplitPairRule.Builder(activitiesPairPredicate, activityIntentPairPredicate,
+                windowMetricsPredicate);
+    }
+
+    static SplitPlaceholderRule.Builder createSplitPlaceholderRuleBuilder(
+            @NonNull Intent placeholderIntent, @NonNull Predicate<Activity> activityPredicate,
+            @NonNull Predicate<Intent> intentPredicate,
+            @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
+        return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate,
+                intentPredicate, windowMetricsPredicate);
+    }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 3cc31f9..f9cbb5c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -34,8 +34,11 @@
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -429,7 +432,7 @@
     @Test
     public void testResolveStartActivityIntent_withoutLaunchingActivity() {
         final Intent intent = new Intent();
-        final ActivityRule expandRule = new ActivityRule.Builder(r -> false, i -> i == intent)
+        final ActivityRule expandRule = createActivityBuilder(r -> false, i -> i == intent)
                 .setShouldAlwaysExpand(true)
                 .build();
         mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1167,7 +1170,7 @@
 
     @Test
     public void testHasSamePresentation() {
-        SplitPairRule splitRule1 = new SplitPairRule.Builder(
+        SplitPairRule splitRule1 = createSplitPairRuleBuilder(
                 activityPair -> true,
                 activityIntentPair -> true,
                 windowMetrics -> true)
@@ -1175,7 +1178,7 @@
                 .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
                 .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
                 .build();
-        SplitPairRule splitRule2 = new SplitPairRule.Builder(
+        SplitPairRule splitRule2 = createSplitPairRuleBuilder(
                 activityPair -> true,
                 activityIntentPair -> true,
                 windowMetrics -> true)
@@ -1188,7 +1191,7 @@
                 SplitController.haveSamePresentation(splitRule1, splitRule2,
                         new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
 
-        splitRule2 = new SplitPairRule.Builder(
+        splitRule2 = createSplitPairRuleBuilder(
                 activityPair -> true,
                 activityIntentPair -> true,
                 windowMetrics -> true)
@@ -1352,7 +1355,7 @@
 
     /** Setups a rule to always expand the given intent. */
     private void setupExpandRule(@NonNull Intent expandIntent) {
-        final ActivityRule expandRule = new ActivityRule.Builder(r -> false, expandIntent::equals)
+        final ActivityRule expandRule = createActivityBuilder(r -> false, expandIntent::equals)
                 .setShouldAlwaysExpand(true)
                 .build();
         mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1360,7 +1363,7 @@
 
     /** Setups a rule to always expand the given activity. */
     private void setupExpandRule(@NonNull Activity expandActivity) {
-        final ActivityRule expandRule = new ActivityRule.Builder(expandActivity::equals, i -> false)
+        final ActivityRule expandRule = createActivityBuilder(expandActivity::equals, i -> false)
                 .setShouldAlwaysExpand(true)
                 .build();
         mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1368,7 +1371,7 @@
 
     /** Setups a rule to launch placeholder for the given activity. */
     private void setupPlaceholderRule(@NonNull Activity primaryActivity) {
-        final SplitRule placeholderRule = new SplitPlaceholderRule.Builder(PLACEHOLDER_INTENT,
+        final SplitRule placeholderRule = createSplitPlaceholderRuleBuilder(PLACEHOLDER_INTENT,
                 primaryActivity::equals, i -> false, w -> true)
                 .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
                 .build();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 6dae0a1..cbb6e31 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -27,6 +27,7 @@
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -473,7 +474,7 @@
         final Activity secondaryActivity = createMockActivity();
         final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID);
         final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
-        final SplitPairRule rule = new SplitPairRule.Builder(pair ->
+        final SplitPairRule rule = createSplitPairRuleBuilder(pair ->
                 pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
                 metrics -> true)
                 .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
@@ -491,7 +492,7 @@
 
     @Test
     public void testComputeSplitAttributes() {
-        final SplitPairRule splitPairRule = new SplitPairRule.Builder(
+        final SplitPairRule splitPairRule = createSplitPairRuleBuilder(
                 activityPair -> true,
                 activityIntentPair -> true,
                 windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS))
diff --git a/libs/WindowManager/Jetpack/window-extensions-core-release.aar b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
new file mode 100644
index 0000000..4c08074
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
Binary files differ
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 4978e04..6fd8d29 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6b4d5ae..e148d4f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -60,7 +60,7 @@
             // except for SystemUI-core.
             // Copied from compose/features/Android.bp.
             static_libs: [
-                "SystemUIComposeCore",
+                "PlatformComposeCore",
 
                 "androidx.compose.runtime_runtime",
                 "androidx.compose.material3_material3",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2f5b42f..810dd33 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -193,7 +193,7 @@
     <permission android:name="com.android.systemui.permission.FLAGS"
                 android:protectionLevel="signature" />
 
-    <permission android:name="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+    <permission android:name="android.permission.CUSTOMIZE_SYSTEM_UI"
         android:protectionLevel="signature|privileged" />
 
     <!-- Adding Quick Settings tiles -->
@@ -1003,10 +1003,10 @@
         </receiver>
 
         <provider
-            android:authorities="com.android.systemui.keyguard.quickaffordance"
-            android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+            android:authorities="com.android.systemui.customization"
+            android:name="com.android.systemui.keyguard.CustomizationProvider"
             android:exported="true"
-            android:permission="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+            android:permission="android.permission.CUSTOMIZE_SYSTEM_UI"
             />
     </application>
 </manifest>
diff --git a/packages/SystemUI/compose/core/Android.bp b/packages/SystemUI/compose/core/Android.bp
index fbdb526..ab3efb8 100644
--- a/packages/SystemUI/compose/core/Android.bp
+++ b/packages/SystemUI/compose/core/Android.bp
@@ -22,7 +22,7 @@
 }
 
 android_library {
-    name: "SystemUIComposeCore",
+    name: "PlatformComposeCore",
     manifest: "AndroidManifest.xml",
 
     srcs: [
diff --git a/packages/SystemUI/compose/core/AndroidManifest.xml b/packages/SystemUI/compose/core/AndroidManifest.xml
index 83c442d..20543db 100644
--- a/packages/SystemUI/compose/core/AndroidManifest.xml
+++ b/packages/SystemUI/compose/core/AndroidManifest.xml
@@ -16,7 +16,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemui.compose.core">
+    package="com.android.compose.core">
 
 
 </manifest>
diff --git a/packages/SystemUI/compose/core/TEST_MAPPING b/packages/SystemUI/compose/core/TEST_MAPPING
index dc243d2..ee95f73 100644
--- a/packages/SystemUI/compose/core/TEST_MAPPING
+++ b/packages/SystemUI/compose/core/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "SystemUIComposeCoreTests",
+      "name": "PlatformComposeCoreTests",
       "options": [
         {
           "exclude-annotation": "org.junit.Ignore"
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
similarity index 94%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index 496f4b3..d1ee18a 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.compose
+package com.android.compose
 
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.layout.PaddingValues
@@ -27,10 +27,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
+import com.android.compose.theme.LocalAndroidColorScheme
 
 @Composable
-fun SysUiButton(
+fun PlatformButton(
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
@@ -48,7 +48,7 @@
 }
 
 @Composable
-fun SysUiOutlinedButton(
+fun PlatformOutlinedButton(
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
@@ -67,7 +67,7 @@
 }
 
 @Composable
-fun SysUiTextButton(
+fun PlatformTextButton(
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiController.kt b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiController.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt
index c9470c8..a02954a 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose
+package com.android.compose
 
 import android.app.Activity
 import android.content.Context
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index d31ca51..259f0ed 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.animation
+package com.android.compose.animation
 
 import android.content.Context
 import android.view.View
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index f75b3a8..edb10c7d 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.animation
+package com.android.compose.animation
 
 import android.view.View
 import android.view.ViewGroup
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ViewTreeSavedStateRegistryOwner.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt
similarity index 96%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ViewTreeSavedStateRegistryOwner.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt
index 79f1cad1..d400f77 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ViewTreeSavedStateRegistryOwner.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.animation
+package com.android.compose.animation
 
 import android.view.View
 import androidx.savedstate.SavedStateRegistryOwner
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/FadingBackground.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
index 121bf2c..13c2464 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/FadingBackground.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.modifiers
+package com.android.compose.modifiers
 
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.DrawModifier
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Padding.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/modifiers/Padding.kt
index 3b13c0b..d9bc9c9 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Padding.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.modifiers
+package com.android.compose.modifiers
 
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.LayoutModifier
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Size.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/modifiers/Size.kt
index 570d2431..ec1b966 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Size.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.compose.modifiers
+package com.android.compose.modifiers
 
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.IntrinsicMeasurable
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt
index 19624e6..eb9d625 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.layout.pager
+package com.android.compose.pager
 
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.DecayAnimationSpec
@@ -40,7 +40,6 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.filter
 
 /** Library-wide switch to turn on debug logging. */
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt
index 288c26e..2e6ae78 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.layout.pager
+package com.android.compose.pager
 
 import androidx.annotation.FloatRange
 import androidx.annotation.IntRange
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt
index 0b53f532..23122de 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.layout.pager
+package com.android.compose.pager
 
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationState
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/runtime/MovableContent.kt b/packages/SystemUI/compose/core/src/com/android/compose/runtime/MovableContent.kt
similarity index 96%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/runtime/MovableContent.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/runtime/MovableContent.kt
index 3f2f96b..228ba3d 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/runtime/MovableContent.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/runtime/MovableContent.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.runtime
+package com.android.compose.runtime
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.InternalComposeApi
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
similarity index 96%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
index caa7e5f..4f1657f 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.theme
+package com.android.compose.theme
 
 import android.annotation.ColorInt
 import android.content.Context
@@ -27,7 +27,7 @@
     staticCompositionLocalOf<AndroidColorScheme> {
         throw IllegalStateException(
             "No AndroidColorScheme configured. Make sure to use LocalAndroidColorScheme in a " +
-                "Composable surrounded by a SystemUITheme {}."
+                "Composable surrounded by a PlatformTheme {}."
         )
     }
 
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
similarity index 95%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
index de47cce..5dbaff6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.theme
+package com.android.compose.theme
 
 import android.annotation.AttrRes
 import androidx.compose.runtime.Composable
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
similarity index 77%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
index 00532f4..b4e90d6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.theme
+package com.android.compose.theme
 
 import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.material3.MaterialTheme
@@ -24,15 +24,15 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
-import com.android.systemui.compose.theme.typography.TypeScaleTokens
-import com.android.systemui.compose.theme.typography.TypefaceNames
-import com.android.systemui.compose.theme.typography.TypefaceTokens
-import com.android.systemui.compose.theme.typography.TypographyTokens
-import com.android.systemui.compose.theme.typography.systemUITypography
+import com.android.compose.theme.typography.TypeScaleTokens
+import com.android.compose.theme.typography.TypefaceNames
+import com.android.compose.theme.typography.TypefaceTokens
+import com.android.compose.theme.typography.TypographyTokens
+import com.android.compose.theme.typography.platformTypography
 
-/** The Material 3 theme that should wrap all SystemUI Composables. */
+/** The Material 3 theme that should wrap all Platform Composables. */
 @Composable
-fun SystemUITheme(
+fun PlatformTheme(
     isDarkTheme: Boolean = isSystemInDarkTheme(),
     content: @Composable () -> Unit,
 ) {
@@ -49,7 +49,7 @@
     val typefaceNames = remember(context) { TypefaceNames.get(context) }
     val typography =
         remember(typefaceNames) {
-            systemUITypography(TypographyTokens(TypeScaleTokens(TypefaceTokens(typefaceNames))))
+            platformTypography(TypographyTokens(TypeScaleTokens(TypefaceTokens(typefaceNames))))
         }
 
     MaterialTheme(colorScheme, typography = typography) {
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/SystemUITypography.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/PlatformTypography.kt
similarity index 91%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/SystemUITypography.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/PlatformTypography.kt
index 365f4bb..1ce1ae3 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/SystemUITypography.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/PlatformTypography.kt
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
 
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Typography
 
 /**
- * The SystemUI typography.
+ * The typography for Platform Compose code.
  *
  * Do not use directly and call [MaterialTheme.typography] instead to access the different text
  * styles.
  */
-internal fun systemUITypography(typographyTokens: TypographyTokens): Typography {
+internal fun platformTypography(typographyTokens: TypographyTokens): Typography {
     return Typography(
         displayLarge = typographyTokens.displayLarge,
         displayMedium = typographyTokens.displayMedium,
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypeScaleTokens.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypeScaleTokens.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypeScaleTokens.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypeScaleTokens.kt
index 537ba0b..78fcf5c 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypeScaleTokens.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypeScaleTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
 
 import androidx.compose.ui.unit.sp
 
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypefaceTokens.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypefaceTokens.kt
similarity index 97%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypefaceTokens.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypefaceTokens.kt
index f3d554f..13acfd6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypefaceTokens.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypefaceTokens.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalTextApi::class)
 
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
 
 import android.content.Context
 import androidx.compose.ui.text.ExperimentalTextApi
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypographyTokens.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypographyTokens.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypographyTokens.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypographyTokens.kt
index 55f3d1f..38aadb8 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypographyTokens.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypographyTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
 
 import androidx.compose.ui.text.TextStyle
 
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index f8023e2..6119e96 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -23,7 +23,7 @@
 
 // TODO(b/230606318): Make those host tests instead of device tests.
 android_test {
-    name: "SystemUIComposeCoreTests",
+    name: "PlatformComposeCoreTests",
     manifest: "AndroidManifest.xml",
     test_suites: ["device-tests"],
     sdk_version: "current",
@@ -34,7 +34,7 @@
     ],
 
     static_libs: [
-        "SystemUIComposeCore",
+        "PlatformComposeCore",
 
         "androidx.test.runner",
         "androidx.test.ext.junit",
diff --git a/packages/SystemUI/compose/core/tests/AndroidManifest.xml b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
index 729ab98..1016340 100644
--- a/packages/SystemUI/compose/core/tests/AndroidManifest.xml
+++ b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
@@ -15,14 +15,14 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemui.compose.core.tests" >
+    package="com.android.compose.core.tests" >
 
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.systemui.compose.core.tests"
-                     android:label="Tests for SystemUIComposeCore"/>
+                     android:targetPackage="com.android.compose.core.tests"
+                     android:label="Tests for PlatformComposeCore"/>
 
 </manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/SystemUIThemeTest.kt
similarity index 90%
rename from packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt
rename to packages/SystemUI/compose/core/tests/src/com/android/compose/theme/SystemUIThemeTest.kt
index 20249f6..9bc6856 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/SystemUIThemeTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.compose.theme
+package com.android.compose.theme
 
 import androidx.compose.material3.Text
 import androidx.compose.ui.test.assertIsDisplayed
@@ -32,7 +32,7 @@
 
     @Test
     fun testThemeShowsContent() {
-        composeRule.setContent { SystemUITheme { Text("foo") } }
+        composeRule.setContent { PlatformTheme { Text("foo") } }
 
         composeRule.onNodeWithText("foo").assertIsDisplayed()
     }
@@ -40,7 +40,7 @@
     @Test
     fun testAndroidColorsAreAvailableInsideTheme() {
         composeRule.setContent {
-            SystemUITheme { Text("foo", color = LocalAndroidColorScheme.current.colorAccent) }
+            PlatformTheme { Text("foo", color = LocalAndroidColorScheme.current.colorAccent) }
         }
 
         composeRule.onNodeWithText("foo").assertIsDisplayed()
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index 16294d9..6991ff8 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -18,7 +18,7 @@
 
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import com.android.systemui.compose.theme.SystemUITheme
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.people.ui.compose.PeopleScreen
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 
@@ -31,6 +31,6 @@
         viewModel: PeopleViewModel,
         onResult: (PeopleViewModel.Result) -> Unit,
     ) {
-        activity.setContent { SystemUITheme { PeopleScreen(viewModel, onResult) } }
+        activity.setContent { PlatformTheme { PeopleScreen(viewModel, onResult) } }
     }
 }
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index 4533330..8f8bb1b 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -31,7 +31,7 @@
 
     static_libs: [
         "SystemUI-core",
-        "SystemUIComposeCore",
+        "PlatformComposeCore",
 
         "androidx.compose.runtime_runtime",
         "androidx.compose.material3_material3",
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 4a56b02..23dacf9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -49,8 +49,8 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.R
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
index 5c9358f..3f590df 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
@@ -41,8 +41,8 @@
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.R
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
 
 @Composable
 internal fun PeopleScreenEmpty(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 654b723..5c5ceef 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -65,14 +65,14 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.animation.Expandable
+import com.android.compose.modifiers.background
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.compose.theme.colorAttr
 import com.android.systemui.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
-import com.android.systemui.compose.animation.Expandable
-import com.android.systemui.compose.modifiers.background
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.compose.theme.colorAttr
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
diff --git a/packages/SystemUI/compose/features/tests/AndroidManifest.xml b/packages/SystemUI/compose/features/tests/AndroidManifest.xml
index 2fa475d..2f41ea9 100644
--- a/packages/SystemUI/compose/features/tests/AndroidManifest.xml
+++ b/packages/SystemUI/compose/features/tests/AndroidManifest.xml
@@ -35,7 +35,7 @@
             android:enabled="false"
             tools:replace="android:authorities"
             tools:node="remove" />
-        <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+        <provider android:name="com.android.systemui.keyguard.CustomizationProvider"
             android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
             android:enabled="false"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/compose/testing/Android.bp b/packages/SystemUI/compose/testing/Android.bp
index 293e51f..555f42e 100644
--- a/packages/SystemUI/compose/testing/Android.bp
+++ b/packages/SystemUI/compose/testing/Android.bp
@@ -30,7 +30,7 @@
     ],
 
     static_libs: [
-        "SystemUIComposeCore",
+        "PlatformComposeCore",
         "SystemUIScreenshotLib",
 
         "androidx.compose.runtime_runtime",
diff --git a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
index 979e1a0..cb9e53c 100644
--- a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
+++ b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
@@ -22,7 +22,7 @@
 import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onRoot
-import com.android.systemui.compose.theme.SystemUITheme
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.testing.screenshot.ScreenshotActivity
 import com.android.systemui.testing.screenshot.SystemUIGoldenImagePathManager
 import com.android.systemui.testing.screenshot.UnitTestBitmapMatcher
@@ -79,7 +79,7 @@
         // Set the content using the AndroidComposeRule to make sure that the Activity is set up
         // correctly.
         composeRule.setContent {
-            SystemUITheme {
+            PlatformTheme {
                 Surface(
                     color = MaterialTheme.colorScheme.background,
                 ) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
similarity index 75%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
index 3213b2e..5bb3707 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.shared.quickaffordance.data.content
+package com.android.systemui.shared.customization.data.content
 
 import android.annotation.SuppressLint
 import android.content.ContentValues
@@ -25,7 +25,7 @@
 import android.graphics.drawable.Drawable
 import android.net.Uri
 import androidx.annotation.DrawableRes
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
@@ -35,7 +35,7 @@
 import kotlinx.coroutines.withContext
 
 /** Client for using a content provider implementing the [Contract]. */
-interface KeyguardQuickAffordanceProviderClient {
+interface CustomizationProviderClient {
 
     /**
      * Selects an affordance with the given ID for a slot on the lock screen with the given ID.
@@ -190,10 +190,10 @@
     )
 }
 
-class KeyguardQuickAffordanceProviderClientImpl(
+class CustomizationProviderClientImpl(
     private val context: Context,
     private val backgroundDispatcher: CoroutineDispatcher,
-) : KeyguardQuickAffordanceProviderClient {
+) : CustomizationProviderClient {
 
     override suspend fun insertSelection(
         slotId: String,
@@ -201,20 +201,23 @@
     ) {
         withContext(backgroundDispatcher) {
             context.contentResolver.insert(
-                Contract.SelectionTable.URI,
+                Contract.LockScreenQuickAffordances.SelectionTable.URI,
                 ContentValues().apply {
-                    put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
-                    put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
+                    put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
+                    put(
+                        Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
+                        affordanceId
+                    )
                 }
             )
         }
     }
 
-    override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
+    override suspend fun querySlots(): List<CustomizationProviderClient.Slot> {
         return withContext(backgroundDispatcher) {
             context.contentResolver
                 .query(
-                    Contract.SlotTable.URI,
+                    Contract.LockScreenQuickAffordances.SlotTable.URI,
                     null,
                     null,
                     null,
@@ -222,16 +225,21 @@
                 )
                 ?.use { cursor ->
                     buildList {
-                        val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
+                        val idColumnIndex =
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.SlotTable.Columns.ID
+                            )
                         val capacityColumnIndex =
-                            cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY
+                            )
                         if (idColumnIndex == -1 || capacityColumnIndex == -1) {
                             return@buildList
                         }
 
                         while (cursor.moveToNext()) {
                             add(
-                                KeyguardQuickAffordanceProviderClient.Slot(
+                                CustomizationProviderClient.Slot(
                                     id = cursor.getString(idColumnIndex),
                                     capacity = cursor.getInt(capacityColumnIndex),
                                 )
@@ -243,7 +251,7 @@
             ?: emptyList()
     }
 
-    override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
+    override suspend fun queryFlags(): List<CustomizationProviderClient.Flag> {
         return withContext(backgroundDispatcher) {
             context.contentResolver
                 .query(
@@ -265,7 +273,7 @@
 
                         while (cursor.moveToNext()) {
                             add(
-                                KeyguardQuickAffordanceProviderClient.Flag(
+                                CustomizationProviderClient.Flag(
                                     name = cursor.getString(nameColumnIndex),
                                     value = cursor.getInt(valueColumnIndex) == 1,
                                 )
@@ -277,20 +285,19 @@
             ?: emptyList()
     }
 
-    override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
-        return observeUri(Contract.SlotTable.URI).map { querySlots() }
+    override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
+        return observeUri(Contract.LockScreenQuickAffordances.SlotTable.URI).map { querySlots() }
     }
 
-    override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
+    override fun observeFlags(): Flow<List<CustomizationProviderClient.Flag>> {
         return observeUri(Contract.FlagsTable.URI).map { queryFlags() }
     }
 
-    override suspend fun queryAffordances():
-        List<KeyguardQuickAffordanceProviderClient.Affordance> {
+    override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
         return withContext(backgroundDispatcher) {
             context.contentResolver
                 .query(
-                    Contract.AffordanceTable.URI,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.URI,
                     null,
                     null,
                     null,
@@ -299,24 +306,36 @@
                 ?.use { cursor ->
                     buildList {
                         val idColumnIndex =
-                            cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID
+                            )
                         val nameColumnIndex =
-                            cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME
+                            )
                         val iconColumnIndex =
-                            cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON
+                            )
                         val isEnabledColumnIndex =
-                            cursor.getColumnIndex(Contract.AffordanceTable.Columns.IS_ENABLED)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+                                    .IS_ENABLED
+                            )
                         val enablementInstructionsColumnIndex =
                             cursor.getColumnIndex(
-                                Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS
+                                Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+                                    .ENABLEMENT_INSTRUCTIONS
                             )
                         val enablementActionTextColumnIndex =
                             cursor.getColumnIndex(
-                                Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT
+                                Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+                                    .ENABLEMENT_ACTION_TEXT
                             )
                         val enablementComponentNameColumnIndex =
                             cursor.getColumnIndex(
-                                Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME
+                                Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+                                    .ENABLEMENT_COMPONENT_NAME
                             )
                         if (
                             idColumnIndex == -1 ||
@@ -332,7 +351,7 @@
 
                         while (cursor.moveToNext()) {
                             add(
-                                KeyguardQuickAffordanceProviderClient.Affordance(
+                                CustomizationProviderClient.Affordance(
                                     id = cursor.getString(idColumnIndex),
                                     name = cursor.getString(nameColumnIndex),
                                     iconResourceId = cursor.getInt(iconColumnIndex),
@@ -341,7 +360,7 @@
                                         cursor
                                             .getString(enablementInstructionsColumnIndex)
                                             ?.split(
-                                                Contract.AffordanceTable
+                                                Contract.LockScreenQuickAffordances.AffordanceTable
                                                     .ENABLEMENT_INSTRUCTIONS_DELIMITER
                                             ),
                                     enablementActionText =
@@ -357,16 +376,17 @@
             ?: emptyList()
     }
 
-    override fun observeAffordances():
-        Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
-        return observeUri(Contract.AffordanceTable.URI).map { queryAffordances() }
+    override fun observeAffordances(): Flow<List<CustomizationProviderClient.Affordance>> {
+        return observeUri(Contract.LockScreenQuickAffordances.AffordanceTable.URI).map {
+            queryAffordances()
+        }
     }
 
-    override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
+    override suspend fun querySelections(): List<CustomizationProviderClient.Selection> {
         return withContext(backgroundDispatcher) {
             context.contentResolver
                 .query(
-                    Contract.SelectionTable.URI,
+                    Contract.LockScreenQuickAffordances.SelectionTable.URI,
                     null,
                     null,
                     null,
@@ -375,11 +395,19 @@
                 ?.use { cursor ->
                     buildList {
                         val slotIdColumnIndex =
-                            cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID
+                            )
                         val affordanceIdColumnIndex =
-                            cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.SelectionTable.Columns
+                                    .AFFORDANCE_ID
+                            )
                         val affordanceNameColumnIndex =
-                            cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
+                            cursor.getColumnIndex(
+                                Contract.LockScreenQuickAffordances.SelectionTable.Columns
+                                    .AFFORDANCE_NAME
+                            )
                         if (
                             slotIdColumnIndex == -1 ||
                                 affordanceIdColumnIndex == -1 ||
@@ -390,7 +418,7 @@
 
                         while (cursor.moveToNext()) {
                             add(
-                                KeyguardQuickAffordanceProviderClient.Selection(
+                                CustomizationProviderClient.Selection(
                                     slotId = cursor.getString(slotIdColumnIndex),
                                     affordanceId = cursor.getString(affordanceIdColumnIndex),
                                     affordanceName = cursor.getString(affordanceNameColumnIndex),
@@ -403,8 +431,10 @@
             ?: emptyList()
     }
 
-    override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
-        return observeUri(Contract.SelectionTable.URI).map { querySelections() }
+    override fun observeSelections(): Flow<List<CustomizationProviderClient.Selection>> {
+        return observeUri(Contract.LockScreenQuickAffordances.SelectionTable.URI).map {
+            querySelections()
+        }
     }
 
     override suspend fun deleteSelection(
@@ -413,9 +443,10 @@
     ) {
         withContext(backgroundDispatcher) {
             context.contentResolver.delete(
-                Contract.SelectionTable.URI,
-                "${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
-                    " ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
+                Contract.LockScreenQuickAffordances.SelectionTable.URI,
+                "${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
+                    " ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
+                    " = ?",
                 arrayOf(
                     slotId,
                     affordanceId,
@@ -429,8 +460,8 @@
     ) {
         withContext(backgroundDispatcher) {
             context.contentResolver.delete(
-                Contract.SelectionTable.URI,
-                Contract.SelectionTable.Columns.SLOT_ID,
+                Contract.LockScreenQuickAffordances.SelectionTable.URI,
+                Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
                 arrayOf(
                     slotId,
                 ),
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
new file mode 100644
index 0000000..1e2e7d2
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 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.systemui.shared.customization.data.content
+
+import android.content.ContentResolver
+import android.net.Uri
+
+/** Contract definitions for querying content about keyguard quick affordances. */
+object CustomizationProviderContract {
+
+    const val AUTHORITY = "com.android.systemui.customization"
+    const val PERMISSION = "android.permission.CUSTOMIZE_SYSTEM_UI"
+
+    private val BASE_URI: Uri =
+        Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
+
+    /** Namespace for lock screen shortcut (quick affordance) tables. */
+    object LockScreenQuickAffordances {
+
+        const val NAMESPACE = "lockscreen_quickaffordance"
+
+        private val LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI: Uri =
+            BASE_URI.buildUpon().path(NAMESPACE).build()
+
+        fun qualifiedTablePath(tableName: String): String {
+            return "$NAMESPACE/$tableName"
+        }
+
+        /**
+         * Table for slots.
+         *
+         * Slots are positions where affordances can be placed on the lock screen. Affordances that
+         * are placed on slots are said to be "selected". The system supports the idea of multiple
+         * affordances per slot, though the implementation may limit the number of affordances on
+         * each slot.
+         *
+         * Supported operations:
+         * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result
+         * set will contain rows with the [SlotTable.Columns] columns.
+         */
+        object SlotTable {
+            const val TABLE_NAME = "slots"
+            val URI: Uri =
+                LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
+
+            object Columns {
+                /** String. Unique ID for this slot. */
+                const val ID = "id"
+                /** Integer. The maximum number of affordances that can be placed in the slot. */
+                const val CAPACITY = "capacity"
+            }
+        }
+
+        /**
+         * Table for affordances.
+         *
+         * Affordances are actions/buttons that the user can execute. They are placed on slots on
+         * the lock screen.
+         *
+         * Supported operations:
+         * - Query - to know about all the affordances that are available on the device, regardless
+         * of which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result
+         * set will contain rows, each with the columns specified in [AffordanceTable.Columns].
+         */
+        object AffordanceTable {
+            const val TABLE_NAME = "affordances"
+            val URI: Uri =
+                LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
+            const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "]["
+            const val COMPONENT_NAME_SEPARATOR = "/"
+
+            object Columns {
+                /** String. Unique ID for this affordance. */
+                const val ID = "id"
+                /** String. User-visible name for this affordance. */
+                const val NAME = "name"
+                /**
+                 * Integer. Resource ID for the drawable to load for this affordance. This is a
+                 * resource ID from the system UI package.
+                 */
+                const val ICON = "icon"
+                /** Integer. `1` if the affordance is enabled or `0` if it disabled. */
+                const val IS_ENABLED = "is_enabled"
+                /**
+                 * String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be
+                 * shown to the user if the affordance is disabled and the user selects the
+                 * affordance.
+                 */
+                const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions"
+                /**
+                 * String. Optional label for a button that, when clicked, opens a destination
+                 * activity where the user can re-enable the disabled affordance.
+                 */
+                const val ENABLEMENT_ACTION_TEXT = "enablement_action_text"
+                /**
+                 * String. Optional package name and activity action string, delimited by
+                 * [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that
+                 * opens a destination where the user can re-enable the disabled affordance.
+                 */
+                const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent"
+            }
+        }
+
+        /**
+         * Table for selections.
+         *
+         * Selections are pairs of slot and affordance IDs.
+         *
+         * Supported operations:
+         * - Insert - to insert an affordance and place it in a slot, insert values for the columns
+         * into the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
+         * Selecting a new affordance for a slot that is already full will automatically remove the
+         * oldest affordance from the slot.
+         * - Query - to know which affordances are set on which slots, query the
+         * [SelectionTable.URI] [Uri]. The result set will contain rows, each of which with the
+         * columns from [SelectionTable.Columns].
+         * - Delete - to unselect an affordance, removing it from a slot, delete from the
+         * [SelectionTable.URI] [Uri], passing in values for each column.
+         */
+        object SelectionTable {
+            const val TABLE_NAME = "selections"
+            val URI: Uri =
+                LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
+
+            object Columns {
+                /** String. Unique ID for the slot. */
+                const val SLOT_ID = "slot_id"
+                /** String. Unique ID for the selected affordance. */
+                const val AFFORDANCE_ID = "affordance_id"
+                /** String. Human-readable name for the affordance. */
+                const val AFFORDANCE_NAME = "affordance_name"
+            }
+        }
+    }
+
+    /**
+     * Table for flags.
+     *
+     * Flags are key-value pairs.
+     *
+     * Supported operations:
+     * - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will
+     * contain rows, each of which with the columns from [FlagsTable.Columns].
+     */
+    object FlagsTable {
+        const val TABLE_NAME = "flags"
+        val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+        /** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
+        const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
+
+        /**
+         * Flag denoting whether the customizable lock screen quick affordances feature is enabled.
+         */
+        const val FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED =
+            "is_custom_lock_screen_quick_affordances_feature_enabled"
+
+        /** Flag denoting whether the customizable clocks feature is enabled. */
+        const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
+
+        object Columns {
+            /** String. Unique ID for the flag. */
+            const val NAME = "name"
+            /** Int. Value of the flag. `1` means `true` and `0` means `false`. */
+            const val VALUE = "value"
+        }
+    }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
similarity index 71%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
index ec5e703..f5a955d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.shared.quickaffordance.data.content
+package com.android.systemui.shared.customization.data.content
 
 import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.Drawable
@@ -25,46 +25,46 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 
-class FakeKeyguardQuickAffordanceProviderClient(
-    slots: List<KeyguardQuickAffordanceProviderClient.Slot> =
+class FakeCustomizationProviderClient(
+    slots: List<CustomizationProviderClient.Slot> =
         listOf(
-            KeyguardQuickAffordanceProviderClient.Slot(
+            CustomizationProviderClient.Slot(
                 id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
                 capacity = 1,
             ),
-            KeyguardQuickAffordanceProviderClient.Slot(
+            CustomizationProviderClient.Slot(
                 id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
                 capacity = 1,
             ),
         ),
-    affordances: List<KeyguardQuickAffordanceProviderClient.Affordance> =
+    affordances: List<CustomizationProviderClient.Affordance> =
         listOf(
-            KeyguardQuickAffordanceProviderClient.Affordance(
+            CustomizationProviderClient.Affordance(
                 id = AFFORDANCE_1,
                 name = AFFORDANCE_1,
                 iconResourceId = 1,
             ),
-            KeyguardQuickAffordanceProviderClient.Affordance(
+            CustomizationProviderClient.Affordance(
                 id = AFFORDANCE_2,
                 name = AFFORDANCE_2,
                 iconResourceId = 2,
             ),
-            KeyguardQuickAffordanceProviderClient.Affordance(
+            CustomizationProviderClient.Affordance(
                 id = AFFORDANCE_3,
                 name = AFFORDANCE_3,
                 iconResourceId = 3,
             ),
         ),
-    flags: List<KeyguardQuickAffordanceProviderClient.Flag> =
+    flags: List<CustomizationProviderClient.Flag> =
         listOf(
-            KeyguardQuickAffordanceProviderClient.Flag(
+            CustomizationProviderClient.Flag(
                 name =
-                    KeyguardQuickAffordanceProviderContract.FlagsTable
+                    CustomizationProviderContract.FlagsTable
                         .FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
                 value = true,
             )
         ),
-) : KeyguardQuickAffordanceProviderClient {
+) : CustomizationProviderClient {
 
     private val slots = MutableStateFlow(slots)
     private val affordances = MutableStateFlow(affordances)
@@ -85,37 +85,35 @@
         selections.value = selections.value.toMutableMap().apply { this[slotId] = affordances }
     }
 
-    override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
+    override suspend fun querySlots(): List<CustomizationProviderClient.Slot> {
         return slots.value
     }
 
-    override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
+    override suspend fun queryFlags(): List<CustomizationProviderClient.Flag> {
         return flags.value
     }
 
-    override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
+    override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
         return slots.asStateFlow()
     }
 
-    override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
+    override fun observeFlags(): Flow<List<CustomizationProviderClient.Flag>> {
         return flags.asStateFlow()
     }
 
-    override suspend fun queryAffordances():
-        List<KeyguardQuickAffordanceProviderClient.Affordance> {
+    override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
         return affordances.value
     }
 
-    override fun observeAffordances():
-        Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
+    override fun observeAffordances(): Flow<List<CustomizationProviderClient.Affordance>> {
         return affordances.asStateFlow()
     }
 
-    override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
+    override suspend fun querySelections(): List<CustomizationProviderClient.Selection> {
         return toSelectionList(selections.value, affordances.value)
     }
 
-    override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
+    override fun observeSelections(): Flow<List<CustomizationProviderClient.Selection>> {
         return combine(selections, affordances) { selections, affordances ->
             toSelectionList(selections, affordances)
         }
@@ -148,7 +146,7 @@
         flags.value =
             flags.value.toMutableList().apply {
                 removeIf { it.name == name }
-                add(KeyguardQuickAffordanceProviderClient.Flag(name = name, value = value))
+                add(CustomizationProviderClient.Flag(name = name, value = value))
             }
     }
 
@@ -157,29 +155,26 @@
             slots.value.toMutableList().apply {
                 val index = indexOfFirst { it.id == slotId }
                 check(index != -1) { "Slot with ID \"$slotId\" doesn't exist!" }
-                set(
-                    index,
-                    KeyguardQuickAffordanceProviderClient.Slot(id = slotId, capacity = capacity)
-                )
+                set(index, CustomizationProviderClient.Slot(id = slotId, capacity = capacity))
             }
     }
 
-    fun addAffordance(affordance: KeyguardQuickAffordanceProviderClient.Affordance): Int {
+    fun addAffordance(affordance: CustomizationProviderClient.Affordance): Int {
         affordances.value = affordances.value + listOf(affordance)
         return affordances.value.size - 1
     }
 
     private fun toSelectionList(
         selections: Map<String, List<String>>,
-        affordances: List<KeyguardQuickAffordanceProviderClient.Affordance>,
-    ): List<KeyguardQuickAffordanceProviderClient.Selection> {
+        affordances: List<CustomizationProviderClient.Affordance>,
+    ): List<CustomizationProviderClient.Selection> {
         return selections
             .map { (slotId, affordanceIds) ->
                 affordanceIds.map { affordanceId ->
                     val affordanceName =
                         affordances.find { it.id == affordanceId }?.name
                             ?: error("No affordance with ID of \"$affordanceId\"!")
-                    KeyguardQuickAffordanceProviderClient.Selection(
+                    CustomizationProviderClient.Selection(
                         slotId = slotId,
                         affordanceId = affordanceId,
                         affordanceName = affordanceName,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
deleted file mode 100644
index e197752..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2022 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.systemui.shared.quickaffordance.data.content
-
-import android.content.ContentResolver
-import android.net.Uri
-
-/** Contract definitions for querying content about keyguard quick affordances. */
-object KeyguardQuickAffordanceProviderContract {
-
-    const val AUTHORITY = "com.android.systemui.keyguard.quickaffordance"
-    const val PERMISSION = "android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
-
-    private val BASE_URI: Uri =
-        Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
-
-    /**
-     * Table for slots.
-     *
-     * Slots are positions where affordances can be placed on the lock screen. Affordances that are
-     * placed on slots are said to be "selected". The system supports the idea of multiple
-     * affordances per slot, though the implementation may limit the number of affordances on each
-     * slot.
-     *
-     * Supported operations:
-     * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result set
-     * will contain rows with the [SlotTable.Columns] columns.
-     */
-    object SlotTable {
-        const val TABLE_NAME = "slots"
-        val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
-
-        object Columns {
-            /** String. Unique ID for this slot. */
-            const val ID = "id"
-            /** Integer. The maximum number of affordances that can be placed in the slot. */
-            const val CAPACITY = "capacity"
-        }
-    }
-
-    /**
-     * Table for affordances.
-     *
-     * Affordances are actions/buttons that the user can execute. They are placed on slots on the
-     * lock screen.
-     *
-     * Supported operations:
-     * - Query - to know about all the affordances that are available on the device, regardless of
-     * which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result set will
-     * contain rows, each with the columns specified in [AffordanceTable.Columns].
-     */
-    object AffordanceTable {
-        const val TABLE_NAME = "affordances"
-        val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
-        const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "]["
-        const val COMPONENT_NAME_SEPARATOR = "/"
-
-        object Columns {
-            /** String. Unique ID for this affordance. */
-            const val ID = "id"
-            /** String. User-visible name for this affordance. */
-            const val NAME = "name"
-            /**
-             * Integer. Resource ID for the drawable to load for this affordance. This is a resource
-             * ID from the system UI package.
-             */
-            const val ICON = "icon"
-            /** Integer. `1` if the affordance is enabled or `0` if it disabled. */
-            const val IS_ENABLED = "is_enabled"
-            /**
-             * String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be shown
-             * to the user if the affordance is disabled and the user selects the affordance. The
-             * first one is a title while the rest are the steps needed to re-enable the affordance.
-             */
-            const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions"
-            /**
-             * String. Optional label for a button that, when clicked, opens a destination activity
-             * where the user can re-enable the disabled affordance.
-             */
-            const val ENABLEMENT_ACTION_TEXT = "enablement_action_text"
-            /**
-             * String. Optional package name and activity action string, delimited by
-             * [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that opens a
-             * destination where the user can re-enable the disabled affordance.
-             */
-            const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent"
-        }
-    }
-
-    /**
-     * Table for selections.
-     *
-     * Selections are pairs of slot and affordance IDs.
-     *
-     * Supported operations:
-     * - Insert - to insert an affordance and place it in a slot, insert values for the columns into
-     * the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
-     * Selecting a new affordance for a slot that is already full will automatically remove the
-     * oldest affordance from the slot.
-     * - Query - to know which affordances are set on which slots, query the [SelectionTable.URI]
-     * [Uri]. The result set will contain rows, each of which with the columns from
-     * [SelectionTable.Columns].
-     * - Delete - to unselect an affordance, removing it from a slot, delete from the
-     * [SelectionTable.URI] [Uri], passing in values for each column.
-     */
-    object SelectionTable {
-        const val TABLE_NAME = "selections"
-        val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
-
-        object Columns {
-            /** String. Unique ID for the slot. */
-            const val SLOT_ID = "slot_id"
-            /** String. Unique ID for the selected affordance. */
-            const val AFFORDANCE_ID = "affordance_id"
-            /** String. Human-readable name for the affordance. */
-            const val AFFORDANCE_NAME = "affordance_name"
-        }
-    }
-
-    /**
-     * Table for flags.
-     *
-     * Flags are key-value pairs.
-     *
-     * Supported operations:
-     * - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will
-     * contain rows, each of which with the columns from [FlagsTable.Columns].
-     */
-    object FlagsTable {
-        const val TABLE_NAME = "flags"
-        val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
-
-        /** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
-        const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
-
-        /**
-         * Flag denoting whether the customizable lock screen quick affordances feature is enabled.
-         */
-        const val FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED =
-            "is_custom_lock_screen_quick_affordances_feature_enabled"
-
-        /** Flag denoting whether the customizable clocks feature is enabled. */
-        const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
-
-        object Columns {
-            /** String. Unique ID for the flag. */
-            const val NAME = "name"
-            /** Int. Value of the flag. `1` means `true` and `0` means `false`. */
-            const val VALUE = "value"
-        }
-    }
-}
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index 01d4f00..ccb35fa 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -31,7 +31,7 @@
 ### Accessing Quick Affordance Data
 Quick Affordances structured data are exposed to other applications through the `KeyguardQuickAffordanceProvider` content provider which is owned by the System UI process.
 
-To access this content provider, applications must have the `android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES` permission which is a signature and privileged permission, limiting access to system apps or apps signed by the same signature as System UI. The `KeyguardQuickAffordanceProviderContract` file defines the content provider schema for consumers.
+To access this content provider, applications must have the `android.permission.CUSTOMIZE_SYSTEM_UI` permission which is a signature and privileged permission, limiting access to system apps or apps signed by the same signature as System UI. The `KeyguardQuickAffordanceProviderContract` file defines the content provider schema for consumers.
 
 Generally speaking, there are three important tables served by the content provider: `slots`, `affordances`, and `selections`. There is also a `flags` table, but that's not important and may be ignored.
 
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index b75c5c7..434f227 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -460,7 +460,7 @@
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiActivityModel.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
@@ -752,7 +752,7 @@
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/ShadeExpansionStateManagerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 29369cd..200c478 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2707,12 +2707,6 @@
     <string name="keyguard_affordance_enablement_dialog_action_template">Open <xliff:g id="appName" example="Wallet">%1$s</xliff:g></string>
 
     <!--
-    Template for a message shown right before a list of instructions that tell the user what to do
-    in order to enable a shortcut to a specific app. [CHAR LIMIT=NONE]
-    -->
-    <string name="keyguard_affordance_enablement_dialog_message">To add the <xliff:g id="appName" example="Wallet">%1$s</xliff:g> app as a shortcut, make sure</string>
-
-    <!--
     Requirement for the wallet app to be available for the user to use. This is shown as part of a
     bulleted list of requirements. When all requirements are met, the app can be accessed through a
     shortcut button on the lock screen. [CHAR LIMIT=NONE].
@@ -2748,10 +2742,10 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2">&#8226; At least one device is available</string>
 
     <!--
-    Error message shown when a button should be pressed and held to activate it, usually shown when
-    the user attempted to tap the button or held it for too short a time. [CHAR LIMIT=32].
+    Error message shown when a shortcut must be pressed and held to activate it, usually shown when
+    the user tried to tap the shortcut or held it for too short a time. [CHAR LIMIT=32].
     -->
-    <string name="keyguard_affordance_press_too_short">Touch &amp; hold to open</string>
+    <string name="keyguard_affordance_press_too_short">Touch &amp; hold shortcut</string>
 
     <!-- Text for education page of cancel button to hide the page. [CHAR_LIMIT=NONE] -->
     <string name="rear_display_bottom_sheet_cancel">Cancel</string>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index b30e0c2..a431a59 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.dagger;
 
-import com.android.systemui.keyguard.KeyguardQuickAffordanceProvider;
+import com.android.systemui.keyguard.CustomizationProvider;
 import com.android.systemui.statusbar.NotificationInsetsModule;
 import com.android.systemui.statusbar.QsFrameTranslateModule;
 
@@ -49,5 +49,5 @@
     /**
      * Member injection into the supplied argument.
      */
-    void inject(KeyguardQuickAffordanceProvider keyguardQuickAffordanceProvider);
+    void inject(CustomizationProvider customizationProvider);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index cbcede0..eaf1081 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -33,11 +33,11 @@
 import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
 import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
 import javax.inject.Inject
 import kotlinx.coroutines.runBlocking
 
-class KeyguardQuickAffordanceProvider :
+class CustomizationProvider :
     ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
 
     @Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
@@ -49,17 +49,23 @@
         UriMatcher(UriMatcher.NO_MATCH).apply {
             addURI(
                 Contract.AUTHORITY,
-                Contract.SlotTable.TABLE_NAME,
+                Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                    Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+                ),
                 MATCH_CODE_ALL_SLOTS,
             )
             addURI(
                 Contract.AUTHORITY,
-                Contract.AffordanceTable.TABLE_NAME,
+                Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                    Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+                ),
                 MATCH_CODE_ALL_AFFORDANCES,
             )
             addURI(
                 Contract.AUTHORITY,
-                Contract.SelectionTable.TABLE_NAME,
+                Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                    Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+                ),
                 MATCH_CODE_ALL_SELECTIONS,
             )
             addURI(
@@ -94,9 +100,18 @@
 
         val tableName =
             when (uriMatcher.match(uri)) {
-                MATCH_CODE_ALL_SLOTS -> Contract.SlotTable.TABLE_NAME
-                MATCH_CODE_ALL_AFFORDANCES -> Contract.AffordanceTable.TABLE_NAME
-                MATCH_CODE_ALL_SELECTIONS -> Contract.SelectionTable.TABLE_NAME
+                MATCH_CODE_ALL_SLOTS ->
+                    Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                        Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+                    )
+                MATCH_CODE_ALL_AFFORDANCES ->
+                    Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                        Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+                    )
+                MATCH_CODE_ALL_SELECTIONS ->
+                    Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                        Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+                    )
                 MATCH_CODE_ALL_FLAGS -> Contract.FlagsTable.TABLE_NAME
                 else -> null
             }
@@ -174,22 +189,34 @@
             throw IllegalArgumentException("Cannot insert selection, no values passed in!")
         }
 
-        if (!values.containsKey(Contract.SelectionTable.Columns.SLOT_ID)) {
+        if (
+            !values.containsKey(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID)
+        ) {
             throw IllegalArgumentException(
                 "Cannot insert selection, " +
-                    "\"${Contract.SelectionTable.Columns.SLOT_ID}\" not specified!"
+                    "\"${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID}\"" +
+                    " not specified!"
             )
         }
 
-        if (!values.containsKey(Contract.SelectionTable.Columns.AFFORDANCE_ID)) {
+        if (
+            !values.containsKey(
+                Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
+            )
+        ) {
             throw IllegalArgumentException(
                 "Cannot insert selection, " +
-                    "\"${Contract.SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
+                    "\"${Contract.LockScreenQuickAffordances
+                        .SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
             )
         }
 
-        val slotId = values.getAsString(Contract.SelectionTable.Columns.SLOT_ID)
-        val affordanceId = values.getAsString(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+        val slotId =
+            values.getAsString(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID)
+        val affordanceId =
+            values.getAsString(
+                Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
+            )
 
         if (slotId.isNullOrEmpty()) {
             throw IllegalArgumentException("Cannot insert selection, slot ID was empty!")
@@ -207,8 +234,10 @@
 
         return if (success) {
             Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
-            context?.contentResolver?.notifyChange(Contract.SelectionTable.URI, null)
-            Contract.SelectionTable.URI
+            context
+                ?.contentResolver
+                ?.notifyChange(Contract.LockScreenQuickAffordances.SelectionTable.URI, null)
+            Contract.LockScreenQuickAffordances.SelectionTable.URI
         } else {
             Log.d(TAG, "Failed to select $affordanceId for slot $slotId")
             null
@@ -218,9 +247,9 @@
     private suspend fun querySelections(): Cursor {
         return MatrixCursor(
                 arrayOf(
-                    Contract.SelectionTable.Columns.SLOT_ID,
-                    Contract.SelectionTable.Columns.AFFORDANCE_ID,
-                    Contract.SelectionTable.Columns.AFFORDANCE_NAME,
+                    Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
+                    Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
+                    Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_NAME,
                 )
             )
             .apply {
@@ -243,13 +272,16 @@
     private suspend fun queryAffordances(): Cursor {
         return MatrixCursor(
                 arrayOf(
-                    Contract.AffordanceTable.Columns.ID,
-                    Contract.AffordanceTable.Columns.NAME,
-                    Contract.AffordanceTable.Columns.ICON,
-                    Contract.AffordanceTable.Columns.IS_ENABLED,
-                    Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS,
-                    Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT,
-                    Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.Columns.IS_ENABLED,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+                        .ENABLEMENT_INSTRUCTIONS,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+                        .ENABLEMENT_ACTION_TEXT,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+                        .ENABLEMENT_COMPONENT_NAME,
                 )
             )
             .apply {
@@ -261,7 +293,8 @@
                             representation.iconResourceId,
                             if (representation.isEnabled) 1 else 0,
                             representation.instructions?.joinToString(
-                                Contract.AffordanceTable.ENABLEMENT_INSTRUCTIONS_DELIMITER
+                                Contract.LockScreenQuickAffordances.AffordanceTable
+                                    .ENABLEMENT_INSTRUCTIONS_DELIMITER
                             ),
                             representation.actionText,
                             representation.actionComponentName,
@@ -274,8 +307,8 @@
     private fun querySlots(): Cursor {
         return MatrixCursor(
                 arrayOf(
-                    Contract.SlotTable.Columns.ID,
-                    Contract.SlotTable.Columns.CAPACITY,
+                    Contract.LockScreenQuickAffordances.SlotTable.Columns.ID,
+                    Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY,
                 )
             )
             .apply {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 394426d..09e5ec0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -81,10 +81,6 @@
                 instructions =
                     listOf(
                         context.getString(
-                            R.string.keyguard_affordance_enablement_dialog_message,
-                            pickerName,
-                        ),
-                        context.getString(
                             R.string.keyguard_affordance_enablement_dialog_home_instruction_1
                         ),
                         context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 02ebcd3..20588e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
 import kotlinx.coroutines.flow.Flow
 
 /** Defines interface that can act as data source for a single quick affordance model. */
@@ -90,8 +90,9 @@
              * the user to be able to set up the quick affordance and make it enabled.
              *
              * This is either just an action for the `Intent` or a package name and action,
-             * separated by [Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR] for convenience, you
-             * can use the [componentName] function.
+             * separated by
+             * [Contract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR] for
+             * convenience, you can use the [componentName] function.
              */
             val actionComponentName: String? = null,
         ) : PickerScreenState() {
@@ -145,8 +146,8 @@
 
         /**
          * Returning this as a result from the [onTriggered] method means that the implementation
-         * has _not_ taken care of the action and the system should show a Dialog using the
-         * given [AlertDialog] and [Expandable].
+         * has _not_ taken care of the action and the system should show a Dialog using the given
+         * [AlertDialog] and [Expandable].
          */
         data class ShowDialog(
             val dialog: AlertDialog,
@@ -162,7 +163,8 @@
             return when {
                 action.isNullOrEmpty() -> null
                 !packageName.isNullOrEmpty() ->
-                    "$packageName${Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR}$action"
+                    "$packageName${Contract.LockScreenQuickAffordances.AffordanceTable
+                        .COMPONENT_NAME_SEPARATOR}$action"
                 else -> action
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
index 727a813..60ef116 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
@@ -19,13 +19,13 @@
 
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 
 interface KeyguardQuickAffordanceProviderClientFactory {
-    fun create(): KeyguardQuickAffordanceProviderClient
+    fun create(): CustomizationProviderClient
 }
 
 class KeyguardQuickAffordanceProviderClientFactoryImpl
@@ -34,8 +34,8 @@
     private val userTracker: UserTracker,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : KeyguardQuickAffordanceProviderClientFactory {
-    override fun create(): KeyguardQuickAffordanceProviderClient {
-        return KeyguardQuickAffordanceProviderClientImpl(
+    override fun create(): CustomizationProviderClient {
+        return CustomizationProviderClientImpl(
             context = userTracker.userContext,
             backgroundDispatcher = backgroundDispatcher,
         )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
index 8ffef25..e9bd889 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -69,7 +69,7 @@
         awaitClose { userTracker.removeCallback(callback) }
     }
 
-    private val clientOrNull: StateFlow<KeyguardQuickAffordanceProviderClient?> =
+    private val clientOrNull: StateFlow<CustomizationProviderClient?> =
         userId
             .distinctUntilChanged()
             .map { selectedUserId ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index a96ce77..4f7990f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -84,10 +84,6 @@
                     instructions =
                         listOf(
                             context.getString(
-                                R.string.keyguard_affordance_enablement_dialog_message,
-                                pickerName,
-                            ),
-                            context.getString(
                                 R.string
                                     .keyguard_affordance_enablement_dialog_qr_scanner_instruction
                             ),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index beb20ce..1928f40 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -118,10 +118,6 @@
                     instructions =
                         listOf(
                             context.getString(
-                                R.string.keyguard_affordance_enablement_dialog_message,
-                                pickerName,
-                            ),
-                            context.getString(
                                 R.string.keyguard_affordance_enablement_dialog_wallet_instruction_1
                             ),
                             context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9772cb9..c219380 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index b19795c..0e4058b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -392,6 +392,7 @@
                                     }
                                     view.performClick()
                                     view.setOnClickListener(null)
+                                    cancel()
                                 }
                         true
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
index 1aa954f..012b9ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
@@ -51,6 +51,16 @@
      */
     val operatorAlphaShort: String? = null,
 
+    /**
+     * TODO (b/263167683): Clarify this field
+     *
+     * This check comes from [com.android.settingslib.Utils.isInService]. It is intended to be a
+     * mapping from a ServiceState to a notion of connectivity. Notably, it will consider a
+     * connection to be in-service if either the voice registration state is IN_SERVICE or the data
+     * registration state is IN_SERVICE and NOT IWLAN.
+     */
+    val isInService: Boolean = false,
+
     /** Fields below from [SignalStrengthsListener.onSignalStrengthsChanged] */
     val isGsm: Boolean = false,
     @IntRange(from = 0, to = 4)
@@ -99,6 +109,10 @@
             row.logChange(COL_OPERATOR, operatorAlphaShort)
         }
 
+        if (prevVal.isInService != isInService) {
+            row.logChange(COL_IS_IN_SERVICE, isInService)
+        }
+
         if (prevVal.isGsm != isGsm) {
             row.logChange(COL_IS_GSM, isGsm)
         }
@@ -129,6 +143,7 @@
         row.logChange(COL_EMERGENCY, isEmergencyOnly)
         row.logChange(COL_ROAMING, isRoaming)
         row.logChange(COL_OPERATOR, operatorAlphaShort)
+        row.logChange(COL_IS_IN_SERVICE, isInService)
         row.logChange(COL_IS_GSM, isGsm)
         row.logChange(COL_CDMA_LEVEL, cdmaLevel)
         row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
@@ -141,6 +156,7 @@
         const val COL_EMERGENCY = "EmergencyOnly"
         const val COL_ROAMING = "Roaming"
         const val COL_OPERATOR = "OperatorName"
+        const val COL_IS_IN_SERVICE = "IsInService"
         const val COL_IS_GSM = "IsGsm"
         const val COL_CDMA_LEVEL = "CdmaLevel"
         const val COL_PRIMARY_LEVEL = "PrimaryLevel"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index b252de8..0b5f9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -247,6 +247,7 @@
         return MobileConnectionModel(
             isEmergencyOnly = false, // TODO(b/261029387): not yet supported
             isRoaming = roaming,
+            isInService = (level ?: 0) > 0,
             isGsm = false, // TODO(b/261029387): not yet supported
             cdmaLevel = level ?: 0,
             primaryLevel = level ?: 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 0b9e158..5cfff82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -32,6 +32,7 @@
 import android.telephony.TelephonyManager.ERI_OFF
 import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
+import com.android.settingslib.Utils
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Application
@@ -117,6 +118,7 @@
                                     isEmergencyOnly = serviceState.isEmergencyOnly,
                                     isRoaming = serviceState.roaming,
                                     operatorAlphaShort = serviceState.operatorAlphaShort,
+                                    isInService = Utils.isInService(serviceState),
                                 )
                             trySend(state)
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index e6686dc..31ac7a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -48,6 +48,9 @@
     /** True when telephony tells us that the data state is CONNECTED */
     val isDataConnected: StateFlow<Boolean>
 
+    /** True if we consider this connection to be in service, i.e. can make calls */
+    val isInService: StateFlow<Boolean>
+
     // TODO(b/256839546): clarify naming of default vs active
     /** True if we want to consider the data connection enabled */
     val isDefaultDataEnabled: StateFlow<Boolean>
@@ -58,6 +61,9 @@
     /** True if the RAT icon should always be displayed and false otherwise. */
     val alwaysShowDataRatIcon: StateFlow<Boolean>
 
+    /** True if the CDMA level should be preferred over the primary level. */
+    val alwaysUseCdmaLevel: StateFlow<Boolean>
+
     /** Observable for RAT type (network type) indicator */
     val networkTypeIconGroup: StateFlow<MobileIconGroup>
 
@@ -94,6 +100,7 @@
     @Application scope: CoroutineScope,
     defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
     override val alwaysShowDataRatIcon: StateFlow<Boolean>,
+    override val alwaysUseCdmaLevel: StateFlow<Boolean>,
     defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
     defaultMobileIconGroup: StateFlow<MobileIconGroup>,
     override val isDefaultConnectionFailed: StateFlow<Boolean>,
@@ -154,13 +161,12 @@
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val level: StateFlow<Int> =
-        connectionInfo
-            .mapLatest { connection ->
-                // TODO: incorporate [MobileMappings.Config.alwaysShowCdmaRssi]
-                if (connection.isGsm) {
-                    connection.primaryLevel
-                } else {
-                    connection.cdmaLevel
+        combine(connectionInfo, alwaysUseCdmaLevel) { connection, alwaysUseCdmaLevel ->
+                when {
+                    // GSM connections should never use the CDMA level
+                    connection.isGsm -> connection.primaryLevel
+                    alwaysUseCdmaLevel -> connection.cdmaLevel
+                    else -> connection.primaryLevel
                 }
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
@@ -175,4 +181,9 @@
         connectionInfo
             .mapLatest { connection -> connection.dataConnectionState == Connected }
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val isInService =
+        connectionRepository.connectionInfo
+            .mapLatest { it.isInService }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 21f6d8e..83da1dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -55,8 +55,13 @@
     val filteredSubscriptions: Flow<List<SubscriptionModel>>
     /** True if the active mobile data subscription has data enabled */
     val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
+
     /** True if the RAT icon should always be displayed and false otherwise. */
     val alwaysShowDataRatIcon: StateFlow<Boolean>
+
+    /** True if the CDMA level should be preferred over the primary level. */
+    val alwaysUseCdmaLevel: StateFlow<Boolean>
+
     /** The icon mapping from network type to [MobileIconGroup] for the default subscription */
     val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
     /** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
@@ -165,6 +170,11 @@
             .mapLatest { it.alwaysShowDataRatIcon }
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
+    override val alwaysUseCdmaLevel: StateFlow<Boolean> =
+        mobileConnectionsRepo.defaultDataSubRatConfig
+            .mapLatest { it.alwaysShowCdmaRssi }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
     /** If there is no mapping in [defaultMobileIconMapping], then use this default icon group */
     override val defaultMobileIconGroup: StateFlow<MobileIconGroup> =
         mobileConnectionsRepo.defaultMobileIconGroup.stateIn(
@@ -196,6 +206,7 @@
             scope,
             activeDataConnectionHasDataEnabled,
             alwaysShowDataRatIcon,
+            alwaysUseCdmaLevel,
             defaultMobileIconMapping,
             defaultMobileIconGroup,
             isDefaultConnectionFailed,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 2d6ac4e..a2117c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -80,11 +80,17 @@
 
     override val iconId: Flow<Int> = run {
         val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
-        combine(iconInteractor.level, iconInteractor.numberOfLevels, showExclamationMark) {
-                level,
-                numberOfLevels,
-                showExclamationMark ->
-                SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+        combine(
+                iconInteractor.level,
+                iconInteractor.numberOfLevels,
+                showExclamationMark,
+                iconInteractor.isInService,
+            ) { level, numberOfLevels, showExclamationMark, isInService ->
+                if (!isInService) {
+                    SignalDrawable.getEmptyState(numberOfLevels)
+                } else {
+                    SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+                }
             }
             .distinctUntilChanged()
             .logDiffsForTable(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index 5ccd6f4..53525f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -16,51 +16,9 @@
 
 package com.android.systemui.statusbar.pipeline.wifi.data.repository
 
-import android.annotation.SuppressLint
-import android.content.IntentFilter
-import android.net.ConnectivityManager
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
-import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
-import android.net.NetworkRequest
-import android.net.wifi.WifiInfo
-import android.net.wifi.WifiManager
-import android.net.wifi.WifiManager.TrafficStateCallback
-import android.util.Log
-import com.android.settingslib.Utils
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
-import java.util.concurrent.Executor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.stateIn
 
 /** Provides data related to the wifi state. */
 interface WifiRepository {
@@ -76,250 +34,3 @@
     /** Observable for the current wifi network activity. */
     val wifiActivity: StateFlow<DataActivityModel>
 }
-
-/** Real implementation of [WifiRepository]. */
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
-@SuppressLint("MissingPermission")
-class WifiRepositoryImpl @Inject constructor(
-    broadcastDispatcher: BroadcastDispatcher,
-    connectivityManager: ConnectivityManager,
-    logger: ConnectivityPipelineLogger,
-    @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
-    @Main mainExecutor: Executor,
-    @Application scope: CoroutineScope,
-    wifiManager: WifiManager?,
-) : WifiRepository {
-
-    private val wifiStateChangeEvents: Flow<Unit> = broadcastDispatcher.broadcastFlow(
-        IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)
-    )
-        .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
-
-    private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
-        MutableSharedFlow(extraBufferCapacity = 1)
-
-    override val isWifiEnabled: StateFlow<Boolean> =
-        if (wifiManager == null) {
-            MutableStateFlow(false).asStateFlow()
-        } else {
-            // Because [WifiManager] doesn't expose a wifi enabled change listener, we do it
-            // internally by fetching [WifiManager.isWifiEnabled] whenever we think the state may
-            // have changed.
-            merge(wifiNetworkChangeEvents, wifiStateChangeEvents)
-                .mapLatest { wifiManager.isWifiEnabled }
-                .distinctUntilChanged()
-                .logDiffsForTable(
-                    wifiTableLogBuffer,
-                    columnPrefix = "",
-                    columnName = "isWifiEnabled",
-                    initialValue = wifiManager.isWifiEnabled,
-                )
-                .stateIn(
-                    scope = scope,
-                    started = SharingStarted.WhileSubscribed(),
-                    initialValue = wifiManager.isWifiEnabled
-                )
-        }
-
-    override val isWifiDefault: StateFlow<Boolean> = conflatedCallbackFlow {
-        // Note: This callback doesn't do any logging because we already log every network change
-        // in the [wifiNetwork] callback.
-        val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
-            override fun onCapabilitiesChanged(
-                network: Network,
-                networkCapabilities: NetworkCapabilities
-            ) {
-                // This method will always be called immediately after the network becomes the
-                // default, in addition to any time the capabilities change while the network is
-                // the default.
-                // If this network contains valid wifi info, then wifi is the default network.
-                val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
-                trySend(wifiInfo != null)
-            }
-
-            override fun onLost(network: Network) {
-                // The system no longer has a default network, so wifi is definitely not default.
-                trySend(false)
-            }
-        }
-
-        connectivityManager.registerDefaultNetworkCallback(callback)
-        awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
-    }
-        .distinctUntilChanged()
-        .logDiffsForTable(
-            wifiTableLogBuffer,
-            columnPrefix = "",
-            columnName = "isWifiDefault",
-            initialValue = false,
-        )
-        .stateIn(
-            scope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = false
-        )
-
-    override val wifiNetwork: StateFlow<WifiNetworkModel> = conflatedCallbackFlow {
-        var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
-
-        val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
-            override fun onCapabilitiesChanged(
-                network: Network,
-                networkCapabilities: NetworkCapabilities
-            ) {
-                logger.logOnCapabilitiesChanged(network, networkCapabilities)
-
-                wifiNetworkChangeEvents.tryEmit(Unit)
-
-                val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
-                if (wifiInfo?.isPrimary == true) {
-                    val wifiNetworkModel = createWifiNetworkModel(
-                        wifiInfo,
-                        network,
-                        networkCapabilities,
-                        wifiManager,
-                    )
-                    logger.logTransformation(
-                        WIFI_NETWORK_CALLBACK_NAME,
-                        oldValue = currentWifi,
-                        newValue = wifiNetworkModel
-                    )
-                    currentWifi = wifiNetworkModel
-                    trySend(wifiNetworkModel)
-                }
-            }
-
-            override fun onLost(network: Network) {
-                logger.logOnLost(network)
-
-                wifiNetworkChangeEvents.tryEmit(Unit)
-
-                val wifi = currentWifi
-                if (wifi is WifiNetworkModel.Active && wifi.networkId == network.getNetId()) {
-                    val newNetworkModel = WifiNetworkModel.Inactive
-                    logger.logTransformation(
-                        WIFI_NETWORK_CALLBACK_NAME,
-                        oldValue = wifi,
-                        newValue = newNetworkModel
-                    )
-                    currentWifi = newNetworkModel
-                    trySend(newNetworkModel)
-                }
-            }
-        }
-
-        connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
-
-        awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
-    }
-        .distinctUntilChanged()
-        .logDiffsForTable(
-            wifiTableLogBuffer,
-            columnPrefix = "wifiNetwork",
-            initialValue = WIFI_NETWORK_DEFAULT,
-        )
-        // There will be multiple wifi icons in different places that will frequently
-        // subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
-        // new subscribes will get the latest value immediately upon subscription. Otherwise, the
-        // views could show stale data. See b/244173280.
-        .stateIn(
-            scope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = WIFI_NETWORK_DEFAULT
-        )
-
-    override val wifiActivity: StateFlow<DataActivityModel> =
-            if (wifiManager == null) {
-                Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
-                flowOf(ACTIVITY_DEFAULT)
-            } else {
-                conflatedCallbackFlow {
-                    val callback = TrafficStateCallback { state ->
-                        logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
-                        trySend(state.toWifiDataActivityModel())
-                    }
-                    wifiManager.registerTrafficStateCallback(mainExecutor, callback)
-                    awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
-                }
-            }
-                .logDiffsForTable(
-                    wifiTableLogBuffer,
-                    columnPrefix = ACTIVITY_PREFIX,
-                    initialValue = ACTIVITY_DEFAULT,
-                )
-                .stateIn(
-                    scope,
-                    started = SharingStarted.WhileSubscribed(),
-                    initialValue = ACTIVITY_DEFAULT
-                )
-
-    companion object {
-        private const val ACTIVITY_PREFIX = "wifiActivity"
-
-        val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
-        // Start out with no known wifi network.
-        // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
-        // initial fetch to get a starting wifi network. But, it uses a deprecated API
-        // [WifiManager.getConnectionInfo()], and the deprecation doc indicates to just use
-        // [ConnectivityManager.NetworkCallback] results instead. So, for now we'll just rely on the
-        // NetworkCallback inside [wifiNetwork] for our wifi network information.
-        val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
-
-        private fun networkCapabilitiesToWifiInfo(
-            networkCapabilities: NetworkCapabilities
-        ): WifiInfo? {
-            return when {
-                networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
-                    networkCapabilities.transportInfo as WifiInfo?
-                networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
-                    // Sometimes, cellular networks can act as wifi networks (known as VCN --
-                    // virtual carrier network). So, see if this cellular network has wifi info.
-                    Utils.tryGetWifiInfoForVcn(networkCapabilities)
-                else -> null
-            }
-        }
-
-        private fun createWifiNetworkModel(
-            wifiInfo: WifiInfo,
-            network: Network,
-            networkCapabilities: NetworkCapabilities,
-            wifiManager: WifiManager?,
-        ): WifiNetworkModel {
-            return if (wifiInfo.isCarrierMerged) {
-                WifiNetworkModel.CarrierMerged
-            } else {
-                WifiNetworkModel.Active(
-                        network.getNetId(),
-                        isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED),
-                        level = wifiManager?.calculateSignalLevel(wifiInfo.rssi),
-                        wifiInfo.ssid,
-                        wifiInfo.isPasspointAp,
-                        wifiInfo.isOsuAp,
-                        wifiInfo.passpointProviderFriendlyName
-                )
-            }
-        }
-
-        private fun prettyPrintActivity(activity: Int): String {
-            return when (activity) {
-                TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
-                TrafficStateCallback.DATA_ACTIVITY_IN -> "IN"
-                TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT"
-                TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT"
-                else -> "INVALID"
-            }
-        }
-
-        private val WIFI_NETWORK_CALLBACK_REQUEST: NetworkRequest =
-            NetworkRequest.Builder()
-                .clearCapabilities()
-                .addCapability(NET_CAPABILITY_NOT_VPN)
-                .addTransportType(TRANSPORT_WIFI)
-                .addTransportType(TRANSPORT_CELLULAR)
-                .build()
-
-        private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index 73bcdfd..be86620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
new file mode 100644
index 0000000..c8c94e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 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.systemui.statusbar.pipeline.wifi.data.repository.prod
+
+import android.annotation.SuppressLint
+import android.content.IntentFilter
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
+import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import android.net.wifi.WifiInfo
+import android.net.wifi.WifiManager
+import android.net.wifi.WifiManager.TrafficStateCallback
+import android.util.Log
+import com.android.settingslib.Utils
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.stateIn
+
+/** Real implementation of [WifiRepository]. */
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+@SuppressLint("MissingPermission")
+class WifiRepositoryImpl @Inject constructor(
+    broadcastDispatcher: BroadcastDispatcher,
+    connectivityManager: ConnectivityManager,
+    logger: ConnectivityPipelineLogger,
+    @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
+    @Main mainExecutor: Executor,
+    @Application scope: CoroutineScope,
+    wifiManager: WifiManager?,
+) : WifiRepository {
+
+    private val wifiStateChangeEvents: Flow<Unit> = broadcastDispatcher.broadcastFlow(
+        IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)
+    )
+        .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
+
+    private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
+        MutableSharedFlow(extraBufferCapacity = 1)
+
+    override val isWifiEnabled: StateFlow<Boolean> =
+        if (wifiManager == null) {
+            MutableStateFlow(false).asStateFlow()
+        } else {
+            // Because [WifiManager] doesn't expose a wifi enabled change listener, we do it
+            // internally by fetching [WifiManager.isWifiEnabled] whenever we think the state may
+            // have changed.
+            merge(wifiNetworkChangeEvents, wifiStateChangeEvents)
+                .mapLatest { wifiManager.isWifiEnabled }
+                .distinctUntilChanged()
+                .logDiffsForTable(
+                    wifiTableLogBuffer,
+                    columnPrefix = "",
+                    columnName = "isWifiEnabled",
+                    initialValue = wifiManager.isWifiEnabled,
+                )
+                .stateIn(
+                    scope = scope,
+                    started = SharingStarted.WhileSubscribed(),
+                    initialValue = wifiManager.isWifiEnabled
+                )
+        }
+
+    override val isWifiDefault: StateFlow<Boolean> = conflatedCallbackFlow {
+        // Note: This callback doesn't do any logging because we already log every network change
+        // in the [wifiNetwork] callback.
+        val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
+            override fun onCapabilitiesChanged(
+                network: Network,
+                networkCapabilities: NetworkCapabilities
+            ) {
+                // This method will always be called immediately after the network becomes the
+                // default, in addition to any time the capabilities change while the network is
+                // the default.
+                // If this network contains valid wifi info, then wifi is the default network.
+                val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
+                trySend(wifiInfo != null)
+            }
+
+            override fun onLost(network: Network) {
+                // The system no longer has a default network, so wifi is definitely not default.
+                trySend(false)
+            }
+        }
+
+        connectivityManager.registerDefaultNetworkCallback(callback)
+        awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+    }
+        .distinctUntilChanged()
+        .logDiffsForTable(
+            wifiTableLogBuffer,
+            columnPrefix = "",
+            columnName = "isWifiDefault",
+            initialValue = false,
+        )
+        .stateIn(
+            scope,
+            started = SharingStarted.WhileSubscribed(),
+            initialValue = false
+        )
+
+    override val wifiNetwork: StateFlow<WifiNetworkModel> = conflatedCallbackFlow {
+        var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
+
+        val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
+            override fun onCapabilitiesChanged(
+                network: Network,
+                networkCapabilities: NetworkCapabilities
+            ) {
+                logger.logOnCapabilitiesChanged(network, networkCapabilities)
+
+                wifiNetworkChangeEvents.tryEmit(Unit)
+
+                val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
+                if (wifiInfo?.isPrimary == true) {
+                    val wifiNetworkModel = createWifiNetworkModel(
+                        wifiInfo,
+                        network,
+                        networkCapabilities,
+                        wifiManager,
+                    )
+                    logger.logTransformation(
+                        WIFI_NETWORK_CALLBACK_NAME,
+                        oldValue = currentWifi,
+                        newValue = wifiNetworkModel
+                    )
+                    currentWifi = wifiNetworkModel
+                    trySend(wifiNetworkModel)
+                }
+            }
+
+            override fun onLost(network: Network) {
+                logger.logOnLost(network)
+
+                wifiNetworkChangeEvents.tryEmit(Unit)
+
+                val wifi = currentWifi
+                if (wifi is WifiNetworkModel.Active && wifi.networkId == network.getNetId()) {
+                    val newNetworkModel = WifiNetworkModel.Inactive
+                    logger.logTransformation(
+                        WIFI_NETWORK_CALLBACK_NAME,
+                        oldValue = wifi,
+                        newValue = newNetworkModel
+                    )
+                    currentWifi = newNetworkModel
+                    trySend(newNetworkModel)
+                }
+            }
+        }
+
+        connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
+
+        awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+    }
+        .distinctUntilChanged()
+        .logDiffsForTable(
+            wifiTableLogBuffer,
+            columnPrefix = "wifiNetwork",
+            initialValue = WIFI_NETWORK_DEFAULT,
+        )
+        // There will be multiple wifi icons in different places that will frequently
+        // subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
+        // new subscribes will get the latest value immediately upon subscription. Otherwise, the
+        // views could show stale data. See b/244173280.
+        .stateIn(
+            scope,
+            started = SharingStarted.WhileSubscribed(),
+            initialValue = WIFI_NETWORK_DEFAULT
+        )
+
+    override val wifiActivity: StateFlow<DataActivityModel> =
+            if (wifiManager == null) {
+                Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
+                flowOf(ACTIVITY_DEFAULT)
+            } else {
+                conflatedCallbackFlow {
+                    val callback = TrafficStateCallback { state ->
+                        logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
+                        trySend(state.toWifiDataActivityModel())
+                    }
+                    wifiManager.registerTrafficStateCallback(mainExecutor, callback)
+                    awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
+                }
+            }
+                .logDiffsForTable(
+                    wifiTableLogBuffer,
+                    columnPrefix = ACTIVITY_PREFIX,
+                    initialValue = ACTIVITY_DEFAULT,
+                )
+                .stateIn(
+                    scope,
+                    started = SharingStarted.WhileSubscribed(),
+                    initialValue = ACTIVITY_DEFAULT
+                )
+
+    companion object {
+        private const val ACTIVITY_PREFIX = "wifiActivity"
+
+        val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+        // Start out with no known wifi network.
+        // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
+        // initial fetch to get a starting wifi network. But, it uses a deprecated API
+        // [WifiManager.getConnectionInfo()], and the deprecation doc indicates to just use
+        // [ConnectivityManager.NetworkCallback] results instead. So, for now we'll just rely on the
+        // NetworkCallback inside [wifiNetwork] for our wifi network information.
+        val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
+
+        private fun networkCapabilitiesToWifiInfo(
+            networkCapabilities: NetworkCapabilities
+        ): WifiInfo? {
+            return when {
+                networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
+                    networkCapabilities.transportInfo as WifiInfo?
+                networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
+                    // Sometimes, cellular networks can act as wifi networks (known as VCN --
+                    // virtual carrier network). So, see if this cellular network has wifi info.
+                    Utils.tryGetWifiInfoForVcn(networkCapabilities)
+                else -> null
+            }
+        }
+
+        private fun createWifiNetworkModel(
+            wifiInfo: WifiInfo,
+            network: Network,
+            networkCapabilities: NetworkCapabilities,
+            wifiManager: WifiManager?,
+        ): WifiNetworkModel {
+            return if (wifiInfo.isCarrierMerged) {
+                WifiNetworkModel.CarrierMerged
+            } else {
+                WifiNetworkModel.Active(
+                        network.getNetId(),
+                        isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED),
+                        level = wifiManager?.calculateSignalLevel(wifiInfo.rssi),
+                        wifiInfo.ssid,
+                        wifiInfo.isPasspointAp,
+                        wifiInfo.isOsuAp,
+                        wifiInfo.passpointProviderFriendlyName
+                )
+            }
+        }
+
+        private fun prettyPrintActivity(activity: Int): String {
+            return when (activity) {
+                TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
+                TrafficStateCallback.DATA_ACTIVITY_IN -> "IN"
+                TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT"
+                TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT"
+                else -> "INVALID"
+            }
+        }
+
+        private val WIFI_NETWORK_CALLBACK_REQUEST: NetworkRequest =
+            NetworkRequest.Builder()
+                .clearCapabilities()
+                .addCapability(NET_CAPABILITY_NOT_VPN)
+                .addTransportType(TRANSPORT_WIFI)
+                .addTransportType(TRANSPORT_CELLULAR)
+                .build()
+
+        private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
+    }
+}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index d8331ab..d4e06bc 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -146,7 +146,7 @@
             tools:replace="android:authorities"
             tools:node="remove" />
 
-        <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+        <provider android:name="com.android.systemui.keyguard.CustomizationProvider"
             android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
             android:enabled="false"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 5e4a43f..4659766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -49,8 +49,8 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.any
@@ -75,7 +75,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
+class CustomizationProviderTest : SysuiTestCase() {
 
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -87,7 +87,7 @@
     @Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
 
-    private lateinit var underTest: KeyguardQuickAffordanceProvider
+    private lateinit var underTest: CustomizationProvider
 
     private lateinit var testScope: TestScope
 
@@ -98,7 +98,7 @@
         whenever(previewRendererFactory.create(any())).thenReturn(previewRenderer)
         whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
 
-        underTest = KeyguardQuickAffordanceProvider()
+        underTest = CustomizationProvider()
         val testDispatcher = StandardTestDispatcher()
         testScope = TestScope(testDispatcher)
         val localUserSelectionManager =
@@ -205,19 +205,34 @@
 
     @Test
     fun getType() {
-        assertThat(underTest.getType(Contract.AffordanceTable.URI))
+        assertThat(underTest.getType(Contract.LockScreenQuickAffordances.AffordanceTable.URI))
             .isEqualTo(
                 "vnd.android.cursor.dir/vnd." +
-                    "${Contract.AUTHORITY}.${Contract.AffordanceTable.TABLE_NAME}"
+                    "${Contract.AUTHORITY}." +
+                    Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                        Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME
+                    )
             )
-        assertThat(underTest.getType(Contract.SlotTable.URI))
+        assertThat(underTest.getType(Contract.LockScreenQuickAffordances.SlotTable.URI))
             .isEqualTo(
-                "vnd.android.cursor.dir/vnd.${Contract.AUTHORITY}.${Contract.SlotTable.TABLE_NAME}"
+                "vnd.android.cursor.dir/vnd.${Contract.AUTHORITY}." +
+                    Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                        Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME
+                    )
             )
-        assertThat(underTest.getType(Contract.SelectionTable.URI))
+        assertThat(underTest.getType(Contract.LockScreenQuickAffordances.SelectionTable.URI))
             .isEqualTo(
                 "vnd.android.cursor.dir/vnd." +
-                    "${Contract.AUTHORITY}.${Contract.SelectionTable.TABLE_NAME}"
+                    "${Contract.AUTHORITY}." +
+                    Contract.LockScreenQuickAffordances.qualifiedTablePath(
+                        Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
+                    )
+            )
+        assertThat(underTest.getType(Contract.FlagsTable.URI))
+            .isEqualTo(
+                "vnd.android.cursor.dir/vnd." +
+                    "${Contract.AUTHORITY}." +
+                    Contract.FlagsTable.TABLE_NAME
             )
     }
 
@@ -296,9 +311,10 @@
             )
 
             context.contentResolver.delete(
-                Contract.SelectionTable.URI,
-                "${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
-                    " ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
+                Contract.LockScreenQuickAffordances.SelectionTable.URI,
+                "${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
+                    " ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
+                    " = ?",
                 arrayOf(
                     KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
                     AFFORDANCE_2,
@@ -330,8 +346,8 @@
             )
 
             context.contentResolver.delete(
-                Contract.SelectionTable.URI,
-                Contract.SelectionTable.Columns.SLOT_ID,
+                Contract.LockScreenQuickAffordances.SelectionTable.URI,
+                Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
                 arrayOf(
                     KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
                 ),
@@ -371,10 +387,13 @@
         affordanceId: String,
     ) {
         context.contentResolver.insert(
-            Contract.SelectionTable.URI,
+            Contract.LockScreenQuickAffordances.SelectionTable.URI,
             ContentValues().apply {
-                put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
-                put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
+                put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
+                put(
+                    Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
+                    affordanceId
+                )
             }
         )
     }
@@ -382,7 +401,7 @@
     private fun querySelections(): List<Selection> {
         return context.contentResolver
             .query(
-                Contract.SelectionTable.URI,
+                Contract.LockScreenQuickAffordances.SelectionTable.URI,
                 null,
                 null,
                 null,
@@ -391,11 +410,18 @@
             ?.use { cursor ->
                 buildList {
                     val slotIdColumnIndex =
-                        cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID
+                        )
                     val affordanceIdColumnIndex =
-                        cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
+                        )
                     val affordanceNameColumnIndex =
-                        cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.SelectionTable.Columns
+                                .AFFORDANCE_NAME
+                        )
                     if (
                         slotIdColumnIndex == -1 ||
                             affordanceIdColumnIndex == -1 ||
@@ -421,7 +447,7 @@
     private fun querySlots(): List<Slot> {
         return context.contentResolver
             .query(
-                Contract.SlotTable.URI,
+                Contract.LockScreenQuickAffordances.SlotTable.URI,
                 null,
                 null,
                 null,
@@ -429,9 +455,14 @@
             )
             ?.use { cursor ->
                 buildList {
-                    val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
+                    val idColumnIndex =
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.SlotTable.Columns.ID
+                        )
                     val capacityColumnIndex =
-                        cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY
+                        )
                     if (idColumnIndex == -1 || capacityColumnIndex == -1) {
                         return@buildList
                     }
@@ -452,7 +483,7 @@
     private fun queryAffordances(): List<Affordance> {
         return context.contentResolver
             .query(
-                Contract.AffordanceTable.URI,
+                Contract.LockScreenQuickAffordances.AffordanceTable.URI,
                 null,
                 null,
                 null,
@@ -460,11 +491,18 @@
             )
             ?.use { cursor ->
                 buildList {
-                    val idColumnIndex = cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
+                    val idColumnIndex =
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID
+                        )
                     val nameColumnIndex =
-                        cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME
+                        )
                     val iconColumnIndex =
-                        cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
+                        cursor.getColumnIndex(
+                            Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON
+                        )
                     if (idColumnIndex == -1 || nameColumnIndex == -1 || iconColumnIndex == -1) {
                         return@buildList
                     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index d7e9cf1..b21cec9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -22,8 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -54,16 +54,16 @@
     private lateinit var testScope: TestScope
     private lateinit var testDispatcher: TestDispatcher
     private lateinit var userTracker: FakeUserTracker
-    private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
-    private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
+    private lateinit var client1: FakeCustomizationProviderClient
+    private lateinit var client2: FakeCustomizationProviderClient
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         whenever(userHandle.identifier).thenReturn(UserHandle.USER_SYSTEM)
         whenever(userHandle.isSystem).thenReturn(true)
-        client1 = FakeKeyguardQuickAffordanceProviderClient()
-        client2 = FakeKeyguardQuickAffordanceProviderClient()
+        client1 = FakeCustomizationProviderClient()
+        client2 = FakeCustomizationProviderClient()
 
         userTracker = FakeUserTracker()
         userTracker.set(
@@ -122,11 +122,11 @@
 
             client1.insertSelection(
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+                affordanceId = FakeCustomizationProviderClient.AFFORDANCE_1,
             )
             client2.insertSelection(
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+                affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2,
             )
 
             userTracker.set(
@@ -139,7 +139,7 @@
                     mapOf(
                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
                             listOf(
-                                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+                                FakeCustomizationProviderClient.AFFORDANCE_1,
                             ),
                     )
                 )
@@ -154,7 +154,7 @@
                     mapOf(
                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
                             listOf(
-                                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+                                FakeCustomizationProviderClient.AFFORDANCE_2,
                             ),
                     )
                 )
@@ -174,7 +174,7 @@
 
             client1.insertSelection(
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+                affordanceId = FakeCustomizationProviderClient.AFFORDANCE_1,
             )
             userTracker.set(
                 userInfos = userTracker.userProfiles,
@@ -197,7 +197,7 @@
 
             underTest.setSelections(
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                affordanceIds = listOf(FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1),
+                affordanceIds = listOf(FakeCustomizationProviderClient.AFFORDANCE_1),
             )
             runCurrent()
 
@@ -206,7 +206,7 @@
                     mapOf(
                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
                             listOf(
-                                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+                                FakeCustomizationProviderClient.AFFORDANCE_1,
                             ),
                     )
                 )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index c187a3f..b071a02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -32,8 +32,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserFileManager
+import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -63,19 +63,13 @@
     private lateinit var config1: FakeKeyguardQuickAffordanceConfig
     private lateinit var config2: FakeKeyguardQuickAffordanceConfig
     private lateinit var userTracker: FakeUserTracker
-    private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
-    private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
+    private lateinit var client1: FakeCustomizationProviderClient
+    private lateinit var client2: FakeCustomizationProviderClient
 
     @Before
     fun setUp() {
-        config1 =
-            FakeKeyguardQuickAffordanceConfig(
-                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1
-            )
-        config2 =
-            FakeKeyguardQuickAffordanceConfig(
-                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2
-            )
+        config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
+        config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
         val scope = CoroutineScope(IMMEDIATE)
         userTracker = FakeUserTracker()
         val localUserSelectionManager =
@@ -95,8 +89,8 @@
                 userTracker = userTracker,
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
-        client1 = FakeKeyguardQuickAffordanceProviderClient()
-        client2 = FakeKeyguardQuickAffordanceProviderClient()
+        client1 = FakeCustomizationProviderClient()
+        client2 = FakeCustomizationProviderClient()
         val remoteUserSelectionManager =
             KeyguardQuickAffordanceRemoteUserSelectionManager(
                 scope = scope,
@@ -256,7 +250,7 @@
             )
             client2.insertSelection(
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+                affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2,
             )
             val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
             val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 7970443..c63dd2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -20,7 +20,10 @@
 import android.os.UserHandle
 import android.provider.Settings
 import android.telephony.CellSignalStrengthCdma
+import android.telephony.NetworkRegistrationInfo
 import android.telephony.ServiceState
+import android.telephony.ServiceState.STATE_IN_SERVICE
+import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
 import android.telephony.SignalStrength
 import android.telephony.SubscriptionInfo
 import android.telephony.TelephonyCallback
@@ -47,7 +50,6 @@
 import android.telephony.TelephonyManager.EXTRA_SPN
 import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
 import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
-import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
@@ -302,7 +304,6 @@
             var latest: MobileConnectionModel? = null
             val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
 
-            val type = NETWORK_TYPE_UNKNOWN
             val expected = UnknownNetworkType
 
             assertThat(latest?.resolvedNetworkType).isEqualTo(expected)
@@ -590,6 +591,56 @@
             job.cancel()
         }
 
+    @Test
+    fun `connection model - isInService - not iwlan`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.connectionInfo.onEach { latest = it.isInService }.launchIn(this)
+
+            val serviceState = ServiceState()
+            serviceState.voiceRegState = STATE_IN_SERVICE
+            serviceState.dataRegState = STATE_IN_SERVICE
+
+            getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+
+            assertThat(latest).isTrue()
+
+            serviceState.voiceRegState = STATE_OUT_OF_SERVICE
+            getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+            assertThat(latest).isTrue()
+
+            serviceState.dataRegState = STATE_OUT_OF_SERVICE
+            getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun `connection model - isInService - is iwlan - voice out of service - data in service`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.connectionInfo.onEach { latest = it.isInService }.launchIn(this)
+
+            // Mock the service state here so we can make it specifically IWLAN
+            val serviceState: ServiceState = mock()
+            whenever(serviceState.state).thenReturn(STATE_OUT_OF_SERVICE)
+            whenever(serviceState.dataRegistrationState).thenReturn(STATE_IN_SERVICE)
+
+            // See [com.android.settingslib.Utils.isInService] for more info. This is one way to
+            // make the network look like IWLAN
+            val networkRegWlan: NetworkRegistrationInfo = mock()
+            whenever(serviceState.getNetworkRegistrationInfo(any(), any()))
+                .thenReturn(networkRegWlan)
+            whenever(networkRegWlan.registrationState)
+                .thenReturn(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+
+            getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     private fun getTelephonyCallbacks(): List<TelephonyCallback> {
         val callbackCaptor = argumentCaptor<TelephonyCallback>()
         Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index c494589..ff72715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -29,6 +29,8 @@
 ) : MobileIconInteractor {
     override val alwaysShowDataRatIcon = MutableStateFlow(false)
 
+    override val alwaysUseCdmaLevel = MutableStateFlow(false)
+
     override val activity =
         MutableStateFlow(
             DataActivityModel(
@@ -52,6 +54,8 @@
 
     override val isDataConnected = MutableStateFlow(true)
 
+    override val isInService = MutableStateFlow(true)
+
     private val _isDataEnabled = MutableStateFlow(true)
     override val isDataEnabled = _isDataEnabled
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 19e5516..1c00646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -58,6 +58,8 @@
 
     override val alwaysShowDataRatIcon = MutableStateFlow(false)
 
+    override val alwaysUseCdmaLevel = MutableStateFlow(false)
+
     private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
     override val defaultMobileIconMapping = _defaultMobileIconMapping
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 83c5055..5abe335 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -61,6 +61,7 @@
                 scope,
                 mobileIconsInteractor.activeDataConnectionHasDataEnabled,
                 mobileIconsInteractor.alwaysShowDataRatIcon,
+                mobileIconsInteractor.alwaysUseCdmaLevel,
                 mobileIconsInteractor.defaultMobileIconMapping,
                 mobileIconsInteractor.defaultMobileIconGroup,
                 mobileIconsInteractor.isDefaultConnectionFailed,
@@ -103,7 +104,27 @@
         }
 
     @Test
-    fun cdma_level_default_unknown() =
+    fun gsm_alwaysShowCdmaTrue_stillUsesGsmLevel() =
+        runBlocking(IMMEDIATE) {
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = true,
+                    primaryLevel = GSM_LEVEL,
+                    cdmaLevel = CDMA_LEVEL,
+                ),
+            )
+            mobileIconsInteractor.alwaysUseCdmaLevel.value = true
+
+            var latest: Int? = null
+            val job = underTest.level.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(GSM_LEVEL)
+
+            job.cancel()
+        }
+
+    @Test
+    fun notGsm_level_default_unknown() =
         runBlocking(IMMEDIATE) {
             connectionRepository.setConnectionInfo(
                 MobileConnectionModel(isGsm = false),
@@ -117,7 +138,7 @@
         }
 
     @Test
-    fun cdma_usesCdmaLevel() =
+    fun notGsm_alwaysShowCdmaTrue_usesCdmaLevel() =
         runBlocking(IMMEDIATE) {
             connectionRepository.setConnectionInfo(
                 MobileConnectionModel(
@@ -126,6 +147,7 @@
                     cdmaLevel = CDMA_LEVEL
                 ),
             )
+            mobileIconsInteractor.alwaysUseCdmaLevel.value = true
 
             var latest: Int? = null
             val job = underTest.level.onEach { latest = it }.launchIn(this)
@@ -136,6 +158,26 @@
         }
 
     @Test
+    fun notGsm_alwaysShowCdmaFalse_usesPrimaryLevel() =
+        runBlocking(IMMEDIATE) {
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = false,
+                    primaryLevel = GSM_LEVEL,
+                    cdmaLevel = CDMA_LEVEL,
+                ),
+            )
+            mobileIconsInteractor.alwaysUseCdmaLevel.value = false
+
+            var latest: Int? = null
+            val job = underTest.level.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(GSM_LEVEL)
+
+            job.cancel()
+        }
+
+    @Test
     fun iconGroup_three_g() =
         runBlocking(IMMEDIATE) {
             connectionRepository.setConnectionInfo(
@@ -240,6 +282,21 @@
         }
 
     @Test
+    fun alwaysUseCdmaLevel_matchesParent() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+
+            mobileIconsInteractor.alwaysUseCdmaLevel.value = true
+            assertThat(latest).isTrue()
+
+            mobileIconsInteractor.alwaysUseCdmaLevel.value = false
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
     fun test_isDefaultDataEnabled_matchesParent() =
         runBlocking(IMMEDIATE) {
             var latest: Boolean? = null
@@ -300,6 +357,23 @@
         }
 
     @Test
+    fun `isInService - uses repository value`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isInService.onEach { latest = it }.launchIn(this)
+
+            connectionRepository.setConnectionInfo(MobileConnectionModel(isInService = true))
+
+            assertThat(latest).isTrue()
+
+            connectionRepository.setConnectionInfo(MobileConnectionModel(isInService = false))
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
     fun `roaming - is gsm - uses connection model`() =
         runBlocking(IMMEDIATE) {
             var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 2fa3467..b82a584 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -291,6 +291,38 @@
             job.cancel()
         }
 
+    @Test
+    fun alwaysUseCdmaLevel_configHasTrue() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+
+            val config = MobileMappings.Config()
+            config.alwaysShowCdmaRssi = true
+            connectionsRepository.defaultDataSubRatConfig.value = config
+            yield()
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun alwaysUseCdmaLevel_configHasFalse() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+
+            val config = MobileMappings.Config()
+            config.alwaysShowCdmaRssi = false
+            connectionsRepository.defaultDataSubRatConfig.value = config
+            yield()
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
         private val tableLogBuffer =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 50221bc..2a8d42f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -98,6 +98,30 @@
         }
 
     @Test
+    fun `icon - uses empty state - when not in service`() =
+        testScope.runTest {
+            var latest: Int? = null
+            val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+
+            interactor.isInService.value = false
+
+            var expected = emptySignal()
+
+            assertThat(latest).isEqualTo(expected)
+
+            // Changing the level doesn't overwrite the disabled state
+            interactor.level.value = 2
+            assertThat(latest).isEqualTo(expected)
+
+            // Once back in service, the regular icon appears
+            interactor.isInService.value = true
+            expected = defaultSignal(level = 2)
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
     fun networkType_dataEnabled_groupIsRepresented() =
         testScope.runTest {
             val expected =
@@ -375,5 +399,7 @@
         ): Int {
             return SignalDrawable.getState(level, /* numLevels */ 4, !connected)
         }
+
+        fun emptySignal(): Int = SignalDrawable.getEmptyState(4)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 4e15b4a..f5837d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index b935442..1085c2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 5d0d87b..befb290 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.pipeline.wifi.data.repository
+package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
 
 import android.net.ConnectivityManager
 import android.net.Network
@@ -33,8 +33,8 @@
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
index d85dd2e..16a1298 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
@@ -18,17 +18,17 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
+import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
 
 class FakeKeyguardQuickAffordanceProviderClientFactory(
     private val userTracker: UserTracker,
-    private val callback: (Int) -> KeyguardQuickAffordanceProviderClient = {
-        FakeKeyguardQuickAffordanceProviderClient()
+    private val callback: (Int) -> CustomizationProviderClient = {
+        FakeCustomizationProviderClient()
     },
 ) : KeyguardQuickAffordanceProviderClientFactory {
 
-    override fun create(): KeyguardQuickAffordanceProviderClient {
+    override fun create(): CustomizationProviderClient {
         return callback(userTracker.userId)
     }
 }
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index d4ef638..658e38b 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -668,8 +668,8 @@
             mIProximityUpdateCallback = new IProximityUpdateCallback.Stub() {
                 @Override
                 public void onProximityUpdate(double distance) {
+                    mCallbackInternal.onProximityUpdate(distance);
                     synchronized (mLock) {
-                        mCallbackInternal.onProximityUpdate(distance);
                         freeIfInactiveLocked();
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f38633f..c712167 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -10127,8 +10127,8 @@
     // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
     // covered with bubbles.
     boolean shouldSendCompatFakeFocus() {
-        return mWmService.mLetterboxConfiguration.isCompatFakeFocusEnabled() && inMultiWindowMode()
-                && !inPinnedWindowingMode() && !inFreeformWindowingMode();
+        return mWmService.mLetterboxConfiguration.isCompatFakeFocusEnabled(info)
+                && inMultiWindowMode() && !inPinnedWindowingMode() && !inFreeformWindowingMode();
     }
 
     static class Builder {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 03c5589..9b84233 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -23,6 +23,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Color;
 import android.provider.DeviceConfig;
 import android.util.Slog;
@@ -39,6 +41,10 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxConfiguration" : TAG_ATM;
 
+    @VisibleForTesting
+    static final String DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS =
+            "enable_compat_fake_focus";
+
     /**
      * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
      * set-fixed-orientation-letterbox-aspect-ratio or via {@link
@@ -108,6 +114,12 @@
     /** Letterboxed app window is aligned to the right side. */
     static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2;
 
+    @VisibleForTesting
+    static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN = "com.android.COMPAT_FAKE_FOCUS_OPT_IN";
+    @VisibleForTesting
+    static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT =
+            "com.android.COMPAT_FAKE_FOCUS_OPT_OUT";
+
     final Context mContext;
 
     // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
@@ -977,11 +989,49 @@
                 "enable_translucent_activity_letterbox", false);
     }
 
-    // TODO(b/262866240): Add listener to check for device config property
+    @VisibleForTesting
+    boolean getPackageManagerProperty(PackageManager pm, String property) {
+        boolean enabled = false;
+        try {
+            final PackageManager.Property p = pm.getProperty(property, mContext.getPackageName());
+            enabled = p.getBoolean();
+        } catch (PackageManager.NameNotFoundException e) {
+            // Property not found
+        }
+        return enabled;
+    }
+
+    @VisibleForTesting
+    boolean isCompatFakeFocusEnabled(ActivityInfo info) {
+        if (!isCompatFakeFocusEnabledOnDevice()) {
+            return false;
+        }
+        // See if the developer has chosen to opt in / out of treatment
+        PackageManager pm = mContext.getPackageManager();
+        if (getPackageManagerProperty(pm, PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT)) {
+            return false;
+        } else if (getPackageManagerProperty(pm, PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN)) {
+            return true;
+        }
+        if (info.isChangeEnabled(ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS)) {
+            return true;
+        }
+        return false;
+    }
+
     /** Whether fake sending focus is enabled for unfocused apps in splitscreen */
-    boolean isCompatFakeFocusEnabled() {
-        return mIsCompatFakeFocusEnabled && DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_WINDOW_MANAGER, "enable_compat_fake_focus", true);
+    boolean isCompatFakeFocusEnabledOnDevice() {
+        return mIsCompatFakeFocusEnabled
+                && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                        DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, true);
+    }
+
+    /**
+     * Overrides whether fake sending focus is enabled for unfocused apps in splitscreen
+     */
+    @VisibleForTesting
+    void setIsCompatFakeFocusEnabled(boolean enabled) {
+        mIsCompatFakeFocusEnabled = enabled;
     }
 
     /** Whether camera compatibility treatment is enabled. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index e196704..ead1a86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -18,6 +18,7 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.server.wm.LetterboxConfiguration.DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
@@ -25,6 +26,8 @@
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -34,6 +37,7 @@
 
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
 
 import androidx.test.filters.SmallTest;
 
@@ -43,18 +47,25 @@
 import java.util.Arrays;
 import java.util.function.BiConsumer;
 
+/**
+ * Tests for the {@link LetterboxConfiguration} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:LetterboxConfigurationTests
+ */
 @SmallTest
 @Presubmit
 public class LetterboxConfigurationTest {
 
+    private Context mContext;
     private LetterboxConfiguration mLetterboxConfiguration;
     private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
 
     @Before
     public void setUp() throws Exception {
-        Context context = getInstrumentation().getTargetContext();
+        mContext = getInstrumentation().getTargetContext();
         mLetterboxConfigurationPersister = mock(LetterboxConfigurationPersister.class);
-        mLetterboxConfiguration = new LetterboxConfiguration(context,
+        mLetterboxConfiguration = new LetterboxConfiguration(mContext,
                 mLetterboxConfigurationPersister);
     }
 
@@ -222,6 +233,34 @@
                 LetterboxConfiguration::movePositionForVerticalReachabilityToNextBottomStop);
     }
 
+    @Test
+    public void testIsCompatFakeFocusEnabledOnDevice() {
+        boolean wasFakeFocusEnabled = DeviceConfig
+                .getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, false);
+
+        // Set runtime flag to true and build time flag to false
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
+        mLetterboxConfiguration.setIsCompatFakeFocusEnabled(false);
+        assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
+
+        // Set runtime flag to false and build time flag to true
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "false", false);
+        mLetterboxConfiguration.setIsCompatFakeFocusEnabled(true);
+        assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
+
+        // Set runtime flag to true so that both are enabled
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
+        assertTrue(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
+
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, Boolean.toString(wasFakeFocusEnabled),
+                false);
+    }
+
     private void assertForHorizontalMove(int from, int expected, int expectedTime,
             boolean halfFoldPose, BiConsumer<LetterboxConfiguration, Boolean> move) {
         // We are in the current position
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 7488e1c..e9080ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -53,6 +53,8 @@
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.LetterboxConfiguration.PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN;
+import static com.android.server.wm.LetterboxConfiguration.PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -3228,6 +3230,80 @@
         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
     }
 
+    private ActivityRecord setUpActivityForCompatFakeFocusTest() {
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setCreateTask(true)
+                .setOnTop(true)
+                // Set the component to be that of the test class in order to enable compat changes
+                .setComponent(ComponentName.createRelative(mContext,
+                        com.android.server.wm.SizeCompatTests.class.getName()))
+                .build();
+        final Task task = activity.getTask();
+        task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        spyOn(activity.mWmService.mLetterboxConfiguration);
+        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+                .isCompatFakeFocusEnabledOnDevice();
+        return activity;
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_returnsTrue() {
+        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+
+        assertTrue(activity.shouldSendCompatFakeFocus());
+    }
+
+    @Test
+    @DisableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
+        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+
+        assertFalse(activity.shouldSendCompatFakeFocus());
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_optOutPropertyAndOverrideEnabled_fakeFocusDisabled() {
+        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT));
+
+        assertFalse(activity.mWmService.mLetterboxConfiguration
+                .isCompatFakeFocusEnabled(activity.info));
+    }
+
+    @Test
+    @DisableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_optInPropertyEnabled_noOverride_fakeFocusEnabled() {
+        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN));
+
+        assertTrue(activity.mWmService.mLetterboxConfiguration
+                .isCompatFakeFocusEnabled(activity.info));
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_optOutPropertyEnabled_fakeFocusDisabled() {
+        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT));
+
+        assertFalse(activity.mWmService.mLetterboxConfiguration
+                .isCompatFakeFocusEnabled(activity.info));
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_optInPropertyEnabled_fakeFocusEnabled() {
+        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN));
+
+        assertTrue(activity.mWmService.mLetterboxConfiguration
+                .isCompatFakeFocusEnabled(activity.info));
+    }
+
     private int getExpectedSplitSize(int dimensionToSplit) {
         int dividerWindowWidth =
                 mActivity.mWmService.mContext.getResources().getDimensionPixelSize(