Merge "DO NOT MERGE - Merge ab/7272582" into stage-aosp-master
diff --git a/library/main/res/values-w1280dp-land/config.xml b/library/main/res/values-w1240dp-land/config.xml
similarity index 100%
rename from library/main/res/values-w1280dp-land/config.xml
rename to library/main/res/values-w1240dp-land/config.xml
diff --git a/library/main/res/values-w1280dp-land/dimens.xml b/library/main/res/values-w1240dp-land/dimens.xml
similarity index 93%
rename from library/main/res/values-w1280dp-land/dimens.xml
rename to library/main/res/values-w1240dp-land/dimens.xml
index 1ed491e..257d03a 100644
--- a/library/main/res/values-w1280dp-land/dimens.xml
+++ b/library/main/res/values-w1240dp-land/dimens.xml
@@ -16,7 +16,7 @@
   -->
 
 <resources>
-    <dimen name="suw_page_margin_horizontal">80dp</dimen>
+    <dimen name="suw_page_margin_horizontal">64dp</dimen>
     <!-- The column inner padding of a two-column layout -->
     <dimen name="suw_column_inner_padding_horizontal">48dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/library/main/res/values-w1280dp/dimens.xml b/library/main/res/values-w1280dp-port/dimens.xml
similarity index 100%
rename from library/main/res/values-w1280dp/dimens.xml
rename to library/main/res/values-w1280dp-port/dimens.xml
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 e110148..ebf0d35 100644
--- a/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
+++ b/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
@@ -310,7 +310,7 @@
                 if (deferredAction != null) {
                     deferredActions.add(deferredAction);
                 }
-            } catch (NullPointerException e) {
+            } catch (NullPointerException | IllegalArgumentException e) {
                 Log.e(
                         TAG,
                         "Unable to load the completion or config state for deferred action: "
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 affdd7c..c58e352 100644
--- a/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java
@@ -18,8 +18,13 @@
 
 import android.car.Car;
 import android.car.CarNotConnectedException;
+import android.car.VehicleAreaType;
+import android.car.VehicleGear;
+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;
@@ -43,11 +48,15 @@
     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 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;
@@ -61,6 +70,25 @@
     @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() == VehicleGear.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();
     }
@@ -108,18 +136,9 @@
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
                 try {
-                    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();
-                    }
+                    registerPropertyManager();
+                    registerRestrictionsManager();
+
                 } catch (CarNotConnectedException e) {
                     Log.e(TAG, "Car not connected", e);
                 }
@@ -181,6 +200,10 @@
                 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);
         }
@@ -225,8 +248,18 @@
     }
 
     private boolean checkIsSetupRestricted(@Nullable CarUxRestrictions restrictionInfo) {
-        return restrictionInfo != null && (restrictionInfo.getActiveRestrictions()
-                & CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP) != 0;
+        if (restrictionInfo == null) {
+            if (isVerboseLoggable()) {
+                Log.v(TAG, "checkIsSetupRestricted restrictionInfo is null, returning false");
+            }
+            return false;
+        }
+        int activeRestrictions = restrictionInfo.getActiveRestrictions();
+        if (isVerboseLoggable()) {
+            Log.v(TAG, "activeRestrictions are " + activeRestrictions);
+        }
+        // There must be at least some restriction in place.
+        return activeRestrictions != 0;
     }
 
     @Override
@@ -275,4 +308,49 @@
     public static void replace(Context context, CarDrivingStateMonitor monitor) {
         CarHelperRegistry.getRegistry(context).putHelper(CarDrivingStateMonitor.class, monitor);
     }
+
+    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();
+        }
+    }
+
+    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() == VehicleGear.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/tests/robotests/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitorTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitorTest.java
index 2de6f5e..afa28f9 100644
--- a/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitorTest.java
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitorTest.java
@@ -127,6 +127,25 @@
     }
 
     @Test
+    public void testOnUxRestrictionsChangedForNonSetup_triggersExit() {
+        mCarDrivingStateMonitor.startMonitor();
+        doReturn(CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO).when(mMockRestrictions)
+                .getActiveRestrictions();
+        mCarDrivingStateMonitor.onUxRestrictionsChanged(mMockRestrictions);
+        assertThat(mShadowApplication.getBroadcastIntents().get(0).getAction())
+                .isEqualTo(CarDrivingStateMonitor.EXIT_BROADCAST_ACTION);
+    }
+
+    @Test
+    public void testStartMonitorWhileDrivingForNonSetup_triggersExit() {
+        doReturn(CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO).when(mMockRestrictions)
+                .getActiveRestrictions();
+        mCarDrivingStateMonitor.startMonitor();
+        assertThat(mShadowApplication.getBroadcastIntents().get(0).getAction())
+                .isEqualTo(CarDrivingStateMonitor.EXIT_BROADCAST_ACTION);
+    }
+
+    @Test
     public void testStartMonitor_clearsStopMonitorRunnable() {
         mCarDrivingStateMonitor.startMonitor();
         ShadowCar.setIsConnected(true);