Added isSupported() method on UserHalService.

This method should be called to make sure the Vehicle Hal supports user
management.

Test: atest CarServiceUnitTest:com.android.car.hal.UserHalServiceTest
Bug: 146207078

Change-Id: I89ab9bb0159ae1a64dafa109cdda5c26ac44c551
diff --git a/service/src/com/android/car/hal/UserHalService.java b/service/src/com/android/car/hal/UserHalService.java
index ab44046..4ce7c16 100644
--- a/service/src/com/android/car/hal/UserHalService.java
+++ b/service/src/com/android/car/hal/UserHalService.java
@@ -37,7 +37,6 @@
 import android.util.SparseArray;
 
 import com.android.car.CarLog;
-import com.android.car.hal.UserHalService.HalCallback.HalCallbackStatus;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
@@ -54,6 +53,8 @@
  */
 public final class UserHalService extends HalServiceBase {
 
+    private static final String UNSUPPORTED_MSG = "Vehicle HAL does not support user management";
+
     private static final String TAG = CarLog.TAG_USER;
 
     // TODO(b/146207078): STOPSHIP - change to false before R is launched
@@ -64,7 +65,8 @@
     private final VehicleHal mHal;
 
     @GuardedBy("mLock")
-    private final SparseArray<VehiclePropConfig> mProperties = new SparseArray<>(1);
+    @Nullable
+    private SparseArray<VehiclePropConfig> mProperties;
 
     // NOTE: handler is currently only used to check if HAL times out replying to requests, by
     // posting messages whose id is the request id. If it needs to be used for more messages, we'll
@@ -131,19 +133,28 @@
     @Nullable
     public Collection<VehiclePropConfig> takeSupportedProperties(
             Collection<VehiclePropConfig> allProperties) {
-        ArrayList<VehiclePropConfig> supported = new ArrayList<>();
-        synchronized (mLock) {
-            for (VehiclePropConfig config : allProperties) {
-                switch (config.prop) {
-                    case INITIAL_USER_INFO:
-                        supported.add(config);
-                        mProperties.put(config.prop, config);
-                        break;
-                }
-
+        boolean supported = false;
+        // TODO(b/146207078): increase capacity once it supports more
+        SparseArray<VehiclePropConfig> properties = new SparseArray<>(1);
+        ArrayList<VehiclePropConfig> taken = new ArrayList<>();
+        for (VehiclePropConfig config : allProperties) {
+            switch (config.prop) {
+                case INITIAL_USER_INFO:
+                    supported = true;
+                    taken.add(config);
+                    properties.put(config.prop, config);
+                    break;
             }
+
         }
-        return supported.isEmpty() ? null : supported;
+        if (!supported) {
+            Log.w(TAG, UNSUPPORTED_MSG);
+            return null;
+        }
+        synchronized (mLock) {
+            mProperties = properties;
+        }
+        return taken;
     }
 
     /**
@@ -179,6 +190,19 @@
     }
 
     /**
+     * Checks if the Vehicle HAL supports user management.
+     */
+    public boolean isSupported() {
+        synchronized (mLock) {
+            return mProperties != null;
+        }
+    }
+
+    private void checkSupportedLocked() {
+        Preconditions.checkState(isSupported(), UNSUPPORTED_MSG);
+    }
+
+    /**
      * Calls HAL to asynchronously get info about the initial user.
      *
      * @param requestType type of request (as defined by
@@ -186,6 +210,9 @@
      * @param timeoutMs how long to wait (in ms) for the property change event.
      * @param usersInfo current state of Android users.
      * @param callback callback to handle the response.
+     *
+     * @throws IllegalStateException if the HAL does not support user management (callers should
+     * call {@link #isSupported()} first to avoid this exception).
      */
     public void getInitialUserInfo(int requestType, int timeoutMs, @NonNull UsersInfo usersInfo,
             @NonNull HalCallback<InitialUserInfoResponse> callback) {
@@ -199,6 +226,7 @@
         propRequest.prop = INITIAL_USER_INFO;
         int requestId;
         synchronized (mLock) {
+            checkSupportedLocked();
             requestId = mNextRequestId++;
             // TODO(b/146207078): use helper method to convert request to prop value
             propRequest.value.int32Values.add(requestId);
@@ -307,6 +335,16 @@
     public void dump(PrintWriter writer) {
         writer.printf("*User HAL*\n");
         synchronized (mLock) {
+            if (!isSupported()) {
+                writer.println(UNSUPPORTED_MSG);
+                return;
+            }
+            int numberProperties = mProperties.size();
+            String indent = "  ";
+            writer.printf("%d supported properties\n", numberProperties);
+            for (int i = 0; i < numberProperties; i++) {
+                writer.printf("%s%s\n", indent, mProperties.valueAt(i));
+            }
             writer.printf("next request id: %d\n", mNextRequestId);
             if (mPendingCallbacks.size() == 0) {
                 writer.println("no pending callbacks");
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java b/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
index a18a73d..9e3c4e3 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
@@ -99,6 +99,8 @@
     @Before
     public void setFixtures() {
         mUserHalService = new UserHalService(mVehicleHal);
+        mUserHalService
+                .takeSupportedProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO)));
 
         mUser0.userId = 0;
         mUser0.flags = 100;
@@ -114,24 +116,32 @@
 
     @Test
     public void testTakeSupportedProperties_unsupportedOnly() {
+        // Cannot use mUserHalService because it's already set with supported properties
+        UserHalService myHalService = new UserHalService(mVehicleHal);
+
         List<VehiclePropConfig> input = Arrays.asList(newConfig(CURRENT_GEAR));
-        Collection<VehiclePropConfig> output = mUserHalService.takeSupportedProperties(input);
+        Collection<VehiclePropConfig> output = myHalService.takeSupportedProperties(input);
+        assertThat(myHalService.isSupported()).isFalse();
         assertThat(output).isNull();
     }
 
     @Test
     public void testTakeSupportedPropertiesAndInit() {
+        // Cannot use mUserHalService because it's already set with supported properties
+        UserHalService myHalService = new UserHalService(mVehicleHal);
+
         VehiclePropConfig unsupportedConfig = newConfig(CURRENT_GEAR);
         VehiclePropConfig userInfoConfig = newSubscribableConfig(INITIAL_USER_INFO);
         List<VehiclePropConfig> input = Arrays.asList(unsupportedConfig, userInfoConfig);
-        Collection<VehiclePropConfig> output = mUserHalService.takeSupportedProperties(input);
+        Collection<VehiclePropConfig> output = myHalService.takeSupportedProperties(input);
+        assertThat(mUserHalService.isSupported()).isTrue();
         assertThat(output).containsExactly(userInfoConfig);
 
         // Ideally there should be 2 test methods (one for takeSupportedProperties() and one for
         // init()), but on "real life" VehicleHal calls these 2 methods in sequence, and the latter
         // depends on the properties set by the former, so it's ok to test both here...
-        mUserHalService.init();
-        verify(mVehicleHal).subscribeProperty(mUserHalService, INITIAL_USER_INFO);
+        myHalService.init();
+        verify(mVehicleHal).subscribeProperty(myHalService, INITIAL_USER_INFO);
     }
 
     @Test