Merge "Import updated Android SetupCompat Library 573702360" into main
diff --git a/Android.bp b/Android.bp
index effa39b..ce42ba2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -96,9 +96,10 @@
     ],
     static_libs: [
         "androidx.annotation_annotation",
+        "androidx.window_window",
         "error_prone_annotations",
     ],
-    min_sdk_version: "14",
+    min_sdk_version: "19",
     sdk_version: "current",
     optimize: {
         proguard_flags_files: ["proguard.flags"],
@@ -120,9 +121,10 @@
     ],
     static_libs: [
         "androidx.annotation_annotation",
+        "androidx.window_window",
         "error_prone_annotations",
     ],
-    min_sdk_version: "14",
+    min_sdk_version: "19",
     sdk_version: "current",
     optimize: {
         proguard_flags_files: ["proguard.flags"],
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cae5fe3..ccd7122 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,7 +15,9 @@
     limitations under the License.
 -->
 
-<manifest package="com.google.android.setupcompat">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.setupcompat" >
   <!-- Set in the BUILD or gradle file -->
-  <uses-sdk />
+  <uses-sdk
+      android:minSdkVersion="19" />
 </manifest>
diff --git a/build.gradle b/build.gradle
index 7398305..a52cb6d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@
     // Not specifying compileSdkVersion here so clients can specify it; must be at least Q
 
     defaultConfig {
-        minSdkVersion 14
+        minSdkVersion 19
         targetSdkVersion 28
     }
 
diff --git a/main/aidl/com/google/android/setupcompat/portal/v1_1/IPortalProgressCallback.aidl b/main/aidl/com/google/android/setupcompat/portal/v1_1/IPortalProgressCallback.aidl
new file mode 100644
index 0000000..2ed44b9
--- /dev/null
+++ b/main/aidl/com/google/android/setupcompat/portal/v1_1/IPortalProgressCallback.aidl
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.setupcompat.portal.v1_1;
+
+import android.os.Bundle;
+
+/**
+ * Interface for progress service to update progress to SUW. Clients should
+ * update progress at least once a minute, or set a pending reason to stop
+ * update progress. Without progress update and pending reason. We considering
+ * the progress service is no response will suspend it and unbinde service.
+ */
+interface IPortalProgressCallback {
+  /**
+   * Sets completed amount.
+   */
+  Bundle setProgressCount(int completed, int failed, int total) = 0;
+
+  /**
+   * Sets completed percentage.
+   */
+  Bundle setProgressPercentage(int percentage) = 1;
+
+  /**
+   * Sets the summary shows on portal activity.
+   */
+  Bundle setSummary(String summary) = 2;
+
+  /**
+   * Let SUW knows the progress is pending now, and stop update progress.
+   * @param reasonResName The name of resource identifier.
+   * @param quantity The number used to get the correct string for the current language's
+   *           plural rules
+   * @param formatArgs The format arguments that will be used for substitution.
+   */
+  Bundle setPendingReason(String reasonResName, int quantity, in int[] formatArgs, int reason) = 3;
+
+  /**
+   * Once the progress completed, and service can be destroy. Call this function.
+   * SUW will unbind the service.
+   * @param resName The name of resource identifier.
+   * @param quantity The number used to get the correct string for the current language's
+   *           plural rules
+   * @param formatArgs The format arguments that will be used for substitution.
+   */
+  Bundle setComplete(String resName, int quantity, in int[] formatArgs) = 4;
+
+  /**
+   * Once the progress failed, and not able to finish the progress. Should call
+   * this function. SUW will unbind service after receive setFailure. Client can
+   * registerProgressService again once the service is ready to execute.
+   * @param resName The name of resource identifier.
+   * @param quantity The number used to get the correct string for the current language's
+   *           plural rules
+   * @param formatArgs The format arguments that will be used for substitution.
+   */
+  Bundle setFailure(String resName, int quantity, in int[] formatArgs) = 5;
+}
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index 21928c8..70b28d9 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -16,10 +16,8 @@
 
 package com.google.android.setupcompat;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.res.TypedArray;
 import android.os.Build;
 import android.os.Build.VERSION;
@@ -101,7 +99,6 @@
   }
 
   @CanIgnoreReturnValue
-  @TargetApi(VERSION_CODES.HONEYCOMB)
   public PartnerCustomizationLayout(Context context, AttributeSet attrs, int defStyleAttr) {
     super(context, attrs, defStyleAttr);
     init(attrs, defStyleAttr);
@@ -219,8 +216,7 @@
   protected void onAttachedToWindow() {
     super.onAttachedToWindow();
     LifecycleFragment.attachNow(activity);
-    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2
-        && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+    if (WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
       getViewTreeObserver().addOnWindowFocusChangeListener(windowFocusChangeListener);
     }
     getMixin(FooterBarMixin.class).onAttachedToWindow();
@@ -258,10 +254,7 @@
           getContext(),
           CustomEvent.create(MetricKey.get("SetupCompatMetrics", activity), persistableBundle));
     }
-
-    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
-      getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener);
-    }
+    getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener);
   }
 
   /**
@@ -279,13 +272,7 @@
   }
 
   public static Activity lookupActivityFromContext(Context context) {
-    if (context instanceof Activity) {
-      return (Activity) context;
-    } else if (context instanceof ContextWrapper) {
-      return lookupActivityFromContext(((ContextWrapper) context).getBaseContext());
-    } else {
-      throw new IllegalArgumentException("Cannot find instance of Activity in parent tree");
-    }
+    return PartnerConfigHelper.lookupActivityFromContext(context);
   }
 
   /**
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index 2c8cb8c..b417857 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -25,7 +25,6 @@
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Color;
-import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.PersistableBundle;
 import android.util.AttributeSet;
@@ -57,7 +56,6 @@
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupcompat.template.FooterButton.ButtonType;
 import java.util.Locale;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * A {@link Mixin} for managing buttons. By default, the button bar expects that buttons on the
@@ -93,8 +91,6 @@
   private boolean removeFooterBarWhenEmpty = true;
   private boolean isSecondaryButtonInPrimaryStyle = false;
 
-  private static final AtomicInteger nextGeneratedId = new AtomicInteger(1);
-
   @VisibleForTesting public final FooterBarMixinMetrics metrics = new FooterBarMixinMetrics();
 
   private FooterButton.OnButtonEventListener createButtonEventListener(@IdRes int id) {
@@ -144,7 +140,6 @@
       }
 
       @Override
-      @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
       public void onLocaleChanged(Locale locale) {
         if (buttonContainer != null) {
           Button button = buttonContainer.findViewById(id);
@@ -155,7 +150,6 @@
       }
 
       @Override
-      @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
       public void onDirectionChanged(int direction) {
         if (buttonContainer != null && direction != -1) {
           buttonContainer.setLayoutDirection(direction);
@@ -281,11 +275,7 @@
       // Ignore action since buttonContainer is null
       return;
     }
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-      buttonContainer.setId(View.generateViewId());
-    } else {
-      buttonContainer.setId(generateViewId());
-    }
+    buttonContainer.setId(View.generateViewId());
     updateFooterBarPadding(
         buttonContainer,
         footerBarPaddingStart,
@@ -430,7 +420,13 @@
     return primaryButton;
   }
 
-  @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+  /**
+   * Returns the {@link Button} of primary button.
+   *
+   * @apiNote It is not recommended to apply style to the view directly. The setup library will
+   *     handle the button style. There is no guarantee that changes made directly to the button
+   *     style will not cause unexpected behavior.
+   */
   public Button getPrimaryButtonView() {
     return buttonContainer == null ? null : buttonContainer.findViewById(primaryButtonId);
   }
@@ -667,7 +663,13 @@
     return buttonContainer.getVisibility();
   }
 
-  @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+  /**
+   * Returns the {@link Button} of secondary button.
+   *
+   * @apiNote It is not recommended to apply style to the view directly. The setup library will
+   *     handle the button style. There is no guarantee that changes made directly to the button
+   *     style will not cause unexpected behavior.
+   */
   public Button getSecondaryButtonView() {
     return buttonContainer == null ? null : buttonContainer.findViewById(secondaryButtonId);
   }
@@ -678,29 +680,11 @@
         && getSecondaryButtonView().getVisibility() == View.VISIBLE;
   }
 
-  private static int generateViewId() {
-    for (; ; ) {
-      final int result = nextGeneratedId.get();
-      // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
-      int newValue = result + 1;
-      if (newValue > 0x00FFFFFF) {
-        newValue = 1; // Roll over to 1, not 0.
-      }
-      if (nextGeneratedId.compareAndSet(result, newValue)) {
-        return result;
-      }
-    }
-  }
-
   private FooterActionButton inflateButton(
       FooterButton footerButton, FooterButtonPartnerConfig footerButtonPartnerConfig) {
     FooterActionButton button =
         createThemedButton(context, footerButtonPartnerConfig.getPartnerTheme());
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-      button.setId(View.generateViewId());
-    } else {
-      button.setId(generateViewId());
-    }
+    button.setId(View.generateViewId());
 
     // apply initial configuration into button view.
     button.setText(footerButton.getText());
@@ -784,12 +768,11 @@
   }
 
   protected View inflateFooter(@LayoutRes int footer) {
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
-      LayoutInflater inflater =
-          LayoutInflater.from(
-              new ContextThemeWrapper(context, R.style.SucPartnerCustomizationButtonBar_Stackable));
-      footerStub.setLayoutInflater(inflater);
-    }
+    LayoutInflater inflater =
+        LayoutInflater.from(
+            new ContextThemeWrapper(context, R.style.SucPartnerCustomizationButtonBar_Stackable));
+    footerStub.setLayoutInflater(inflater);
+
     footerStub.setLayoutResource(footer);
     return footerStub.inflate();
   }
diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
index c4818d9..fc56aad 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
@@ -426,11 +426,7 @@
     } else {
       iconStart = icon;
     }
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-      button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null);
-    } else {
-      button.setCompoundDrawables(iconStart, null, iconEnd, null);
-    }
+    button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null);
   }
 
   static void updateButtonBackground(Button button, @ColorInt int color) {
diff --git a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
index cccc413..55f3ad6 100644
--- a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
+++ b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
@@ -84,7 +84,7 @@
    */
   public static boolean isAtLeastU() {
     return (Build.VERSION.CODENAME.equals("REL") && Build.VERSION.SDK_INT >= 34)
-      || isAtLeastPreReleaseCodename("UpsideDownCake");
+        || isAtLeastPreReleaseCodename("UpsideDownCake");
   }
 
   private static boolean isAtLeastPreReleaseCodename(String codename) {
@@ -99,4 +99,4 @@
   }
 
   private BuildCompatUtils() {}
-}
+}
\ No newline at end of file
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index f28cd6d..60b2e21 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -20,8 +20,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
 import android.provider.Settings;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -151,17 +149,9 @@
    * @see #isDeviceProvisioned(Context)
    */
   public static boolean isUserSetupComplete(Context context) {
-    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-      return Settings.Secure.getInt(
-              context.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0)
-          == 1;
-    } else {
-      // For versions below JB MR1, there are no user profiles. Just return the global device
-      // provisioned state.
-      return Settings.Secure.getInt(
-              context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
-          == 1;
-    }
+    return Settings.Secure.getInt(
+            context.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0)
+        == 1;
   }
 
   /**
@@ -174,15 +164,9 @@
    * @see #isUserSetupComplete(Context)
    */
   public static boolean isDeviceProvisioned(Context context) {
-    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-      return Settings.Global.getInt(
-              context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
-          == 1;
-    } else {
-      return Settings.Secure.getInt(
-              context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
-          == 1;
-    }
+    return Settings.Global.getInt(
+            context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
+        == 1;
   }
 
   /**
diff --git a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
index 1ef7b39..7500f26 100644
--- a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
+++ b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
@@ -70,7 +70,8 @@
 
     super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
 
-    if (!isFooterButtonsEventlyWeighted(getContext()) && (getMeasuredWidth() > widthSize)) {
+    final boolean childrenLargerThanContainer = (widthSize > 0) && (getMeasuredWidth() > widthSize);
+    if (!isFooterButtonsEvenlyWeighted(getContext()) && childrenLargerThanContainer) {
       setStacked(true);
 
       // Measure again in the new orientation.
@@ -193,18 +194,18 @@
     return childFooterButtons;
   }
 
-  private boolean isFooterButtonsEventlyWeighted(Context context) {
+  private boolean isFooterButtonsEvenlyWeighted(Context context) {
     int childCount = getChildCount();
-    int primayButtonCount = 0;
+    int primaryButtonCount = 0;
     for (int i = 0; i < childCount; i++) {
       View child = getChildAt(i);
       if (child instanceof FooterActionButton) {
         if (((FooterActionButton) child).isPrimaryButtonStyle()) {
-          primayButtonCount += 1;
+          primaryButtonCount += 1;
         }
       }
     }
-    if (primayButtonCount != 2) {
+    if (primaryButtonCount != 2) {
       return false;
     }
 
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
index e08cfa6..e87d998 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -16,8 +16,12 @@
 
 package com.google.android.setupcompat.partnerconfig;
 
+import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -34,12 +38,14 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.window.embedding.ActivityEmbeddingController;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig.ResourceType;
 import com.google.android.setupcompat.util.BuildCompatUtils;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.List;
+import java.util.Objects;
 
 /** The helper reads and caches the partner configurations from SUW. */
 public class PartnerConfigHelper {
@@ -66,6 +72,9 @@
   public static final String IS_DYNAMIC_COLOR_ENABLED_METHOD = "isDynamicColorEnabled";
 
   @VisibleForTesting
+  public static final String IS_FULL_DYNAMIC_COLOR_ENABLED_METHOD = "isFullDynamicColorEnabled";
+
+  @VisibleForTesting
   public static final String IS_NEUTRAL_BUTTON_STYLE_ENABLED_METHOD = "isNeutralButtonStyleEnabled";
 
   @VisibleForTesting
@@ -91,6 +100,7 @@
   @VisibleForTesting public static Bundle applyMaterialYouConfigBundle = null;
 
   @VisibleForTesting public static Bundle applyDynamicColorBundle = null;
+  @VisibleForTesting public static Bundle applyFullDynamicColorBundle = null;
 
   @VisibleForTesting public static Bundle applyNeutralButtonStyleBundle = null;
 
@@ -578,15 +588,40 @@
     if (fallbackBundle != null) {
       resourceEntryBundle.putBundle(KEY_FALLBACK_CONFIG, fallbackBundle.getBundle(resourceName));
     }
-    ResourceEntry adjustResourceEntry =
-        adjustResourceEntryDefaultValue(
-            context, ResourceEntry.fromBundle(context, resourceEntryBundle));
-    ;
-    if (BuildCompatUtils.isAtLeastU() && isEmbeddedActivityOnePaneEnabled(context)) {
-      adjustResourceEntry = embeddedActivityResourceEntryDefaultValue(context, adjustResourceEntry);
+
+    ResourceEntry resourceEntry = ResourceEntry.fromBundle(context, resourceEntryBundle);
+
+    if (BuildCompatUtils.isAtLeastU() && isActivityEmbedded(context)) {
+      resourceEntry = adjustEmbeddedActivityResourceEntryDefaultValue(context, resourceEntry);
+    } else if (BuildCompatUtils.isAtLeastT() && shouldApplyMaterialYouStyle(context)) {
+      resourceEntry = adjustMaterialYouResourceEntryDefaultValue(context, resourceEntry);
     }
 
-    return adjustResourceEntryDayNightMode(context, adjustResourceEntry);
+    return adjustResourceEntryDayNightMode(context, resourceEntry);
+  }
+
+  @VisibleForTesting
+  boolean isActivityEmbedded(Context context) {
+    Activity activity;
+    try {
+      activity = lookupActivityFromContext(context);
+    } catch (IllegalArgumentException e) {
+      Log.w(TAG, "Not a Activity instance in parent tree");
+      return false;
+    }
+
+    return isEmbeddedActivityOnePaneEnabled(context)
+        && ActivityEmbeddingController.getInstance(activity).isActivityEmbedded(activity);
+  }
+
+  public static Activity lookupActivityFromContext(Context context) {
+    if (context instanceof Activity) {
+      return (Activity) context;
+    } else if (context instanceof ContextWrapper) {
+      return lookupActivityFromContext(((ContextWrapper) context).getBaseContext());
+    } else {
+      throw new IllegalArgumentException("Cannot find instance of Activity in parent tree");
+    }
   }
 
   /**
@@ -614,68 +649,29 @@
   // Check the MNStyle flag and replace the inputResourceEntry.resourceName &
   // inputResourceEntry.resourceId after T, that means if using Gliv4 before S, will always use
   // glifv3 resources.
-  ResourceEntry adjustResourceEntryDefaultValue(Context context, ResourceEntry inputResourceEntry) {
-    if (BuildCompatUtils.isAtLeastT() && shouldApplyMaterialYouStyle(context)) {
-      // If not overlay resource
-      try {
-        if (SUW_PACKAGE_NAME.equals(inputResourceEntry.getPackageName())) {
-          String resourceTypeName =
-              inputResourceEntry
-                  .getResources()
-                  .getResourceTypeName(inputResourceEntry.getResourceId());
-          // try to update resourceName & resourceId
-          String materialYouResourceName =
-              inputResourceEntry.getResourceName().concat(MATERIAL_YOU_RESOURCE_SUFFIX);
-          int materialYouResourceId =
-              inputResourceEntry
-                  .getResources()
-                  .getIdentifier(
-                      materialYouResourceName,
-                      resourceTypeName,
-                      inputResourceEntry.getPackageName());
-          if (materialYouResourceId != 0) {
-            Log.i(TAG, "use material you resource:" + materialYouResourceName);
-            return new ResourceEntry(
-                inputResourceEntry.getPackageName(),
-                materialYouResourceName,
-                materialYouResourceId,
-                inputResourceEntry.getResources());
-          }
-        }
-      } catch (NotFoundException ex) {
-        // fall through
-      }
-    }
-    return inputResourceEntry;
-  }
-
-  // Check the embedded activity flag and replace the inputResourceEntry.resourceName &
-  // inputResourceEntry.resourceId after U.
-  ResourceEntry embeddedActivityResourceEntryDefaultValue(
+  ResourceEntry adjustMaterialYouResourceEntryDefaultValue(
       Context context, ResourceEntry inputResourceEntry) {
     // If not overlay resource
     try {
-      if (SUW_PACKAGE_NAME.equals(inputResourceEntry.getPackageName())) {
+      if (Objects.equals(inputResourceEntry.getPackageName(), SUW_PACKAGE_NAME)) {
         String resourceTypeName =
             inputResourceEntry
                 .getResources()
                 .getResourceTypeName(inputResourceEntry.getResourceId());
         // try to update resourceName & resourceId
-        String embeddedActivityResourceName =
-            inputResourceEntry.getResourceName().concat(EMBEDDED_ACTIVITY_RESOURCE_SUFFIX);
-        int embeddedActivityResourceId =
+        String materialYouResourceName =
+            inputResourceEntry.getResourceName().concat(MATERIAL_YOU_RESOURCE_SUFFIX);
+        int materialYouResourceId =
             inputResourceEntry
                 .getResources()
                 .getIdentifier(
-                    embeddedActivityResourceName,
-                    resourceTypeName,
-                    inputResourceEntry.getPackageName());
-        if (embeddedActivityResourceId != 0) {
-          Log.i(TAG, "use embedded activity resource:" + embeddedActivityResourceName);
+                    materialYouResourceName, resourceTypeName, inputResourceEntry.getPackageName());
+        if (materialYouResourceId != 0) {
+          Log.i(TAG, "use material you resource:" + materialYouResourceName);
           return new ResourceEntry(
               inputResourceEntry.getPackageName(),
-              embeddedActivityResourceName,
-              embeddedActivityResourceId,
+              materialYouResourceName,
+              materialYouResourceId,
               inputResourceEntry.getResources());
         }
       }
@@ -685,6 +681,55 @@
     return inputResourceEntry;
   }
 
+  // Check the embedded activity flag and replace the inputResourceEntry.resourceName &
+  // inputResourceEntry.resourceId, and try to find the embedded resource from the different
+  // package.
+  ResourceEntry adjustEmbeddedActivityResourceEntryDefaultValue(
+      Context context, ResourceEntry inputResourceEntry) {
+    // If not overlay resource
+    try {
+      String resourceTypeName =
+          inputResourceEntry.getResources().getResourceTypeName(inputResourceEntry.getResourceId());
+      // For the first time to get embedded activity resource id, it may get from setup wizard
+      // package or Overlay package.
+      String embeddedActivityResourceName =
+          inputResourceEntry.getResourceName().concat(EMBEDDED_ACTIVITY_RESOURCE_SUFFIX);
+      int embeddedActivityResourceId =
+          inputResourceEntry
+              .getResources()
+              .getIdentifier(
+                  embeddedActivityResourceName,
+                  resourceTypeName,
+                  inputResourceEntry.getPackageName());
+      if (embeddedActivityResourceId != 0) {
+        Log.i(TAG, "use embedded activity resource:" + embeddedActivityResourceName);
+        return new ResourceEntry(
+            inputResourceEntry.getPackageName(),
+            embeddedActivityResourceName,
+            embeddedActivityResourceId,
+            inputResourceEntry.getResources());
+      } else {
+        // If resource id is not available from the Overlay package, try to get it from setup wizard
+        // package.
+        PackageManager manager = context.getPackageManager();
+        Resources resources = manager.getResourcesForApplication(SUW_PACKAGE_NAME);
+        embeddedActivityResourceId =
+            resources.getIdentifier(
+                embeddedActivityResourceName, resourceTypeName, SUW_PACKAGE_NAME);
+        if (embeddedActivityResourceId != 0) {
+          return new ResourceEntry(
+              SUW_PACKAGE_NAME,
+              embeddedActivityResourceName,
+              embeddedActivityResourceId,
+              resources);
+        }
+      }
+    } catch (NotFoundException | NameNotFoundException ex) {
+      // fall through
+    }
+    return inputResourceEntry;
+  }
+
   @VisibleForTesting
   public static synchronized void resetInstance() {
     instance = null;
@@ -692,6 +737,7 @@
     applyExtendedPartnerConfigBundle = null;
     applyMaterialYouConfigBundle = null;
     applyDynamicColorBundle = null;
+    applyFullDynamicColorBundle = null;
     applyNeutralButtonStyleBundle = null;
     applyEmbeddedActivityOnePaneBundle = null;
     suwDefaultThemeBundle = null;
@@ -836,6 +882,29 @@
         && applyDynamicColorBundle.getBoolean(IS_DYNAMIC_COLOR_ENABLED_METHOD, false));
   }
 
+  /** Returns {@code true} if the SetupWizard supports the full dynamic color during setup flow. */
+  public static boolean isSetupWizardFullDynamicColorEnabled(@NonNull Context context) {
+    if (applyFullDynamicColorBundle == null) {
+      try {
+        applyFullDynamicColorBundle =
+            context
+                .getContentResolver()
+                .call(
+                    getContentUri(),
+                    IS_FULL_DYNAMIC_COLOR_ENABLED_METHOD,
+                    /* arg= */ null,
+                    /* extras= */ null);
+      } catch (IllegalArgumentException | SecurityException exception) {
+        Log.w(TAG, "SetupWizard full dynamic color supporting status unknown; return as false.");
+        applyFullDynamicColorBundle = null;
+        return false;
+      }
+    }
+
+    return (applyFullDynamicColorBundle != null
+        && applyFullDynamicColorBundle.getBoolean(IS_FULL_DYNAMIC_COLOR_ENABLED_METHOD, false));
+  }
+
   /** Returns true if the SetupWizard supports the one-pane embedded activity during setup flow. */
   public static boolean isEmbeddedActivityOnePaneEnabled(@NonNull Context context) {
     if (applyEmbeddedActivityOnePaneBundle == null) {
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
index 2b83576..ec1da2e 100644
--- a/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
@@ -21,6 +21,7 @@
 
 /**
  * Declares the interface for portal used by GmsCore.
+ * @deprecated, use {@link com.google.android.setupcompat.portal.v1_1.ISetupNotificationServicePortalExtension}.
  */
 interface ISetupNotificationServicePortalExtension {
   IPortalProgressCallback registerTask(in TaskComponent component) = 1;
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
index 8a4dfb3..cdc0a9b 100644
--- a/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
@@ -19,5 +19,6 @@
 /**
  * A class that represents how a persistent notification is to be presented to the user using the
  * {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ * @Deprecated, use {@link com.google.android.setupcompat.portal.v1_1.TaskkComponent}.
  */
 parcelable TaskComponent;
\ No newline at end of file
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/ISetupNotificationServicePortalExtension.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/ISetupNotificationServicePortalExtension.aidl
new file mode 100644
index 0000000..c8b9dd4
--- /dev/null
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/ISetupNotificationServicePortalExtension.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.setupcompat.portal.v1_1;
+
+import com.google.android.setupcompat.portal.v1_1.IPortalProgressCallback;
+import com.google.android.setupcompat.portal.v1_1.TaskComponent;
+
+/**
+ * Declares the interface for portal used by GmsCore.
+ */
+interface ISetupNotificationServicePortalExtension {
+  IPortalProgressCallback registerTask(in TaskComponent component) = 1;
+
+  boolean removeTask(String packageName, String taskName) = 2;
+}
\ No newline at end of file
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/TaskComponent.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/TaskComponent.aidl
new file mode 100644
index 0000000..c89173b
--- /dev/null
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/TaskComponent.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.setupcompat.portal.v1_1;
+
+/**
+ * A class that represents how a persistent notification is to be presented to the user using the
+ * {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ */
+parcelable TaskComponent;
\ No newline at end of file
diff --git a/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java b/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java
index 4f1b884..e0a51d2 100644
--- a/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java
+++ b/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java
@@ -18,9 +18,19 @@
 
 /** Constant values used for PortalExtension */
 public class PortalExtensionConstants {
+  /**
+   * Intent action to bind Portal Service.
+   * @deprecated, use {@code BIND_SERVICE_V_1_1_INTENT_ACTION}.
+   * */
+  @Deprecated
   public static final String BIND_SERVICE_INTENT_ACTION =
       "com.google.android.setupcompat.portal.SetupNotificationService.BIND_EXTENSION";
 
+  /**
+   * Intent action to bind Portal Service.
+   */
+  public static final String BIND_SERVICE_V_1_1_INTENT_ACTION =
+      "com.google.android.setupcompat.portal.SetupNotificationService.BIND_EXTENSION_V_1_1";
+
   private PortalExtensionConstants() {}
-  ;
 }
diff --git a/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java b/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java
index 16d35a3..16289e0 100644
--- a/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java
+++ b/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java
@@ -27,7 +27,10 @@
 /**
  * A class that represents how a persistent notification is to be presented to the user using the
  * {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ *
+ * @deprecated use {@link com.google.android.setupcompat.portal.v1_1.TaskComponent}.
  */
+@Deprecated
 public class TaskComponent implements Parcelable {
   private final String packageName;
   private final String taskName;
diff --git a/portal_extension/java/com/google/android/setupcompat/portal/v1_1/TaskComponent.java b/portal_extension/java/com/google/android/setupcompat/portal/v1_1/TaskComponent.java
new file mode 100644
index 0000000..6cb9037
--- /dev/null
+++ b/portal_extension/java/com/google/android/setupcompat/portal/v1_1/TaskComponent.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.setupcompat.portal.v1_1;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import androidx.annotation.NonNull;
+import com.google.android.setupcompat.internal.Preconditions;
+
+/**
+ * A class that represents how a persistent notification is to be presented to the user using the
+ * {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ */
+public class TaskComponent implements Parcelable {
+  private final String packageName;
+  private final String taskName;
+  private final String displayNameResName;
+  private final String displayIconResName;
+  private final Intent itemClickIntent;
+
+  private TaskComponent(
+      String packageName,
+      String taskName,
+      String displayNameResName,
+      String displayIconResName,
+      Intent itemClickIntent) {
+    this.packageName = packageName;
+    this.taskName = taskName;
+    this.displayNameResName = displayNameResName;
+    this.displayIconResName = displayIconResName;
+    this.itemClickIntent = itemClickIntent;
+  }
+
+  /** Returns a new instance of {@link Builder}. */
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  /** Returns the package name where the service exist. */
+  @NonNull
+  public String getPackageName() {
+    return packageName;
+  }
+
+  /** Returns the service class name */
+  @NonNull
+  public String getTaskName() {
+    return taskName;
+  }
+
+  /** Returns the string resource name of display name. */
+  @NonNull
+  public String getDisplayName() {
+    return displayNameResName;
+  }
+
+  /** Returns the drawable resource name of display icon. */
+  @NonNull
+  public String getDisplayIcon() {
+    return displayIconResName;
+  }
+
+  /** Returns the Intent to start the user interface while progress item click. */
+  public Intent getItemClickIntent() {
+    return itemClickIntent;
+  }
+
+  @Override
+  public void writeToParcel(Parcel dest, int flags) {
+    dest.writeString(getPackageName());
+    dest.writeString(getTaskName());
+    dest.writeString(getDisplayName());
+    dest.writeString(getDisplayIcon());
+    dest.writeParcelable(getItemClickIntent(), 0);
+  }
+
+  @Override
+  public int describeContents() {
+    return 0;
+  }
+
+  public static final Creator<TaskComponent> CREATOR =
+      new Creator<TaskComponent>() {
+        @Override
+        public TaskComponent createFromParcel(Parcel in) {
+          return TaskComponent.newBuilder()
+              .setPackageName(in.readString())
+              .setTaskName(in.readString())
+              .setDisplayName(in.readString())
+              .setDisplayIcon(in.readString())
+              .setItemClickIntent(in.readParcelable(Intent.class.getClassLoader()))
+              .build();
+        }
+
+        @Override
+        public TaskComponent[] newArray(int size) {
+          return new TaskComponent[size];
+        }
+      };
+
+  /** Builder class for {@link com.google.android.setupcompat.portal.TaskComponent} objects. */
+  public static class Builder {
+    private String packageName;
+    private String taskName;
+    private String displayNameResName;
+    private String displayIconResName;
+    private Intent itemClickIntent;
+
+    /** Sets the packages name which is the service exists */
+    public Builder setPackageName(@NonNull String packageName) {
+      this.packageName = packageName;
+      return this;
+    }
+
+    /** Sets a name to identify what task this progress is. */
+    public Builder setTaskName(@NonNull String taskName) {
+      this.taskName = taskName;
+      return this;
+    }
+
+    /** Sets the name which is displayed on PortalActivity */
+    public Builder setDisplayName(String displayNameResName) {
+      this.displayNameResName = displayNameResName;
+      return this;
+    }
+
+    /** Sets the icon which is display on PortalActivity */
+    public Builder setDisplayIcon(String displayIconResName) {
+      this.displayIconResName = displayIconResName;
+      return this;
+    }
+
+    public Builder setItemClickIntent(Intent itemClickIntent) {
+      this.itemClickIntent = itemClickIntent;
+      return this;
+    }
+
+    public TaskComponent build() {
+      Preconditions.checkNotNull(packageName, "packageName cannot be null.");
+      Preconditions.checkNotNull(taskName, "serviceClass cannot be null.");
+      Preconditions.checkNotNull(itemClickIntent, "Item click intent cannot be null");
+      Preconditions.checkNotNull(displayNameResName, "Display name cannot be null");
+      Preconditions.checkNotNull(displayIconResName, "Display icon cannot be null");
+
+      return new TaskComponent(
+          packageName, taskName, displayNameResName, displayIconResName, itemClickIntent);
+    }
+  }
+}