Merge Android R

Bug: 168057903
Merged-In: I64d2be5b9eba48e8269a898a585037e94a6b18e9
Change-Id: I695b1f98565cfe04b895d36b348c6ec2ffaf37f3
diff --git a/OWNERS b/OWNERS
index 422989e..30c6f6a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,7 @@
 # People who can approve changes for submission.
+jdsouza@google.com
 jinjian@google.com
-kevinjm@google.com
+sandraalfaro@google.com
 
 # TLs
 rlagos@google.com
diff --git a/library/main/Android.bp b/library/main/Android.bp
index af4f995..b7cc78f 100644
--- a/library/main/Android.bp
+++ b/library/main/Android.bp
@@ -17,8 +17,8 @@
     name: "car-setup-wizard-lib",
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
-    libs: ["android.car"],
-    static_libs: ["androidx.car_car"],
+    libs: ["android.car-system-stubs"],
+    static_libs: ["androidx.car_car-resources-partially-dejetified"],
     optimize: {
         enabled: false,
     },
diff --git a/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java
index cbf47f1..3da7df6 100644
--- a/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java
+++ b/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java
@@ -28,6 +28,8 @@
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.car.setupwizardlib.partner.PartnerConfig;
+import com.android.car.setupwizardlib.partner.PartnerConfigHelper;
 import com.android.car.setupwizardlib.util.CarDrivingStateMonitor;
 import com.android.car.setupwizardlib.util.CarSetupWizardUiUtils;
 import com.android.car.setupwizardlib.util.CarWizardManagerHelper;
@@ -111,7 +113,9 @@
     protected void onStart() {
         super.onStart();
         // Must be done here so that the SystemUI is hidden when back button is clicked
-        CarSetupWizardUiUtils.hideSystemUI(this);
+        CarSetupWizardUiUtils.setWindowImmersiveMode(this.getWindow(),
+                PartnerConfigHelper.get(this).getString(
+                        this, PartnerConfig.CONFIG_IMMERSIVE_MODE));
         // Fragment commits are not allowed once the Activity's state has been saved. Once
         // onStart() has been called, the FragmentManager should now allow commits.
         mAllowFragmentCommits = true;
@@ -156,7 +160,9 @@
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
         if (hasFocus) {
-            CarSetupWizardUiUtils.hideSystemUI(this);
+            CarSetupWizardUiUtils.setWindowImmersiveMode(this.getWindow(),
+                    PartnerConfigHelper.get(this).getString(
+                            this, PartnerConfig.CONFIG_IMMERSIVE_MODE));
         }
     }
 
@@ -479,7 +485,7 @@
                 CarWizardManagerHelper.getNextIntent(getIntent(), mResultCode, mResultData);
         if (forResult) {
             startActivityForResult(nextIntent, REQUEST_CODE_NEXT);
-        } else  {
+        } else {
             startActivity(nextIntent);
         }
     }
diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java
index 526a5ca..7a7808d 100644
--- a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java
+++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
-import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
@@ -578,9 +577,10 @@
     /** Sets button text color using partner overlay if exists */
     @VisibleForTesting
     void setButtonTextColor(TextView button, PartnerConfig config) {
-        int color = mPartnerConfigHelper.getColor(getContext(), config);
-        if (color != 0) {
-            button.setTextColor(color);
+        ColorStateList colorStateList =
+                mPartnerConfigHelper.getColorStateList(getContext(), config);
+        if (colorStateList != null) {
+            button.setTextColor(colorStateList);
         }
     }
 
@@ -605,12 +605,9 @@
     /** Sets button background color using partner overlay if exists */
     @VisibleForTesting
     void setBackgroundColor(View button, PartnerConfig config) {
-        int color = mPartnerConfigHelper.getColor(getContext(), config);
-        if (color != 0) {
-            Drawable background = button.getBackground();
-            if (background != null) {
-                background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
-            }
+        ColorStateList color = mPartnerConfigHelper.getColorStateList(getContext(), config);
+        if (color != null) {
+            button.setBackgroundTintList(color);
         }
     }
 
diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java
index 8946b7a..00c0ea3 100644
--- a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java
+++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java
@@ -19,6 +19,9 @@
 /** Resources that can be customized by partner overlay APK. */
 public enum PartnerConfig {
 
+    CONFIG_IMMERSIVE_MODE(
+        PartnerConfigKey.KEY_IMMERSIVE_MODE, ResourceType.STRING),
+
     CONFIG_TOOLBAR_BG_COLOR(
             PartnerConfigKey.KEY_TOOLBAR_BG_COLOR, ResourceType.COLOR),
 
diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java
index 2f367a8..dfb134f 100644
--- a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java
+++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java
@@ -19,6 +19,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.graphics.drawable.Drawable;
@@ -78,7 +79,10 @@
         }
 
         if (mPartnerResourceCache.containsKey(partnerConfig)) {
-            return (int) mPartnerResourceCache.get(partnerConfig);
+            Object cacheValue = mPartnerResourceCache.get(partnerConfig);
+            if (cacheValue instanceof Integer) {
+                return (int) cacheValue;
+            }
         }
 
         int result = 0;
@@ -100,6 +104,53 @@
     }
 
     /**
+     * Returns the {@link android.content.res.ColorStateList} of given {@link PartnerConfig}, or
+     * {@code null} if the given {@code partnerConfig} is not found. If the {@code ResourceType} of
+     * the given {@link PartnerConfig} is not a color, IllegalArgumentException will be thrown.
+     *
+     * @param context The context of client activity
+     * @param partnerConfig The {@link PartnerConfig} of target resource
+     */
+    @Nullable
+    public ColorStateList getColorStateList(@NonNull Context context, PartnerConfig partnerConfig) {
+        if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.COLOR) {
+            throw new IllegalArgumentException("Not a color resource");
+        }
+
+        if (mPartnerResourceCache.containsKey(partnerConfig)) {
+            Object cacheValue = mPartnerResourceCache.get(partnerConfig);
+            if (cacheValue instanceof ColorStateList) {
+                return (ColorStateList) cacheValue;
+            }
+        }
+
+        ColorStateList result = null;
+        try {
+            String resourceName = partnerConfig.getResourceName();
+            ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
+            if (resourceEntry == null) {
+                Log.w(TAG, "Resource not found: " + resourceName);
+                return null;
+            }
+
+            Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
+
+            // In case the resource is {@code null} it's simply returned as it is.
+            TypedValue outValue = new TypedValue();
+            resource.getValue(resourceEntry.getResourceId(), outValue, true);
+            if (outValue.type == TypedValue.TYPE_REFERENCE && outValue.data == 0) {
+                return result;
+            }
+
+            result = resource.getColorStateList(resourceEntry.getResourceId(), null);
+            mPartnerResourceCache.put(partnerConfig, result);
+        } catch (PackageManager.NameNotFoundException exception) {
+            Log.e(TAG, exception.getMessage());
+        }
+        return result;
+    }
+
+    /**
      * Returns the {@code Drawable} of given {@code partnerConfig}, or {@code null} if the given
      * {@code partnerConfig} is not found. If the {@code ResourceType} of the given {@code
      * resourceConfig} is not drawable, IllegalArgumentException will be thrown.
diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java
index de4aa9e..32caceb 100644
--- a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java
+++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java
@@ -23,6 +23,7 @@
 
 @Retention(RetentionPolicy.SOURCE)
 @StringDef({
+        PartnerConfigKey.KEY_IMMERSIVE_MODE,
         PartnerConfigKey.KEY_TOOLBAR_BG_COLOR,
         PartnerConfigKey.KEY_TOOLBAR_BUTTON_ICON_BACK,
         PartnerConfigKey.KEY_TOOLBAR_BUTTON_FONT_FAMILY,
@@ -47,6 +48,8 @@
 /** Resource names that can be customized by partner overlay APK. */
 public @interface PartnerConfigKey {
 
+    String KEY_IMMERSIVE_MODE = "suw_compat_immersive_mode";
+
     String KEY_TOOLBAR_BG_COLOR = "suw_compat_toolbar_bg_color";
 
     String KEY_TOOLBAR_BUTTON_ICON_BACK = "suw_compat_toolbar_button_icon_back";
diff --git a/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java b/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
index f0cc786..ed556cd 100644
--- a/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
+++ b/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
@@ -75,13 +75,13 @@
 
     // Extra used as a key for the action id passed in to query summary action state.
     private static final String EXTRA_ACTION_ID = "action_id";
-    private static PartnerSummaryActionsCollector sPartnerSummaryActionsCollector;
-    private final Context mContext;
+    private static PartnerSummaryActionsCollector partnerSummaryActionsCollector;
+    private final Context context;
     private Uri mContentProviderUri;
 
     /** private constructor, should use getter. */
     private PartnerSummaryActionsCollector(Context context) {
-        mContext = context;
+        this.context = context;
         ResolveInfo resolveInfo = getSummaryContentProviderResolveInfo(context.getPackageManager());
 
         if (resolveInfo == null) {
@@ -98,10 +98,10 @@
 
     /** Gets the current instance of the {@link PartnerSummaryActionsCollector}. */
     public static PartnerSummaryActionsCollector get(Context context) {
-        if (sPartnerSummaryActionsCollector == null) {
-            sPartnerSummaryActionsCollector = new PartnerSummaryActionsCollector(context);
+        if (partnerSummaryActionsCollector == null) {
+            partnerSummaryActionsCollector = new PartnerSummaryActionsCollector(context);
         }
-        return sPartnerSummaryActionsCollector;
+        return partnerSummaryActionsCollector;
     }
 
     /**
@@ -330,7 +330,7 @@
      *                                  properly.
      */
     private ArrayList<String> getPartnerSummaryActionsFromContentProvider(Uri contentProviderUri) {
-        Bundle result = mContext.getContentResolver().call(
+        Bundle result = context.getContentResolver().call(
                 contentProviderUri,
                 METHOD_GET_SUMMARY_ACTIONS,
                 /* arg= */ null,
@@ -358,7 +358,7 @@
             String actionId, Uri contentProviderUri) {
         Bundle completionStateArgs = new Bundle();
         completionStateArgs.putString(EXTRA_ACTION_ID, actionId);
-        Bundle result = mContext.getContentResolver().call(
+        Bundle result = context.getContentResolver().call(
                 contentProviderUri,
                 METHOD_GET_ACTION_COMPLETION_STATE,
                 /* arg= */ null,
@@ -374,7 +374,7 @@
             Uri contentProviderUri) {
         Bundle summaryStateArgs = new Bundle();
         summaryStateArgs.putString(EXTRA_ACTION_ID, actionId);
-        Bundle result = mContext.getContentResolver().call(
+        Bundle result = context.getContentResolver().call(
                 contentProviderUri,
                 METHOD_GET_ACTION_SUMMARY_STATE,
                 /* arg= */ null,
@@ -392,7 +392,7 @@
         deferredStateArgs.putString(EXTRA_ACTION_ID, actionId);
         Bundle result;
         try {
-            result = mContext.getContentResolver().call(
+            result = context.getContentResolver().call(
                     contentProviderUri,
                     METHOD_GET_DEFERRED_ACTION_STATE,
                     /* arg= */ null,
diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java b/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java
index 126f7e0..759dd81 100644
--- a/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java
@@ -18,12 +18,8 @@
 
 import android.car.Car;
 import android.car.CarNotConnectedException;
-import android.car.VehicleAreaType;
-import android.car.VehiclePropertyIds;
 import android.car.drivingstate.CarUxRestrictions;
 import android.car.drivingstate.CarUxRestrictionsManager;
-import android.car.hardware.CarPropertyValue;
-import android.car.hardware.property.CarPropertyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -33,6 +29,7 @@
 import android.os.Looper;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 /**
@@ -46,17 +43,11 @@
     public static final String EXIT_BROADCAST_ACTION =
             "com.android.car.setupwizardlib.driving_exit";
 
-    public static final String INTENT_EXTRA_REASON = "reason";
-    public static final String REASON_GEAR_REVERSAL = "gear_reversal";
-
     private static final String TAG = "CarDrivingStateMonitor";
     private static final long DISCONNECT_DELAY_MS = 700;
 
-    private static final int GEAR_REVERSE = 2;
-
     private Car mCar;
     private CarUxRestrictionsManager mRestrictionsManager;
-    private CarPropertyManager mCarPropertyManager;
     // Need to track the number of times the monitor is started so a single stopMonitor call does
     // not override them all.
     private int mMonitorStartedCount;
@@ -70,25 +61,6 @@
     @VisibleForTesting
     final Runnable mDisconnectRunnable = this::disconnectCarMonitor;
 
-    private final CarPropertyManager.CarPropertyEventCallback mGearChangeCallback =
-            new CarPropertyManager.CarPropertyEventCallback() {
-        @SuppressWarnings("rawtypes")
-        @Override
-        public void onChangeEvent(CarPropertyValue value) {
-            switch (value.getPropertyId()) {
-                case VehiclePropertyIds.GEAR_SELECTION:
-                    if ((Integer) value.getValue() == GEAR_REVERSE) {
-                        Log.v(TAG, "Gear has reversed, exiting SetupWizard.");
-                        broadcastGearReversal();
-                    }
-                    break;
-            }
-        }
-
-        @Override
-        public void onErrorEvent(int propertyId, int zone) {}
-    };
-
     private CarDrivingStateMonitor(Context context) {
         mContext = context.getApplicationContext();
     }
@@ -136,9 +108,18 @@
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
                 try {
-                    registerPropertyManager();
-                    registerRestrictionsManager();
-
+                    mRestrictionsManager = (CarUxRestrictionsManager)
+                            mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+                    if (mRestrictionsManager == null) {
+                        Log.e(TAG, "Unable to get CarUxRestrictionsManager");
+                        return;
+                    }
+                    onUxRestrictionsChanged(mRestrictionsManager.getCurrentCarUxRestrictions());
+                    mRestrictionsManager.registerListener(CarDrivingStateMonitor.this);
+                    if (mStopMonitorAfterUxCheck) {
+                        mStopMonitorAfterUxCheck = false;
+                        stopMonitor();
+                    }
                 } catch (CarNotConnectedException e) {
                     Log.e(TAG, "Car not connected", e);
                 }
@@ -199,10 +180,6 @@
                 mRestrictionsManager.unregisterListener();
                 mRestrictionsManager = null;
             }
-            if (mCarPropertyManager != null) {
-                mCarPropertyManager.unregisterCallback(mGearChangeCallback);
-                mCarPropertyManager = null;
-            }
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Car not connected for unregistering listener", e);
         }
@@ -246,8 +223,8 @@
         return false;
     }
 
-    private boolean checkIsSetupRestricted(CarUxRestrictions restrictionInfo) {
-        return (restrictionInfo.getActiveRestrictions()
+    private boolean checkIsSetupRestricted(@Nullable CarUxRestrictions restrictionInfo) {
+        return restrictionInfo != null && (restrictionInfo.getActiveRestrictions()
                 & CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP) != 0;
     }
 
@@ -292,49 +269,9 @@
                 CarDrivingStateMonitor.class, new CarDrivingStateMonitor(context));
     }
 
-    private void registerRestrictionsManager() {
-        mRestrictionsManager = (CarUxRestrictionsManager)
-                mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
-        if (mRestrictionsManager == null) {
-            Log.e(TAG, "Unable to get CarUxRestrictionsManager");
-            return;
-        }
-        onUxRestrictionsChanged(mRestrictionsManager.getCurrentCarUxRestrictions());
-        mRestrictionsManager.registerListener(CarDrivingStateMonitor.this);
-        if (mStopMonitorAfterUxCheck) {
-            mStopMonitorAfterUxCheck = false;
-            stopMonitor();
-        }
+    /** Testing only */
+    @VisibleForTesting
+    public static void replace(Context context, CarDrivingStateMonitor monitor) {
+        CarHelperRegistry.getRegistry(context).putHelper(CarDrivingStateMonitor.class, monitor);
     }
-
-    private void registerPropertyManager() {
-        mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
-        if (mCarPropertyManager == null) {
-            Log.e(TAG, "Unable to get CarPropertyManager");
-            return;
-        }
-        mCarPropertyManager.registerCallback(
-                mGearChangeCallback, VehiclePropertyIds.GEAR_SELECTION,
-                CarPropertyManager.SENSOR_RATE_ONCHANGE);
-        CarPropertyValue<Integer> gearSelection =
-                mCarPropertyManager.getProperty(Integer.class, VehiclePropertyIds.GEAR_SELECTION,
-                    VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
-        if (gearSelection != null
-                && gearSelection.getStatus() == CarPropertyValue.STATUS_AVAILABLE) {
-            if (gearSelection.getValue() == GEAR_REVERSE) {
-                Log.v(TAG, "SetupWizard started when gear is in reverse, exiting.");
-                broadcastGearReversal();
-            }
-        } else {
-            Log.e(TAG, "GEAR_SELECTION is not available.");
-        }
-    }
-
-    private void broadcastGearReversal() {
-        Intent intent = new Intent();
-        intent.setAction(EXIT_BROADCAST_ACTION);
-        intent.putExtra(INTENT_EXTRA_REASON, REASON_GEAR_REVERSAL);
-        mContext.sendBroadcast(intent);
-    }
-
 }
diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java b/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java
index 3af842a..3611931 100644
--- a/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java
@@ -17,14 +17,23 @@
 package com.android.car.setupwizardlib.util;
 
 import android.app.Activity;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.Log;
 import android.view.View;
+import android.view.Window;
 
 import androidx.core.util.Preconditions;
 
-/** Utilities to aid in UI for car setup wizard flow. */
+/**
+ * Utilities to aid in UI for car setup wizard flow.
+ */
 public final class CarSetupWizardUiUtils {
     private static final String TAG = CarSetupWizardUiUtils.class.getSimpleName();
 
+    /** Key for immersive mode value pased to 1P apps */
+    public static final String IMMERSIVE_MODE_TYPE = "immersiveModeType";
+
     /** Hide system UI */
     public static void hideSystemUI(Activity activity) {
         maybeHideSystemUI(activity);
@@ -35,15 +44,66 @@
      **/
     @Deprecated
     public static void maybeHideSystemUI(Activity activity) {
-        Preconditions.checkNotNull(activity);
+        enableImmersiveMode(activity.getWindow());
+    }
+
+    /**
+    * Set the appropriate immersive mode according to immersiveModeValue
+    */
+    public static void setWindowImmersiveMode(Window window, String immersiveModeValue) {
+        Preconditions.checkNotNull(window);
+        ImmersiveModeTypes immersiveModeType;
+        try {
+            immersiveModeType = ImmersiveModeTypes.valueOf(immersiveModeValue);
+        } catch (IllegalArgumentException | NullPointerException e) {
+            Log.w(TAG, "Immersive Mode value: " + immersiveModeValue
+                    + " not valid, using IMMERSIVE");
+            immersiveModeType = ImmersiveModeTypes.IMMERSIVE;
+        }
+        Log.v(TAG, "Enable " + immersiveModeType + " mode");
+        switch (immersiveModeType) {
+            case IMMERSIVE:
+                enableImmersiveMode(window);
+                window.getDecorView().setOnSystemUiVisibilityChangeListener(
+                        visibility -> enableImmersiveMode(window));
+                break;
+            case IMMERSIVE_WITH_STATUS:
+                enableImmersiveModeWithStatus(window);
+                window.getDecorView().setOnSystemUiVisibilityChangeListener(
+                        visibility -> enableImmersiveModeWithStatus(window));
+                break;
+            case NON_IMMERSIVE:
+                disableImmersiveMode(window);
+                window.getDecorView().setOnSystemUiVisibilityChangeListener(
+                        visibility -> disableImmersiveMode(window));
+                break;
+            case SYSTEM_DEFAULT:
+                //SUW won't change the current immersive mode.
+                break;
+        }
+    }
+
+    /**
+     * Enables immersive mode hiding system UI.
+     *
+     * @param window to apply immersive mode.
+     *
+     * @deprecated Use {@code setWindowImmersiveMode}
+     */
+    @Deprecated
+    public static void enableImmersiveMode(Window window) {
+        if (Log.isLoggable(TAG, Log.INFO)) {
+            Log.i(TAG, "enableImmersiveMode");
+        }
+
+        Preconditions.checkNotNull(window);
 
         // See https://developer.android.com/training/system-ui/immersive#EnableFullscreen
         // Enables regular immersive mode.
         // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
         // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
-        View decorView = activity.getWindow().getDecorView();
-        decorView.setSystemUiVisibility(
-                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+        window.getDecorView().setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_IMMERSIVE
                         // Set the content to appear under the system bars so that the
                         // content doesn't resize when the system bars hide and show.
                         | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
@@ -54,6 +114,79 @@
                         | View.SYSTEM_UI_FLAG_FULLSCREEN);
     }
 
+    /**
+     * Enables immersive mode hiding only navigation bar.
+     *
+     * @param window to apply immersive mode.
+     */
+    private static void enableImmersiveModeWithStatus(Window window) {
+        if (Log.isLoggable(TAG, Log.INFO)) {
+            Log.i(TAG, "enableImmersiveModeWithStatus");
+        }
+
+        Preconditions.checkNotNull(window);
+
+        // See https://developer.android.com/training/system-ui/immersive#EnableFullscreen
+        // Enables regular immersive mode.
+        window.getDecorView().setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_IMMERSIVE
+                        // Set the content to appear under the system bars so that the
+                        // content doesn't resize when the system bars hide and show.
+                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                        // Hide the nav bar
+                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+    }
+
+    /**
+     * Disables immersive mode hiding system UI and restores the previous colors.
+     *
+     * @param window the current window instance.
+     *
+     * @deprecated Use {@code setWindowImmersiveMode}
+     */
+    @Deprecated
+    public static void disableImmersiveMode(Window window) {
+        if (Log.isLoggable(TAG, Log.INFO)) {
+            Log.i(TAG, "disableImmersiveMode");
+        }
+
+        Preconditions.checkNotNull(window);
+
+        // Restores the decor view flags to disable the immersive mode.
+        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+
+        // Tries to restore colors for nav and status bar from resources.
+        Context context = window.getContext();
+        if (context == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "Can't restore colors for navigation and status bar.");
+            }
+            return;
+        }
+
+        // Reads the colors for navigation and status bar from resources.
+        final TypedArray typedArray =
+                context.obtainStyledAttributes(
+                        new int[]{
+                                android.R.attr.statusBarColor,
+                                android.R.attr.navigationBarColor});
+        int statusBarColor = typedArray.getColor(0, 0);
+        int navigationBarColor = typedArray.getColor(1, 0);
+
+        window.setStatusBarColor(statusBarColor);
+        window.setNavigationBarColor(navigationBarColor);
+
+        typedArray.recycle();
+    }
+
+    /** Types of Immersive Mode supported by SUW */
+    public enum ImmersiveModeTypes {
+        IMMERSIVE,
+        IMMERSIVE_WITH_STATUS,
+        NON_IMMERSIVE,
+        SYSTEM_DEFAULT
+    }
+
     private CarSetupWizardUiUtils() {
     }
 }
diff --git a/library/main/tests/robotests/res/values/styles.xml b/library/main/tests/robotests/res/values/styles.xml
new file mode 100644
index 0000000..7b867c7
--- /dev/null
+++ b/library/main/tests/robotests/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<resources>
+    <style name="NavAndStatusBarTestTheme" parent="Theme.AppCompat">
+        <item name="android:statusBarColor">#00000001</item>
+        <item name="android:navigationBarColor">#00000002</item>
+    </style>
+</resources>
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ExternalResources.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ExternalResources.java
index fec2290..3892266 100644
--- a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ExternalResources.java
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ExternalResources.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
@@ -162,6 +163,24 @@
             return (Drawable) get(id, ResType.DRAWABLE);
         }
 
+        @NonNull
+        @Override
+        public ColorStateList getColorStateList(int id) throws NotFoundException {
+            return (ColorStateList) get(id, ResType.COLOR_STATE_LIST);
+        }
+
+        @NonNull
+        @Override
+        public ColorStateList getColorStateList(int id, @Nullable Theme theme) {
+            return (ColorStateList) get(id, ResType.COLOR_STATE_LIST);
+        }
+
+        public void putColorStateList(String name, ColorStateList value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "color"),
+                    new TypedResource<>(value, ResType.COLOR_STATE_LIST, null));
+        }
+
         public void putDrawable(String name, Drawable value) {
             put(
                     ResName.qualifyResName(name, mPackageName, "drawable"),
@@ -229,8 +248,13 @@
 
         private Object get(@AnyRes int id, ResType type) {
             TypedResource<?> override = mOverrideResources.get(id);
-            if (override != null && override.getResType() == type) {
-                return override.getData();
+            if (override != null) {
+                if (override.getResType() == type) {
+                    return override.getData();
+                } else if (type == ResType.COLOR_STATE_LIST
+                        && override.getResType() == ResType.COLOR) {
+                    return ColorStateList.valueOf((Integer) override.getData());
+                }
             }
             throw new NotFoundException();
         }
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/PartnerConfigHelperTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/PartnerConfigHelperTest.java
index 081bb22..9969f08 100644
--- a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/PartnerConfigHelperTest.java
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/PartnerConfigHelperTest.java
@@ -20,6 +20,7 @@
 
 import static org.robolectric.RuntimeEnvironment.application;
 
+import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 
 
@@ -88,17 +89,81 @@
 
     @Test
     public void getColor_shouldReturnExpectedColor() {
+        // Making this a global variable makes the VM crash when tests run.
+        ColorStateList grayColorStateList = ColorStateList.valueOf(android.R.color.darker_gray);
+
         PartnerConfigHelper helper = PartnerConfigHelper.get(application);
         assertThat(helper.mPartnerResourceCache).doesNotContainKey(TEST_COLOR_RESOURCE_NAME);
 
-        int result = helper.getColor(application, TEST_COLOR_RESOURCE_NAME);
-        assertThat(result).isEqualTo(android.R.color.darker_gray);
+        int color = helper.getColor(application, TEST_COLOR_RESOURCE_NAME);
+        assertThat(color).isEqualTo(android.R.color.darker_gray);
         assertThat(helper.mPartnerResourceCache).containsKey(TEST_COLOR_RESOURCE_NAME);
         assertThat(helper.mPartnerResourceCache.get(TEST_COLOR_RESOURCE_NAME))
                 .isEqualTo(android.R.color.darker_gray);
     }
 
     @Test
+    public void getColor_whenCacheHasColorStateList_shouldReturnExpectedColor() {
+        // Making this a global variable makes the VM crash when tests run.
+        ColorStateList grayColorStateList = ColorStateList.valueOf(android.R.color.darker_gray);
+
+        PartnerConfigHelper helper = PartnerConfigHelper.get(application);
+        assertThat(helper.mPartnerResourceCache).doesNotContainKey(TEST_COLOR_RESOURCE_NAME);
+
+        // Filling up cache with gray color state list.
+        helper.getColorStateList(application, TEST_COLOR_RESOURCE_NAME);
+
+        // Trying to retrieve the color should return the color state list default darker_gray.
+        int color = helper.getColor(application, TEST_COLOR_RESOURCE_NAME);
+        assertThat(color).isEqualTo(android.R.color.darker_gray);
+        assertThat(helper.mPartnerResourceCache).containsKey(TEST_COLOR_RESOURCE_NAME);
+        assertThat(helper.mPartnerResourceCache.get(TEST_COLOR_RESOURCE_NAME))
+                .isEqualTo(android.R.color.darker_gray);
+    }
+
+    @Test
+    public void getColorStateList_shouldReturnExpectedColorStateList() {
+        // Making this a global variable makes the VM crash when tests run.
+        ColorStateList grayColorStateList = ColorStateList.valueOf(android.R.color.darker_gray);
+
+        PartnerConfigHelper helper = PartnerConfigHelper.get(application);
+        assertThat(helper.mPartnerResourceCache)
+                .doesNotContainKey(TEST_COLOR_RESOURCE_NAME);
+
+        ColorStateList colorStateList = helper.getColorStateList(
+                application, TEST_COLOR_RESOURCE_NAME);
+        assertThat(colorStateList)
+                .isEqualTo(grayColorStateList);
+        assertThat(helper.mPartnerResourceCache)
+                .containsKey(TEST_COLOR_RESOURCE_NAME);
+        assertThat(helper.mPartnerResourceCache.get(TEST_COLOR_RESOURCE_NAME))
+                .isEqualTo(grayColorStateList);
+    }
+
+    @Test
+    public void getColorStateList_whenCacheHasColorValue_shouldReturnExpectedColorStateList() {
+        // Making this a global variable makes the VM crash when tests run.
+        ColorStateList grayColorStateList = ColorStateList.valueOf(android.R.color.darker_gray);
+
+        PartnerConfigHelper helper = PartnerConfigHelper.get(application);
+        assertThat(helper.mPartnerResourceCache)
+                .doesNotContainKey(TEST_COLOR_RESOURCE_NAME);
+
+        // Filling up cache with gray color.
+        helper.getColor(application, TEST_COLOR_RESOURCE_NAME);
+
+        // Trying to retrieve the color state list should return it with a single value.
+        ColorStateList colorStateList = helper.getColorStateList(
+                application, TEST_COLOR_RESOURCE_NAME);
+        assertThat(colorStateList)
+                .isEqualTo(grayColorStateList);
+        assertThat(helper.mPartnerResourceCache)
+                .containsKey(TEST_COLOR_RESOURCE_NAME);
+        assertThat(helper.mPartnerResourceCache.get(TEST_COLOR_RESOURCE_NAME))
+                .isEqualTo(grayColorStateList);
+    }
+
+    @Test
     public void getString_shouldReturnExpectedString() {
         PartnerConfigHelper helper = PartnerConfigHelper.get(application);
         assertThat(helper.mPartnerResourceCache).doesNotContainKey(TEST_STRING_RESOURCE_NAME);
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtilsTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtilsTest.java
new file mode 100644
index 0000000..5933bd4
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtilsTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 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.car.setupwizardlib.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.view.View;
+import android.view.Window;
+
+import com.android.car.setupwizardlib.robotests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class CarSetupWizardUiUtilsTest {
+
+    private static final int IMMERSIVE_MODE_FLAGS =
+            View.SYSTEM_UI_FLAG_IMMERSIVE
+                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_FULLSCREEN;
+
+    private static final int NON_IMMERSIVE_MODE_FLAGS =
+            View.SYSTEM_UI_FLAG_VISIBLE;
+
+    private static final int IMMERSIVE_WITH_STATUS_MODE_FLAGS =
+            View.SYSTEM_UI_FLAG_IMMERSIVE
+                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+
+    private static final int[] RES_ID_NAV_AND_STATUS_BARS = new int[]{
+            android.R.attr.statusBarColor,
+            android.R.attr.navigationBarColor};
+
+    // Note that these colors are defined in the test theme
+    private static final int TEST_THEME = R.style.NavAndStatusBarTestTheme;
+    private static final int EXPECTED_COLOR_STATUS_BAR = Color.getHtmlColor("#001");
+    private static final int EXPECTED_COLOR_NAVIGATION_BAR = Color.getHtmlColor("#002");
+
+    private Activity mActivity;
+    private Window mWindow;
+
+    @Before
+    public void setup() {
+        mActivity = Robolectric
+                .buildActivity(Activity.class)
+                .create()
+                .get();
+        mActivity.setTheme(TEST_THEME);
+        mWindow = mActivity.getWindow();
+    }
+
+    @Test
+    public void maybeHideSystemUI() {
+        CarSetupWizardUiUtils.maybeHideSystemUI(mActivity);
+        assertThat(mWindow.getDecorView().getSystemUiVisibility())
+                .isEqualTo(IMMERSIVE_MODE_FLAGS);
+    }
+
+    @Test
+    public void enableImmersiveMode() {
+        CarSetupWizardUiUtils.enableImmersiveMode(mWindow);
+        assertThat(mWindow.getDecorView().getSystemUiVisibility())
+                .isEqualTo(IMMERSIVE_MODE_FLAGS);
+    }
+
+    @Test
+    public void disableImmersiveMode() {
+        // Resetting the status bar colors.
+        mWindow.setNavigationBarColor(Color.TRANSPARENT);
+        mWindow.setStatusBarColor(Color.TRANSPARENT);
+
+        CarSetupWizardUiUtils.disableImmersiveMode(mWindow);
+
+        assertThat(mWindow.getDecorView().getSystemUiVisibility())
+                .isEqualTo(NON_IMMERSIVE_MODE_FLAGS);
+        assertThat(mWindow.getNavigationBarColor())
+                .isEqualTo(EXPECTED_COLOR_NAVIGATION_BAR);
+        assertThat(mWindow.getStatusBarColor())
+                .isEqualTo(EXPECTED_COLOR_STATUS_BAR);
+    }
+
+    @Test
+    public void setWindow_Immersive() {
+        CarSetupWizardUiUtils.setWindowImmersiveMode(mWindow,
+                CarSetupWizardUiUtils.ImmersiveModeTypes.IMMERSIVE.toString());
+        assertThat(mWindow.getDecorView().getSystemUiVisibility())
+                .isEqualTo(IMMERSIVE_MODE_FLAGS);
+    }
+
+    @Test
+    public void setWindow_ImmersiveWithStatus() {
+        CarSetupWizardUiUtils.setWindowImmersiveMode(mWindow,
+                CarSetupWizardUiUtils.ImmersiveModeTypes.IMMERSIVE_WITH_STATUS.toString());
+        assertThat(mWindow.getDecorView().getSystemUiVisibility())
+                .isEqualTo(IMMERSIVE_WITH_STATUS_MODE_FLAGS);
+    }
+
+
+    @Test
+    public void setWindow_NonImmersive() {
+        CarSetupWizardUiUtils.setWindowImmersiveMode(mWindow,
+                CarSetupWizardUiUtils.ImmersiveModeTypes.NON_IMMERSIVE.toString());
+        assertThat(mWindow.getDecorView().getSystemUiVisibility())
+                .isEqualTo(NON_IMMERSIVE_MODE_FLAGS);
+    }
+
+}
diff --git a/library/utils/Android.bp b/library/utils/Android.bp
index f8cb4c7..7cd7c0b 100644
--- a/library/utils/Android.bp
+++ b/library/utils/Android.bp
@@ -15,15 +15,9 @@
 
 android_library {
     name: "car-setup-wizard-lib-utils",
-    srcs: [
-        "src/**/*.java",
-        "src/com/android/car/setupwizardlib/IInitialLockSetupService.aidl",
-        "src/com/android/car/setupwizardlib/InitialLockListener.java",
-        "src/com/android/car/setupwizardlib/InitialLockSetupClient.java",
-        "src/com/android/car/setupwizardlib/InitialLockSetupConstants.java",
-        "src/com/android/car/setupwizardlib/InitialLockSetupHelper.java",
-    ],
-    libs: ["android.car"],
+    srcs: ["src/**/*.java",
+            "src/**/*.aidl"],
+    libs: ["android.car-system-stubs"],
     optimize: {
         enabled: false,
     },