Changed SystemUI HVAC app to dynamically use CarPropertyConfig areaIds

Bug: 259990415
Test: atest HvacControllerTest
Test: atest HvacBooleanToggleButtonTest
Change-Id: I414f23b5d9c690237758d8657e8fed103a3f253f
Merged-In: I414f23b5d9c690237758d8657e8fed103a3f253f
diff --git a/src/com/android/systemui/car/hvac/HvacController.java b/src/com/android/systemui/car/hvac/HvacController.java
index 8f8584b..2411edb 100644
--- a/src/com/android/systemui/car/hvac/HvacController.java
+++ b/src/com/android/systemui/car/hvac/HvacController.java
@@ -41,6 +41,7 @@
 import android.annotation.IntDef;
 import android.car.Car;
 import android.car.VehicleUnit;
+import android.car.hardware.CarPropertyConfig;
 import android.car.hardware.CarPropertyValue;
 import android.car.hardware.property.CarPropertyManager;
 import android.content.res.Resources;
@@ -48,10 +49,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.R;
 import com.android.systemui.car.CarServiceProvider;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -86,6 +85,9 @@
                     HVAC_ACTUAL_FAN_SPEED_RPM, HVAC_POWER_ON, HVAC_FAN_DIRECTION_AVAILABLE,
                     HVAC_AUTO_RECIRC_ON, HVAC_SEAT_VENTILATION, HVAC_ELECTRIC_DEFROSTER_ON};
     private static final int[] HVAC_PROPERTIES_TO_GET_ON_INIT = {HVAC_POWER_ON, HVAC_AUTO_ON};
+    private static final int GLOBAL_AREA_ID = 0;
+    private static final int VEHICLE_AREA_GLOBAL = 0x01000000;
+    private static final int VEHICLE_AREA_SEAT = 0x05000000;
 
     @IntDef(value = {HVAC_FAN_SPEED, HVAC_FAN_DIRECTION, HVAC_TEMPERATURE_CURRENT,
             HVAC_TEMPERATURE_SET, HVAC_DEFROSTER, HVAC_AC_ON, HVAC_MAX_AC_ON, HVAC_MAX_DEFROST_ON,
@@ -104,8 +106,6 @@
     private Executor mExecutor;
     private CarPropertyManager mCarPropertyManager;
     private boolean mIsConnectedToCar;
-    @Nullable
-    private Integer mHvacGlobalAreaId;
 
     /**
      * Contains views to init until car service is connected.
@@ -119,7 +119,9 @@
             new CarPropertyManager.CarPropertyEventCallback() {
                 @Override
                 public void onChangeEvent(CarPropertyValue value) {
-                    mExecutor.execute(() -> handleHvacPropertyChange(value.getPropertyId(), value));
+                    mExecutor.execute(() -> {
+                        handleHvacPropertyChange(value.getPropertyId(), value);
+                    });
                 }
 
                 @Override
@@ -157,14 +159,42 @@
             carServiceProvider.addListener(mCarServiceLifecycleListener);
         }
         configurationController.addCallback(this);
-        mHvacGlobalAreaId = resources.getInteger(R.integer.hvac_global_area_id);
+    }
+
+    private int[] getSupportedAreaIds(int propertyId) {
+        CarPropertyConfig config = mCarPropertyManager.getCarPropertyConfig(propertyId);
+        if (config == null) {
+            // This property isn't supported/exposed by the CarPropertyManager. So an empty array is
+            // returned here to signify that no areaIds with this propertyId are going to be
+            // registered or updated.
+            return new int[] {};
+        }
+        return config.getAreaIds();
+    }
+
+    private ArrayList<Integer> getAreaIdsFromTargetAreaId(int propertyId, int targetAreaId) {
+        ArrayList<Integer> areaIdsFromTargetAreaId = new ArrayList<Integer>();
+        int[] supportedAreaIds = getSupportedAreaIds(propertyId);
+
+        for (int supportedAreaId : supportedAreaIds) {
+            if (targetAreaId == GLOBAL_AREA_ID || (targetAreaId & supportedAreaId) != 0) {
+                areaIdsFromTargetAreaId.add(supportedAreaId);
+            }
+        }
+
+        return areaIdsFromTargetAreaId;
     }
 
     @Override
-    public void setHvacProperty(@HvacProperty Integer propertyId, int areaId, int val) {
+    public void setHvacProperty(@HvacProperty Integer propertyId, int targetAreaId,
+            int val) {
         mExecutor.execute(() -> {
             try {
-                mCarPropertyManager.setIntProperty(propertyId, areaId, val);
+                ArrayList<Integer> supportedAreaIds = getAreaIdsFromTargetAreaId(
+                        propertyId.intValue(), targetAreaId);
+                for (int areaId : supportedAreaIds) {
+                    mCarPropertyManager.setIntProperty(propertyId, areaId, val);
+                }
             } catch (RuntimeException e) {
                 Log.w(TAG, "Error while setting HVAC property: ", e);
             }
@@ -172,10 +202,15 @@
     }
 
     @Override
-    public void setHvacProperty(@HvacProperty Integer propertyId, int areaId, float val) {
+    public void setHvacProperty(@HvacProperty Integer propertyId, int targetAreaId,
+            float val) {
         mExecutor.execute(() -> {
             try {
-                mCarPropertyManager.setFloatProperty(propertyId, areaId, val);
+                ArrayList<Integer> supportedAreaIds = getAreaIdsFromTargetAreaId(
+                        propertyId.intValue(), targetAreaId);
+                for (int areaId : supportedAreaIds) {
+                    mCarPropertyManager.setFloatProperty(propertyId, areaId, val);
+                }
             } catch (RuntimeException e) {
                 Log.w(TAG, "Error while setting HVAC property: ", e);
             }
@@ -183,10 +218,15 @@
     }
 
     @Override
-    public void setHvacProperty(@HvacProperty Integer propertyId, int areaId, boolean val) {
+    public void setHvacProperty(@HvacProperty Integer propertyId, int targetAreaId,
+            boolean val) {
         mExecutor.execute(() -> {
             try {
-                mCarPropertyManager.setBooleanProperty(propertyId, areaId, val);
+                ArrayList<Integer> supportedAreaIds = getAreaIdsFromTargetAreaId(
+                        propertyId.intValue(), targetAreaId);
+                for (int areaId : supportedAreaIds) {
+                    mCarPropertyManager.setBooleanProperty(propertyId, areaId, val);
+                }
             } catch (RuntimeException e) {
                 Log.w(TAG, "Error while setting HVAC property: ", e);
             }
@@ -207,25 +247,48 @@
             try {
                 HvacView hvacView = (HvacView) rootView;
                 @HvacProperty Integer propId = hvacView.getHvacPropertyToView();
-                @AreaId Integer areaId = hvacView.getAreaId();
+                @AreaId Integer targetAreaId = hvacView.getAreaId();
                 hvacView.setHvacPropertySetter(this);
 
-                addHvacViewToMap(propId, areaId, hvacView);
+                ArrayList<Integer> supportedAreaIds = getAreaIdsFromTargetAreaId(propId.intValue(),
+                        targetAreaId.intValue());
+                for (Integer areaId : supportedAreaIds) {
+                    addHvacViewToMap(propId.intValue(), areaId.intValue(), hvacView);
+                }
 
                 if (mCarPropertyManager != null) {
-                    CarPropertyValue initValue = mCarPropertyManager.getProperty(propId, areaId);
                     boolean usesFahrenheit = mCarPropertyManager.getIntProperty(
-                            HVAC_TEMPERATURE_DISPLAY_UNITS,
-                            mCarPropertyManager.getAreaId(HVAC_TEMPERATURE_DISPLAY_UNITS,
-                                    areaId)) == VehicleUnit.FAHRENHEIT;
+                            HVAC_TEMPERATURE_DISPLAY_UNITS, GLOBAL_AREA_ID)
+                            == VehicleUnit.FAHRENHEIT;
+                    for (Integer areaId : supportedAreaIds) {
+                        CarPropertyValue initValue = mCarPropertyManager.getProperty(propId,
+                                areaId);
 
-                    // Initialize the view with the initial value.
-                    hvacView.onPropertyChanged(initValue);
-                    hvacView.onHvacTemperatureUnitChanged(usesFahrenheit);
-                    for (int propToGetOnInitId : HVAC_PROPERTIES_TO_GET_ON_INIT) {
-                        CarPropertyValue propToGetOnInitValue = mCarPropertyManager.getProperty(
-                                propToGetOnInitId, mHvacGlobalAreaId);
-                        hvacView.onPropertyChanged(propToGetOnInitValue);
+                        // Initialize the view with the initial value.
+                        hvacView.onPropertyChanged(initValue);
+                        hvacView.onHvacTemperatureUnitChanged(usesFahrenheit);
+
+                        if (mCarPropertyManager.getCarPropertyConfig(propId).getAreaType()
+                                != VEHICLE_AREA_SEAT) {
+                            continue;
+                        }
+
+                        for (int propToGetOnInitId : HVAC_PROPERTIES_TO_GET_ON_INIT) {
+                            int[] propToGetOnInitSupportedAreaIds = getSupportedAreaIds(
+                                    propToGetOnInitId);
+
+                            int areaIdToFind = areaId.intValue();
+
+                            for (int supportedAreaId : propToGetOnInitSupportedAreaIds) {
+                                if ((supportedAreaId & areaIdToFind) == areaIdToFind) {
+                                    CarPropertyValue propToGetOnInitValue =
+                                            mCarPropertyManager.getProperty(propToGetOnInitId,
+                                            supportedAreaId);
+                                    hvacView.onPropertyChanged(propToGetOnInitValue);
+                                    break;
+                                }
+                            }
+                        }
                     }
                 }
             } catch (IllegalArgumentException ex) {
@@ -248,9 +311,13 @@
         if (rootView instanceof HvacView) {
             HvacView hvacView = (HvacView) rootView;
             @HvacProperty Integer propId = hvacView.getHvacPropertyToView();
-            @AreaId Integer areaId = hvacView.getAreaId();
+            @AreaId Integer targetAreaId = hvacView.getAreaId();
 
-            removeHvacViewFromMap(propId, areaId, hvacView);
+            ArrayList<Integer> supportedAreaIds = getAreaIdsFromTargetAreaId(propId.intValue(),
+                    targetAreaId.intValue());
+            for (Integer areaId : supportedAreaIds) {
+                removeHvacViewFromMap(propId.intValue(), areaId.intValue(), hvacView);
+            }
         }
 
         if (rootView instanceof ViewGroup) {
@@ -263,17 +330,6 @@
 
     @VisibleForTesting
     void handleHvacPropertyChange(@HvacProperty int propertyId, CarPropertyValue value) {
-        List<HvacView> viewsToNotify = null;
-
-        if (value.getAreaId() == mHvacGlobalAreaId) {
-            mHvacPropertyViewMap.forEach((propId, areaIds) -> {
-                areaIds.forEach((areaId, views) -> {
-                    views.forEach(v -> v.onPropertyChanged(value));
-                });
-            });
-            return;
-        }
-
         if (value.getPropertyId() == HVAC_TEMPERATURE_DISPLAY_UNITS) {
             mHvacPropertyViewMap.forEach((propId, areaIds) -> {
                 areaIds.forEach((areaId, views) -> {
@@ -284,12 +340,25 @@
             return;
         }
 
-        Map<Integer, List<HvacView>> viewsRegisteredForProp = mHvacPropertyViewMap.get(propertyId);
-        if (viewsRegisteredForProp != null) {
-            viewsToNotify = viewsRegisteredForProp.get(value.getAreaId());
-            if (viewsToNotify != null) {
-                viewsToNotify.forEach(v -> v.onPropertyChanged(value));
-            }
+        int valueAreaType = mCarPropertyManager.getCarPropertyConfig(value.getPropertyId())
+                .getAreaType();
+        if (valueAreaType == VEHICLE_AREA_GLOBAL) {
+            mHvacPropertyViewMap.forEach((propId, areaIds) -> {
+                areaIds.forEach((areaId, views) -> {
+                    views.forEach(v -> v.onPropertyChanged(value));
+                });
+            });
+        } else {
+            mHvacPropertyViewMap.forEach((propId, areaIds) -> {
+                if (valueAreaType
+                        == mCarPropertyManager.getCarPropertyConfig(propId).getAreaType()) {
+                    areaIds.forEach((areaId, views) -> {
+                        if ((value.getAreaId() & areaId) == areaId) {
+                            views.forEach(v -> v.onPropertyChanged(value));
+                        }
+                    });
+                }
+            });
         }
     }
 
diff --git a/src/com/android/systemui/car/hvac/toggle/HvacToggleButton.java b/src/com/android/systemui/car/hvac/toggle/HvacToggleButton.java
index 13e611b..cdcac60 100644
--- a/src/com/android/systemui/car/hvac/toggle/HvacToggleButton.java
+++ b/src/com/android/systemui/car/hvac/toggle/HvacToggleButton.java
@@ -44,6 +44,8 @@
     protected static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG;
     private static final String TAG = "HvacToggleButton";
     private static final int INVALID_ID = -1;
+    private static final int VEHICLE_AREA_MASK = 0x0f000000;
+    private static final int VEHICLE_AREA_SEAT = 0x05000000;
 
     private int mPropertyId;
     private int mAreaId;
@@ -128,12 +130,14 @@
     protected abstract boolean isToggleOn();
 
     protected boolean shouldAllowControl() {
-        if (mTurnOffIfPowerOff && !mPowerOn) {
-            return false;
-        }
+        if ((mPropertyId & VEHICLE_AREA_MASK) == VEHICLE_AREA_SEAT) {
+            if (mTurnOffIfPowerOff && !mPowerOn) {
+                return false;
+            }
 
-        if (mTurnOffIfAutoOn && mAutoOn) {
-            return false;
+            if (mTurnOffIfAutoOn && mAutoOn) {
+                return false;
+            }
         }
 
         return true;
diff --git a/tests/res/layout/hvac_toggle_button_view.xml b/tests/res/layout/hvac_toggle_button_view.xml
index 16be59c..1dd730b 100644
--- a/tests/res/layout/hvac_toggle_button_view.xml
+++ b/tests/res/layout/hvac_toggle_button_view.xml
@@ -29,7 +29,7 @@
         android:background="@drawable/hvac_panel_button_bg"
         style="@style/HvacButton"
         systemui:hvacAreaId="1"
-        systemui:hvacPropertyId="320865540"
+        systemui:hvacPropertyId="354419973"
         systemui:hvacToggleOnButtonDrawable="@drawable/ic_front_defroster_on"
         systemui:hvacToggleOffButtonDrawable="@drawable/ic_front_defroster_off"/>
 
@@ -38,7 +38,7 @@
         android:background="@drawable/hvac_panel_button_bg"
         style="@style/HvacButton"
         systemui:hvacAreaId="1"
-        systemui:hvacPropertyId="320865540"
+        systemui:hvacPropertyId="354419973"
         systemui:hvacTurnOffIfPowerOff="false"
         systemui:hvacToggleOnButtonDrawable="@drawable/ic_front_defroster_on"
         systemui:hvacToggleOffButtonDrawable="@drawable/ic_front_defroster_off"/>
@@ -48,7 +48,7 @@
         android:background="@drawable/hvac_panel_button_bg"
         style="@style/HvacButton"
         systemui:hvacAreaId="1"
-        systemui:hvacPropertyId="320865540"
+        systemui:hvacPropertyId="354419973"
         systemui:hvacTurnOffIfAutoOn="true"
         systemui:hvacToggleOnButtonDrawable="@drawable/ic_front_defroster_on"
         systemui:hvacToggleOffButtonDrawable="@drawable/ic_front_defroster_off"/>
diff --git a/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java b/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
index 71ffaf0..a978c4f 100644
--- a/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
+++ b/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
@@ -23,6 +23,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -30,6 +31,7 @@
 
 import android.car.Car;
 import android.car.VehicleUnit;
+import android.car.hardware.CarPropertyConfig;
 import android.car.hardware.CarPropertyValue;
 import android.car.hardware.property.CarPropertyManager;
 import android.content.Context;
@@ -59,14 +61,16 @@
 public class HvacControllerTest extends SysuiTestCase {
 
     private static final int AREA_1 = 1;
-    private static final int AREA_2 = 2;
-    private static final int AREA_3 = 3;
+    private static final int AREA_4 = 4;
+    private static final int AREA_16 = 16;
 
     @Mock
     private Car mCar;
     @Mock
     private CarPropertyManager mCarPropertyManager;
     @Mock
+    private CarPropertyConfig mCarPropertyConfig;
+    @Mock
     private CarServiceProvider mCarServiceProvider;
     @Mock
     private TestHvacView mTestHvacView1;
@@ -86,6 +90,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager);
+        when(mCarPropertyManager.getCarPropertyConfig(anyInt())).thenReturn(mCarPropertyConfig);
+        when(mCarPropertyConfig.getAreaIds()).thenReturn(new int[] {AREA_1, AREA_4, AREA_16});
 
         mExecutor = new FakeExecutor(new FakeSystemClock());
         mHvacController = new HvacController(mCarServiceProvider, mExecutor,
@@ -127,6 +133,7 @@
     public void hvacPropertyChanged_subscribingViewRegistered_viewHandleChange() {
         registerAllTestHvacViews();
         when(mCarPropertyValue.getAreaId()).thenReturn(AREA_1);
+        when(mCarPropertyValue.getPropertyId()).thenReturn(HVAC_TEMPERATURE_SET);
 
         mHvacController.handleHvacPropertyChange(HVAC_TEMPERATURE_SET, mCarPropertyValue);
 
@@ -157,6 +164,7 @@
         registerAllTestHvacViews();
         when(mCarPropertyValue.getPropertyId()).thenReturn(HVAC_TEMPERATURE_DISPLAY_UNITS);
         when(mCarPropertyValue.getValue()).thenReturn(VehicleUnit.FAHRENHEIT);
+        when(mCarPropertyValue.getAreaId()).thenReturn(0);
         reset(mTestHvacView1);
         reset(mTestHvacView2);
         reset(mTestHvacView3);
@@ -173,6 +181,7 @@
         registerAllTestHvacViews();
         when(mCarPropertyValue.getPropertyId()).thenReturn(HVAC_TEMPERATURE_DISPLAY_UNITS);
         when(mCarPropertyValue.getValue()).thenReturn(VehicleUnit.CELSIUS);
+        when(mCarPropertyValue.getAreaId()).thenReturn(0);
         reset(mTestHvacView1);
         reset(mTestHvacView2);
         reset(mTestHvacView3);
@@ -188,10 +197,10 @@
         when(mTestHvacView1.getAreaId()).thenReturn(AREA_1);
         when(mTestHvacView1.getHvacPropertyToView()).thenReturn(HVAC_TEMPERATURE_SET);
 
-        when(mTestHvacView2.getAreaId()).thenReturn(AREA_2);
+        when(mTestHvacView2.getAreaId()).thenReturn(AREA_4);
         when(mTestHvacView2.getHvacPropertyToView()).thenReturn(HVAC_TEMPERATURE_SET);
 
-        when(mTestHvacView3.getAreaId()).thenReturn(AREA_3);
+        when(mTestHvacView3.getAreaId()).thenReturn(AREA_16);
         when(mTestHvacView3.getHvacPropertyToView()).thenReturn(HVAC_AUTO_ON);
 
         mHvacController.registerHvacViews(mTestHvacView1);
diff --git a/tests/src/com/android/systemui/car/hvac/toggle/HvacBooleanToggleButtonTest.java b/tests/src/com/android/systemui/car/hvac/toggle/HvacBooleanToggleButtonTest.java
index cc2bec4..983fe21 100644
--- a/tests/src/com/android/systemui/car/hvac/toggle/HvacBooleanToggleButtonTest.java
+++ b/tests/src/com/android/systemui/car/hvac/toggle/HvacBooleanToggleButtonTest.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.car.hvac.toggle;
 
+import static android.car.VehiclePropertyIds.HVAC_AC_ON;
 import static android.car.VehiclePropertyIds.HVAC_AUTO_ON;
-import static android.car.VehiclePropertyIds.HVAC_DEFROSTER;
 import static android.car.VehiclePropertyIds.HVAC_POWER_ON;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -51,7 +51,7 @@
 public class HvacBooleanToggleButtonTest extends SysuiTestCase {
     private static final int GLOBAL_AREA_ID = 117;
     private static final int AREA_ID = 1;
-    private static final int PROPERTY_ID = HVAC_DEFROSTER;
+    private static final int PROPERTY_ID = HVAC_AC_ON;
 
     private View mTestLayout;
     private HvacBooleanToggleButton mDefaultButton;