Merge "Add independent playback modes to CarMediaService" into rvc-dev
diff --git a/computepipe/runner/engine/DefaultEngine.cpp b/computepipe/runner/engine/DefaultEngine.cpp
index d93de2e..0f7c160 100644
--- a/computepipe/runner/engine/DefaultEngine.cpp
+++ b/computepipe/runner/engine/DefaultEngine.cpp
@@ -130,8 +130,16 @@
         return Status::SUCCESS;
     }
     if (command.has_death_notification()) {
-        mCurrentPhaseError = std::make_unique<ComponentError>(
-                "ClientInterface", "Client death", mCurrentPhase, false);
+        if (mCurrentPhase == kResetPhase) {
+            /**
+             * The runner is already in reset state, no need to broadcast client death
+             * to components
+             */
+            LOG(INFO) << "client death notification with no configuration";
+            return Status::SUCCESS;
+        }
+        mCurrentPhaseError = std::make_unique<ComponentError>("ClientInterface", "Client death",
+                                                              mCurrentPhase, false);
         mWakeLooper.notify_all();
         return Status::SUCCESS;
     }
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index 179623d..b051569 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -23,3 +23,6 @@
 # Allow the driver to access kobject uevents
 allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
 
+# Allow the driver to use automotive display proxy service
+allow hal_evs_driver automotive_display_service_server:binder call;
+
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index 348522d..b15ede5 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -59,6 +59,20 @@
     @GuardedBy("mLock")
     private @Nullable ICarWatchdog mCarWatchdogDaemon;
 
+    private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+        @Override
+        public void binderDied() {
+            Log.w(TAG_WATCHDOG, "Car watchdog daemon died: reconnecting");
+            unlinkToDeath();
+            synchronized (mLock) {
+                mCarWatchdogDaemon = null;
+            }
+            mMainHandler.postDelayed(() -> {
+                connectToDaemon(CAR_WATCHDOG_DAEMON_BIND_MAX_RETRY);
+            }, CAR_WATCHDOG_DAEMON_BIND_RETRY_INTERVAL_MS);
+        }
+    };
+
     public CarWatchdogService(Context context) {
         // Car watchdog daemon is found at init().
         this(context, /* daemon= */ null);
@@ -81,12 +95,14 @@
         if (daemon == null) {
             connectToDaemon(CAR_WATCHDOG_DAEMON_BIND_MAX_RETRY);
         } else {
+            linkToDeath();
             registerToDaemon(daemon);
         }
     }
 
     @Override
     public void release() {
+        unlinkToDeath();
         ICarWatchdog daemon;
         synchronized (mLock) {
             daemon = mCarWatchdogDaemon;
@@ -149,23 +165,6 @@
             Log.wtf(TAG_WATCHDOG,
                     "Finding car watchdog daemon took too long(" + elapsedTimeMs + "ms)");
         }
-        try {
-            binder.linkToDeath(new DeathRecipient() {
-                @Override
-                public void binderDied() {
-                    Log.w(TAG_WATCHDOG, "Car watchdog daemon died: reconnecting");
-                    synchronized (mLock) {
-                        mCarWatchdogDaemon = null;
-                    }
-                    mMainHandler.postDelayed(() -> {
-                        connectToDaemon(CAR_WATCHDOG_DAEMON_BIND_MAX_RETRY);
-                    }, CAR_WATCHDOG_DAEMON_BIND_RETRY_INTERVAL_MS);
-                }
-            }, 0);
-        } catch (RemoteException e) {
-            Log.w(TAG_WATCHDOG, "Linking to binder death recipient failed: " + e);
-            return false;
-        }
 
         ICarWatchdog daemon = ICarWatchdog.Stub.asInterface(binder);
         if (daemon == null) {
@@ -175,6 +174,7 @@
         synchronized (mLock) {
             mCarWatchdogDaemon = daemon;
         }
+        linkToDeath();
         registerToDaemon(daemon);
         return true;
     }
@@ -211,6 +211,32 @@
         });
     }
 
+    private void linkToDeath() {
+        IBinder binder;
+        synchronized (mLock) {
+            if (mCarWatchdogDaemon == null) {
+                return;
+            }
+            binder = mCarWatchdogDaemon.asBinder();
+        }
+        try {
+            binder.linkToDeath(mDeathRecipient, 0);
+        } catch (RemoteException e) {
+            Log.w(TAG_WATCHDOG, "Linking to binder death recipient failed: " + e);
+        }
+    }
+
+    private void unlinkToDeath() {
+        IBinder binder;
+        synchronized (mLock) {
+            if (mCarWatchdogDaemon == null) {
+                return;
+            }
+            binder = mCarWatchdogDaemon.asBinder();
+        }
+        binder.unlinkToDeath(mDeathRecipient, 0);
+    }
+
     private static final class ICarWatchdogClientImpl extends ICarWatchdogClient.Stub {
         private final WeakReference<CarWatchdogService> mService;
 
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/AbstractBluetoothA2dpPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/AbstractBluetoothA2dpPreferenceController.java
index d4afe48..1451f19 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/AbstractBluetoothA2dpPreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/AbstractBluetoothA2dpPreferenceController.java
@@ -83,7 +83,10 @@
         final BluetoothCodecConfig codecConfig = mBluetoothA2dpConfigStore.createCodecConfig();
         synchronized (mBluetoothA2dpConfigStore) {
             if (mBluetoothA2dp != null) {
-                setCodecConfigPreference(null, codecConfig);    // Use current active device
+                BluetoothDevice activeDevice = mBluetoothA2dp.getActiveDevice();
+                if (activeDevice != null) {
+                    setCodecConfigPreference(mBluetoothA2dp.getActiveDevice(), codecConfig);
+                }
             }
         }
         // Because the setting is not persisted into permanent storage, we cannot call update state
@@ -102,13 +105,14 @@
 
     @Override
     public void updateState(Preference preference) {
-        if (getCodecConfig(null) == null || mPreference == null) { // Use current active device
+        BluetoothDevice activeDevice = mBluetoothA2dp.getActiveDevice();
+        if (activeDevice == null || getCodecConfig(activeDevice) == null || mPreference == null) {
             return;
         }
 
         BluetoothCodecConfig codecConfig;
         synchronized (mBluetoothA2dpConfigStore) {
-            codecConfig = getCodecConfig(null);         // Use current active device
+            codecConfig = getCodecConfig(activeDevice);         // Use current active device
         }
 
         final int index = getCurrentA2dpSettingIndex(codecConfig);
@@ -178,13 +182,22 @@
     @VisibleForTesting
     void setCodecConfigPreference(BluetoothDevice device,
             BluetoothCodecConfig config) {
-        mBluetoothA2dp.setCodecConfigPreference(device, config);
+        BluetoothDevice bluetoothDevice =
+                (device != null) ? device : mBluetoothA2dp.getActiveDevice();
+        if (bluetoothDevice != null) {
+            mBluetoothA2dp.setCodecConfigPreference(bluetoothDevice, config);
+        }
     }
 
     @VisibleForTesting
     BluetoothCodecConfig getCodecConfig(BluetoothDevice device) {
         if (mBluetoothA2dp != null) {
-            BluetoothCodecStatus codecStatus = mBluetoothA2dp.getCodecStatus(device);
+            BluetoothDevice bluetoothDevice =
+                    (device != null) ? device : mBluetoothA2dp.getActiveDevice();
+            if (bluetoothDevice == null) {
+                return null;
+            }
+            BluetoothCodecStatus codecStatus = mBluetoothA2dp.getCodecStatus(bluetoothDevice);
             if (codecStatus != null) {
                 return codecStatus.getCodecConfig();
             }
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BluetoothAudioCodecPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BluetoothAudioCodecPreferenceController.java
index 5cf9adc..74858b6 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BluetoothAudioCodecPreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BluetoothAudioCodecPreferenceController.java
@@ -17,6 +17,7 @@
 package com.android.car.developeroptions.development;
 
 import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 
 import com.android.car.developeroptions.R;
@@ -109,14 +110,20 @@
             case 6:
                 synchronized (mBluetoothA2dpConfigStore) {
                     if (mBluetoothA2dp != null) {
-                        mBluetoothA2dp.enableOptionalCodecs(null); // Use current active device
+                        BluetoothDevice activeDevice = mBluetoothA2dp.getActiveDevice();
+                        if (activeDevice != null) {
+                            mBluetoothA2dp.enableOptionalCodecs(activeDevice);
+                        }
                     }
                 }
                 return;
             case 7:
                 synchronized (mBluetoothA2dpConfigStore) {
                     if (mBluetoothA2dp != null) {
-                        mBluetoothA2dp.disableOptionalCodecs(null); // Use current active device
+                        BluetoothDevice activeDevice = mBluetoothA2dp.getActiveDevice();
+                        if (activeDevice != null) {
+                            mBluetoothA2dp.disableOptionalCodecs(activeDevice);
+                        }
                     }
                 }
                 return;
diff --git a/tests/SecurityPermissionTest/Android.bp b/tests/SecurityPermissionTest/Android.bp
index f77d151..028875e 100644
--- a/tests/SecurityPermissionTest/Android.bp
+++ b/tests/SecurityPermissionTest/Android.bp
@@ -30,6 +30,7 @@
         "androidx.test.core",
         "androidx.test.ext.junit",
         "androidx.test.rules",
+        "truth-prebuilt",
         "car-frameworks-service",
         "testng",
     ],
diff --git a/tests/SecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicTest.java b/tests/SecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicTest.java
new file mode 100644
index 0000000..111afe4
--- /dev/null
+++ b/tests/SecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.car.Car;
+import android.car.VehicleAreaType;
+import android.car.VehiclePropertyIds;
+import android.car.VehiclePropertyType;
+import android.car.hardware.property.CarPropertyManager;
+import android.os.Handler;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+
+/**
+ * This class contains security permission tests for the {@link CarPropertyManager}'s public APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CarPropertyManagerPublicTest {
+    private Car mCar = null;
+    private CarPropertyManager mPropertyManager;
+    private HashSet<Integer> mProps = new HashSet<>();
+    private static final String TAG = CarPropertyManagerPublicTest.class.getSimpleName();
+    private static final Integer DUMMY_AREA_ID = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+    @Before
+    public void setUp() throws Exception  {
+        initAllPropertyIds();
+        mCar = Car.createCar(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(), (Handler) null);
+        mPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
+        assertThat(mPropertyManager).isNotNull();
+    }
+
+    private synchronized void initAllPropertyIds() {
+        mProps.add(VehiclePropertyIds.DOOR_POS);
+        mProps.add(VehiclePropertyIds.DOOR_MOVE);
+        mProps.add(VehiclePropertyIds.DOOR_LOCK);
+        mProps.add(VehiclePropertyIds.MIRROR_Z_POS);
+        mProps.add(VehiclePropertyIds.MIRROR_Z_MOVE);
+        mProps.add(VehiclePropertyIds.MIRROR_Y_POS);
+        mProps.add(VehiclePropertyIds.MIRROR_Y_MOVE);
+        mProps.add(VehiclePropertyIds.MIRROR_LOCK);
+        mProps.add(VehiclePropertyIds.MIRROR_FOLD);
+        mProps.add(VehiclePropertyIds.SEAT_MEMORY_SELECT);
+        mProps.add(VehiclePropertyIds.SEAT_MEMORY_SET);
+        mProps.add(VehiclePropertyIds.SEAT_BELT_BUCKLED);
+        mProps.add(VehiclePropertyIds.SEAT_BELT_HEIGHT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_BELT_HEIGHT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_FORE_AFT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_FORE_AFT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_BACKREST_ANGLE_1_POS);
+        mProps.add(VehiclePropertyIds.SEAT_BACKREST_ANGLE_1_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_BACKREST_ANGLE_2_POS);
+        mProps.add(VehiclePropertyIds.SEAT_BACKREST_ANGLE_2_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_HEIGHT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_HEIGHT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_DEPTH_POS);
+        mProps.add(VehiclePropertyIds.SEAT_DEPTH_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_TILT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_TILT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_LUMBAR_FORE_AFT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_LUMBAR_FORE_AFT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_LUMBAR_SIDE_SUPPORT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_LUMBAR_SIDE_SUPPORT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_HEADREST_HEIGHT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_HEADREST_HEIGHT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_HEADREST_ANGLE_POS);
+        mProps.add(VehiclePropertyIds.SEAT_HEADREST_ANGLE_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_HEADREST_FORE_AFT_POS);
+        mProps.add(VehiclePropertyIds.SEAT_HEADREST_FORE_AFT_MOVE);
+        mProps.add(VehiclePropertyIds.SEAT_OCCUPANCY);
+        mProps.add(VehiclePropertyIds.WINDOW_POS);
+        mProps.add(VehiclePropertyIds.WINDOW_MOVE);
+        mProps.add(VehiclePropertyIds.WINDOW_LOCK);
+
+        // HVAC properties
+        mProps.add(VehiclePropertyIds.HVAC_FAN_SPEED);
+        mProps.add(VehiclePropertyIds.HVAC_FAN_DIRECTION);
+        mProps.add(VehiclePropertyIds.HVAC_TEMPERATURE_CURRENT);
+        mProps.add(VehiclePropertyIds.HVAC_TEMPERATURE_SET);
+        mProps.add(VehiclePropertyIds.HVAC_DEFROSTER);
+        mProps.add(VehiclePropertyIds.HVAC_ELECTRIC_DEFROSTER_ON);
+        mProps.add(VehiclePropertyIds.HVAC_AC_ON);
+        mProps.add(VehiclePropertyIds.HVAC_MAX_AC_ON);
+        mProps.add(VehiclePropertyIds.HVAC_MAX_DEFROST_ON);
+        mProps.add(VehiclePropertyIds.HVAC_RECIRC_ON);
+        mProps.add(VehiclePropertyIds.HVAC_DUAL_ON);
+        mProps.add(VehiclePropertyIds.HVAC_AUTO_ON);
+        mProps.add(VehiclePropertyIds.HVAC_SEAT_TEMPERATURE);
+        mProps.add(VehiclePropertyIds.HVAC_SIDE_MIRROR_HEAT);
+        mProps.add(VehiclePropertyIds.HVAC_STEERING_WHEEL_HEAT);
+        mProps.add(VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS);
+        mProps.add(VehiclePropertyIds.HVAC_ACTUAL_FAN_SPEED_RPM);
+        mProps.add(VehiclePropertyIds.HVAC_POWER_ON);
+        mProps.add(VehiclePropertyIds.HVAC_FAN_DIRECTION_AVAILABLE);
+        mProps.add(VehiclePropertyIds.HVAC_AUTO_RECIRC_ON);
+        mProps.add(VehiclePropertyIds.HVAC_SEAT_VENTILATION);
+
+        // Info properties
+        mProps.add(VehiclePropertyIds.INFO_VIN);
+        mProps.add(VehiclePropertyIds.INFO_MAKE);
+        mProps.add(VehiclePropertyIds.INFO_MODEL);
+        mProps.add(VehiclePropertyIds.INFO_MODEL_YEAR);
+        mProps.add(VehiclePropertyIds.INFO_FUEL_CAPACITY);
+        mProps.add(VehiclePropertyIds.INFO_FUEL_TYPE);
+        mProps.add(VehiclePropertyIds.INFO_EV_BATTERY_CAPACITY);
+        mProps.add(VehiclePropertyIds.INFO_EV_CONNECTOR_TYPE);
+        mProps.add(VehiclePropertyIds.INFO_FUEL_DOOR_LOCATION);
+        mProps.add(VehiclePropertyIds.INFO_MULTI_EV_PORT_LOCATIONS);
+        mProps.add(VehiclePropertyIds.INFO_EV_PORT_LOCATION);
+        mProps.add(VehiclePropertyIds.INFO_DRIVER_SEAT);
+        mProps.add(VehiclePropertyIds.INFO_EXTERIOR_DIMENSIONS);
+
+        // Sensor properties
+        mProps.add(VehiclePropertyIds.PERF_ODOMETER);
+        mProps.add(VehiclePropertyIds.PERF_VEHICLE_SPEED);
+        mProps.add(VehiclePropertyIds.PERF_VEHICLE_SPEED_DISPLAY);
+        mProps.add(VehiclePropertyIds.ENGINE_COOLANT_TEMP);
+        mProps.add(VehiclePropertyIds.ENGINE_OIL_LEVEL);
+        mProps.add(VehiclePropertyIds.ENGINE_OIL_TEMP);
+        mProps.add(VehiclePropertyIds.ENGINE_RPM);
+        mProps.add(VehiclePropertyIds.WHEEL_TICK);
+        mProps.add(VehiclePropertyIds.FUEL_LEVEL);
+        mProps.add(VehiclePropertyIds.FUEL_DOOR_OPEN);
+        mProps.add(VehiclePropertyIds.EV_BATTERY_LEVEL);
+        mProps.add(VehiclePropertyIds.EV_CHARGE_PORT_OPEN);
+        mProps.add(VehiclePropertyIds.EV_CHARGE_PORT_CONNECTED);
+        mProps.add(VehiclePropertyIds.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE);
+        mProps.add(VehiclePropertyIds.RANGE_REMAINING);
+        mProps.add(VehiclePropertyIds.TIRE_PRESSURE);
+        mProps.add(VehiclePropertyIds.PERF_STEERING_ANGLE);
+        mProps.add(VehiclePropertyIds.PERF_REAR_STEERING_ANGLE);
+        mProps.add(VehiclePropertyIds.GEAR_SELECTION);
+        mProps.add(VehiclePropertyIds.CURRENT_GEAR);
+        mProps.add(VehiclePropertyIds.PARKING_BRAKE_ON);
+        mProps.add(VehiclePropertyIds.PARKING_BRAKE_AUTO_APPLY);
+        mProps.add(VehiclePropertyIds.FUEL_LEVEL_LOW);
+        mProps.add(VehiclePropertyIds.NIGHT_MODE);
+        mProps.add(VehiclePropertyIds.TURN_SIGNAL_STATE);
+        mProps.add(VehiclePropertyIds.IGNITION_STATE);
+        mProps.add(VehiclePropertyIds.ABS_ACTIVE);
+        mProps.add(VehiclePropertyIds.TRACTION_CONTROL_ACTIVE);
+        mProps.add(VehiclePropertyIds.ENV_OUTSIDE_TEMPERATURE);
+        mProps.add(VehiclePropertyIds.HEADLIGHTS_STATE);
+        mProps.add(VehiclePropertyIds.HIGH_BEAM_LIGHTS_STATE);
+        mProps.add(VehiclePropertyIds.FOG_LIGHTS_STATE);
+        mProps.add(VehiclePropertyIds.HAZARD_LIGHTS_STATE);
+        mProps.add(VehiclePropertyIds.HEADLIGHTS_SWITCH);
+        mProps.add(VehiclePropertyIds.HIGH_BEAM_LIGHTS_SWITCH);
+        mProps.add(VehiclePropertyIds.FOG_LIGHTS_SWITCH);
+        mProps.add(VehiclePropertyIds.HAZARD_LIGHTS_SWITCH);
+        mProps.add(VehiclePropertyIds.READING_LIGHTS_STATE);
+        mProps.add(VehiclePropertyIds.CABIN_LIGHTS_STATE);
+        mProps.add(VehiclePropertyIds.READING_LIGHTS_SWITCH);
+        mProps.add(VehiclePropertyIds.CABIN_LIGHTS_SWITCH);
+        // Display_Units
+        mProps.add(VehiclePropertyIds.DISTANCE_DISPLAY_UNITS);
+        mProps.add(VehiclePropertyIds.FUEL_VOLUME_DISPLAY_UNITS);
+        mProps.add(VehiclePropertyIds.TIRE_PRESSURE_DISPLAY_UNITS);
+        mProps.add(VehiclePropertyIds.EV_BATTERY_DISPLAY_UNITS);
+        mProps.add(VehiclePropertyIds.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME);
+        mProps.add(VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS);
+    }
+
+    @After
+    public void tearDown() {
+        if (mCar != null) {
+            mCar.disconnect();
+        }
+    }
+
+    @Test
+    public void testCarPropertyManagerGetter() {
+        for (int propertyId : mProps) {
+            try {
+                switch (propertyId & VehiclePropertyType.MASK) {
+                    case VehiclePropertyType.BOOLEAN:
+                        // The areaId may not match with it in propertyConfig. CarService
+                        // check the permission before checking valid areaId.
+                        mPropertyManager.getBooleanProperty(propertyId, DUMMY_AREA_ID);
+                        break;
+                    case VehiclePropertyType.FLOAT:
+                        mPropertyManager.getFloatProperty(propertyId, DUMMY_AREA_ID);
+                        break;
+                    case VehiclePropertyType.INT32_VEC:
+                        mPropertyManager.getIntArrayProperty(propertyId, DUMMY_AREA_ID);
+                        break;
+                    case VehiclePropertyType.INT32:
+                        mPropertyManager.getIntProperty(propertyId, DUMMY_AREA_ID);
+                        break;
+                    default:
+                        mPropertyManager.getProperty(propertyId, DUMMY_AREA_ID);
+                }
+            } catch (Exception e) {
+                assertWithMessage("Get property: 0x" + Integer.toHexString(propertyId)
+                        + " cause an unexpected exception: " + e)
+                        .that(e).isInstanceOf(SecurityException.class);
+                continue;
+            }
+            assertPropertyNotImplementedInVhal(propertyId);
+        }
+    }
+
+    private void assertPropertyNotImplementedInVhal(int propertyId) {
+        assertWithMessage("Get property : 0x " + Integer.toHexString(propertyId)
+                + " without permission.")
+                .that(mPropertyManager.getProperty(propertyId, DUMMY_AREA_ID)).isNull();
+        Log.w(TAG, "Property id: 0x" + Integer.toHexString(propertyId)
+                + " does not exist in the VHAL implementation.");
+    }
+
+}
diff --git a/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogManagerTest.java b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogManagerTest.java
new file mode 100644
index 0000000..345d7c3
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogManagerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+import static android.car.watchdog.CarWatchdogManager.TIMEOUT_NORMAL;
+
+import static org.testng.Assert.assertThrows;
+
+import android.car.Car;
+import android.car.watchdog.CarWatchdogManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class CarWatchdogManagerTest extends MockedCarTestBase {
+
+    private static final String TAG = CarWatchdogManagerTest.class.getSimpleName();
+
+    private CarWatchdogManager mCarWatchdogManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mCarWatchdogManager = (CarWatchdogManager) getCar().getCarManager(Car.CAR_WATCHDOG_SERVICE);
+    }
+
+    @Test
+    public void testRegisterClient() throws Exception {
+        TestClient client = new TestClient();
+        mCarWatchdogManager.registerClient(getContext().getMainExecutor(), client, TIMEOUT_NORMAL);
+        mCarWatchdogManager.unregisterClient(client);
+    }
+
+    @Test
+    public void testUnregisterUnregisteredClient() throws Exception {
+        TestClient client = new TestClient();
+        mCarWatchdogManager.registerClient(getContext().getMainExecutor(), client, TIMEOUT_NORMAL);
+        mCarWatchdogManager.unregisterClient(client);
+        // The following call should not throw an exception.
+        mCarWatchdogManager.unregisterClient(client);
+    }
+
+    @Test
+    public void testRegisterMultipleClients() {
+        Executor executor = getContext().getMainExecutor();
+        TestClient client1 = new TestClient();
+        TestClient client2 = new TestClient();
+        mCarWatchdogManager.registerClient(executor, client1, TIMEOUT_NORMAL);
+        assertThrows(IllegalStateException.class,
+                () -> mCarWatchdogManager.registerClient(executor, client2, TIMEOUT_NORMAL));
+    }
+
+    public class TestClient extends CarWatchdogManager.CarWatchdogClientCallback {
+        @Override
+        public boolean onCheckHealthStatus(int sessionId, int timeout) {
+            return true;
+        }
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
index dcc6f81..3ab03e5 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
@@ -18,11 +18,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+
 import android.automotive.watchdog.ICarWatchdog;
 import android.automotive.watchdog.ICarWatchdogClient;
 import android.automotive.watchdog.TimeoutLength;
 import android.content.Context;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
@@ -47,6 +52,7 @@
     private static final String TAG = CarWatchdogServiceTest.class.getSimpleName();
 
     @Mock private Context mMockContext;
+    @Mock private IBinder mBinder;
 
     private CarWatchdogService mCarWatchdogService;
     private FakeCarWatchdog mFakeCarWatchdog;
@@ -71,6 +77,18 @@
         assertThat(mFakeCarWatchdog.gotResponse()).isTrue();
     }
 
+    @Test
+    public void testLinkUnlinkDeathRecipient() {
+        mCarWatchdogService.init();
+        try {
+            verify(mBinder).linkToDeath(any(), anyInt());
+        } catch (RemoteException e) {
+            // Do nothing
+        }
+        mCarWatchdogService.release();
+        verify(mBinder).unlinkToDeath(any(), anyInt());
+    }
+
     // FakeCarWatchdog mimics ICarWatchdog daemon in local process.
     final class FakeCarWatchdog extends ICarWatchdog.Default {
 
@@ -98,6 +116,11 @@
         }
 
         @Override
+        public IBinder asBinder() {
+            return mBinder;
+        }
+
+        @Override
         public void registerMediator(ICarWatchdogClient mediator) throws RemoteException {
             mClients.add(mediator);
             mMainHandler.post(() -> {