Merge "Move CAR_IDENTIFICATION to signature" into pi-dev
diff --git a/car-lib/api/current.txt b/car-lib/api/current.txt
index 71eb12c..9cd2e4c 100644
--- a/car-lib/api/current.txt
+++ b/car-lib/api/current.txt
@@ -229,11 +229,11 @@
 
   public final class CarUxRestrictionsManager {
     method public android.car.drivingstate.CarUxRestrictions getCurrentCarUxRestrictions() throws android.car.CarNotConnectedException;
-    method public synchronized void registerListener(android.car.drivingstate.CarUxRestrictionsManager.onUxRestrictionsChangedListener) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+    method public synchronized void registerListener(android.car.drivingstate.CarUxRestrictionsManager.OnUxRestrictionsChangedListener) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
     method public synchronized void unregisterListener() throws android.car.CarNotConnectedException;
   }
 
-  public static abstract interface CarUxRestrictionsManager.onUxRestrictionsChangedListener {
+  public static abstract interface CarUxRestrictionsManager.OnUxRestrictionsChangedListener {
     method public abstract void onUxRestrictionsChanged(android.car.drivingstate.CarUxRestrictions);
   }
 
@@ -245,20 +245,20 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.car.hardware.CarSensorEvent> CREATOR;
-    field public static final int GEAR_DRIVE = 100; // 0x64
-    field public static final int GEAR_EIGHTH = 8; // 0x8
-    field public static final int GEAR_FIFTH = 5; // 0x5
-    field public static final int GEAR_FIRST = 1; // 0x1
-    field public static final int GEAR_FOURTH = 4; // 0x4
-    field public static final int GEAR_NEUTRAL = 0; // 0x0
-    field public static final int GEAR_NINTH = 9; // 0x9
-    field public static final int GEAR_PARK = 101; // 0x65
-    field public static final int GEAR_REVERSE = 102; // 0x66
-    field public static final int GEAR_SECOND = 2; // 0x2
-    field public static final int GEAR_SEVENTH = 7; // 0x7
-    field public static final int GEAR_SIXTH = 6; // 0x6
-    field public static final int GEAR_TENTH = 10; // 0xa
-    field public static final int GEAR_THIRD = 3; // 0x3
+    field public static final int GEAR_DRIVE = 8; // 0x8
+    field public static final int GEAR_EIGHTH = 2048; // 0x800
+    field public static final int GEAR_FIFTH = 256; // 0x100
+    field public static final int GEAR_FIRST = 16; // 0x10
+    field public static final int GEAR_FOURTH = 128; // 0x80
+    field public static final int GEAR_NEUTRAL = 1; // 0x1
+    field public static final int GEAR_NINTH = 4096; // 0x1000
+    field public static final int GEAR_PARK = 4; // 0x4
+    field public static final int GEAR_REVERSE = 2; // 0x2
+    field public static final int GEAR_SECOND = 32; // 0x20
+    field public static final int GEAR_SEVENTH = 1024; // 0x400
+    field public static final int GEAR_SIXTH = 512; // 0x200
+    field public static final int GEAR_TENTH = 8192; // 0x2000
+    field public static final int GEAR_THIRD = 64; // 0x40
     field public static final int IGNITION_STATE_ACC = 3; // 0x3
     field public static final int IGNITION_STATE_LOCK = 1; // 0x1
     field public static final int IGNITION_STATE_OFF = 2; // 0x2
@@ -287,34 +287,34 @@
 
   public final class CarSensorManager {
     method public android.car.hardware.CarSensorEvent getLatestSensorEvent(int) throws android.car.CarNotConnectedException;
+    method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
     method public int[] getSupportedSensors() throws android.car.CarNotConnectedException;
     method public boolean isSensorSupported(int) throws android.car.CarNotConnectedException;
     method public static boolean isSensorSupported(int[], int);
     method public boolean registerListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
     method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener);
     method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int);
-    field public static final int SENSOR_RATE_FAST = 1; // 0x1
-    field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
-    field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
-    field public static final int SENSOR_RATE_UI = 2; // 0x2
-    field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
-    field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
+    field public static final int SENSOR_RATE_FAST = 10; // 0xa
+    field public static final int SENSOR_RATE_FASTEST = 100; // 0x64
+    field public static final int SENSOR_RATE_NORMAL = 1; // 0x1
+    field public static final int SENSOR_RATE_UI = 5; // 0x5
+    field public static final int SENSOR_TYPE_ABS_ACTIVE = 287310858; // 0x1120040a
+    field public static final int SENSOR_TYPE_CAR_SPEED = 291504647; // 0x11600207
     field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
-    field public static final int SENSOR_TYPE_EV_BATTERY_CHARGE_RATE = 31; // 0x1f
-    field public static final int SENSOR_TYPE_EV_BATTERY_LEVEL = 28; // 0x1c
-    field public static final int SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED = 30; // 0x1e
-    field public static final int SENSOR_TYPE_EV_CHARGE_PORT_OPEN = 29; // 0x1d
-    field public static final int SENSOR_TYPE_FUEL_DOOR_OPEN = 27; // 0x1b
-    field public static final int SENSOR_TYPE_FUEL_LEVEL = 5; // 0x5
-    field public static final int SENSOR_TYPE_GEAR = 7; // 0x7
-    field public static final int SENSOR_TYPE_IGNITION_STATE = 22; // 0x16
-    field public static final int SENSOR_TYPE_NIGHT = 9; // 0x9
-    field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
-    field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
-    field public static final int SENSOR_TYPE_RPM = 3; // 0x3
-    field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25; // 0x19
-    field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
-    field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23; // 0x17
+    field public static final int SENSOR_TYPE_EV_BATTERY_CHARGE_RATE = 291504908; // 0x1160030c
+    field public static final int SENSOR_TYPE_EV_BATTERY_LEVEL = 291504905; // 0x11600309
+    field public static final int SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED = 287310603; // 0x1120030b
+    field public static final int SENSOR_TYPE_EV_CHARGE_PORT_OPEN = 287310602; // 0x1120030a
+    field public static final int SENSOR_TYPE_FUEL_DOOR_OPEN = 287310600; // 0x11200308
+    field public static final int SENSOR_TYPE_FUEL_LEVEL = 291504903; // 0x11600307
+    field public static final int SENSOR_TYPE_GEAR = 289408000; // 0x11400400
+    field public static final int SENSOR_TYPE_IGNITION_STATE = 289408009; // 0x11400409
+    field public static final int SENSOR_TYPE_NIGHT = 287310855; // 0x11200407
+    field public static final int SENSOR_TYPE_ODOMETER = 291504644; // 0x11600204
+    field public static final int SENSOR_TYPE_PARKING_BRAKE = 287310850; // 0x11200402
+    field public static final int SENSOR_TYPE_RPM = 291504901; // 0x11600305
+    field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 287310859; // 0x1120040b
+    field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 290521862; // 0x11510306
   }
 
   public static abstract interface CarSensorManager.OnSensorChangedListener {
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index e18e573..d611932 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -32,6 +32,7 @@
     field public static final java.lang.String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
     field public static final java.lang.String POWER_SERVICE = "power";
     field public static final java.lang.String PROJECTION_SERVICE = "projection";
+    field public static final java.lang.String PROPERTY_SERVICE = "property";
     field public static final java.lang.String STORAGE_MONITORING_SERVICE = "storage_monitoring";
     field public static final java.lang.String TEST_SERVICE = "car-service-test";
     field public static final java.lang.String VENDOR_EXTENSION_SERVICE = "vendor_extension";
@@ -83,8 +84,8 @@
 
   public final class VehicleAreaType {
     field public static final int VEHICLE_AREA_TYPE_DOOR = 4; // 0x4
+    field public static final int VEHICLE_AREA_TYPE_GLOBAL = 0; // 0x0
     field public static final int VEHICLE_AREA_TYPE_MIRROR = 5; // 0x5
-    field public static final int VEHICLE_AREA_TYPE_NONE = 0; // 0x0
     field public static final int VEHICLE_AREA_TYPE_SEAT = 3; // 0x3
     field public static final int VEHICLE_AREA_TYPE_WHEEL = 6; // 0x6
     field public static final int VEHICLE_AREA_TYPE_WINDOW = 2; // 0x2
@@ -559,7 +560,7 @@
     method public void registerCallback(android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback) throws android.car.CarNotConnectedException;
     method public <E> void setGlobalProperty(java.lang.Class<E>, int, E) throws android.car.CarNotConnectedException;
     method public <E> void setProperty(java.lang.Class<E>, int, int, E) throws android.car.CarNotConnectedException;
-    method public void unregisterCallback(android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback);
+    method public void unregisterCallback(android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback) throws android.car.CarNotConnectedException;
   }
 
   public static abstract interface CarVendorExtensionManager.CarVendorExtensionCallback {
@@ -581,46 +582,46 @@
     method public void setBooleanProperty(int, int, boolean) throws android.car.CarNotConnectedException;
     method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
     method public void setIntProperty(int, int, int) throws android.car.CarNotConnectedException;
-    method public synchronized void unregisterCallback(android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback);
-    field public static final int ID_DOOR_LOCK = 3; // 0x3
-    field public static final int ID_DOOR_MOVE = 2; // 0x2
-    field public static final int ID_DOOR_POS = 1; // 0x1
-    field public static final int ID_MIRROR_FOLD = 4102; // 0x1006
-    field public static final int ID_MIRROR_LOCK = 4101; // 0x1005
-    field public static final int ID_MIRROR_Y_MOVE = 4100; // 0x1004
-    field public static final int ID_MIRROR_Y_POS = 4099; // 0x1003
-    field public static final int ID_MIRROR_Z_MOVE = 4098; // 0x1002
-    field public static final int ID_MIRROR_Z_POS = 4097; // 0x1001
-    field public static final int ID_SEAT_BACKREST_ANGLE_1_MOVE = 8201; // 0x2009
-    field public static final int ID_SEAT_BACKREST_ANGLE_1_POS = 8200; // 0x2008
-    field public static final int ID_SEAT_BACKREST_ANGLE_2_MOVE = 8203; // 0x200b
-    field public static final int ID_SEAT_BACKREST_ANGLE_2_POS = 8202; // 0x200a
-    field public static final int ID_SEAT_BELT_BUCKLED = 8195; // 0x2003
-    field public static final int ID_SEAT_BELT_HEIGHT_MOVE = 8197; // 0x2005
-    field public static final int ID_SEAT_BELT_HEIGHT_POS = 8196; // 0x2004
-    field public static final int ID_SEAT_DEPTH_MOVE = 8207; // 0x200f
-    field public static final int ID_SEAT_DEPTH_POS = 8206; // 0x200e
-    field public static final int ID_SEAT_FORE_AFT_MOVE = 8199; // 0x2007
-    field public static final int ID_SEAT_FORE_AFT_POS = 8198; // 0x2006
-    field public static final int ID_SEAT_HEADREST_ANGLE_MOVE = 8217; // 0x2019
-    field public static final int ID_SEAT_HEADREST_ANGLE_POS = 8216; // 0x2018
-    field public static final int ID_SEAT_HEADREST_FORE_AFT_MOVE = 8219; // 0x201b
-    field public static final int ID_SEAT_HEADREST_FORE_AFT_POS = 8218; // 0x201a
-    field public static final int ID_SEAT_HEADREST_HEIGHT_MOVE = 8215; // 0x2017
-    field public static final int ID_SEAT_HEADREST_HEIGHT_POS = 8214; // 0x2016
-    field public static final int ID_SEAT_HEIGHT_MOVE = 8205; // 0x200d
-    field public static final int ID_SEAT_HEIGHT_POS = 8204; // 0x200c
-    field public static final int ID_SEAT_LUMBAR_FORE_AFT_MOVE = 8211; // 0x2013
-    field public static final int ID_SEAT_LUMBAR_FORE_AFT_POS = 8210; // 0x2012
-    field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 8213; // 0x2015
-    field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_POS = 8212; // 0x2014
-    field public static final int ID_SEAT_MEMORY_SELECT = 8193; // 0x2001
-    field public static final int ID_SEAT_MEMORY_SET = 8194; // 0x2002
-    field public static final int ID_SEAT_TILT_MOVE = 8209; // 0x2011
-    field public static final int ID_SEAT_TILT_POS = 8208; // 0x2010
-    field public static final int ID_WINDOW_LOCK = 12291; // 0x3003
-    field public static final int ID_WINDOW_MOVE = 12290; // 0x3002
-    field public static final int ID_WINDOW_POS = 12289; // 0x3001
+    method public synchronized void unregisterCallback(android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback) throws android.car.CarNotConnectedException;
+    field public static final int ID_DOOR_LOCK = 371198722; // 0x16200b02
+    field public static final int ID_DOOR_MOVE = 373295873; // 0x16400b01
+    field public static final int ID_DOOR_POS = 373295872; // 0x16400b00
+    field public static final int ID_MIRROR_FOLD = 287312709; // 0x11200b45
+    field public static final int ID_MIRROR_LOCK = 287312708; // 0x11200b44
+    field public static final int ID_MIRROR_Y_MOVE = 339741507; // 0x14400b43
+    field public static final int ID_MIRROR_Y_POS = 339741506; // 0x14400b42
+    field public static final int ID_MIRROR_Z_MOVE = 339741505; // 0x14400b41
+    field public static final int ID_MIRROR_Z_POS = 339741504; // 0x14400b40
+    field public static final int ID_SEAT_BACKREST_ANGLE_1_MOVE = 356518792; // 0x15400b88
+    field public static final int ID_SEAT_BACKREST_ANGLE_1_POS = 356518791; // 0x15400b87
+    field public static final int ID_SEAT_BACKREST_ANGLE_2_MOVE = 356518794; // 0x15400b8a
+    field public static final int ID_SEAT_BACKREST_ANGLE_2_POS = 356518793; // 0x15400b89
+    field public static final int ID_SEAT_BELT_BUCKLED = 354421634; // 0x15200b82
+    field public static final int ID_SEAT_BELT_HEIGHT_MOVE = 356518788; // 0x15400b84
+    field public static final int ID_SEAT_BELT_HEIGHT_POS = 356518787; // 0x15400b83
+    field public static final int ID_SEAT_DEPTH_MOVE = 356518798; // 0x15400b8e
+    field public static final int ID_SEAT_DEPTH_POS = 356518797; // 0x15400b8d
+    field public static final int ID_SEAT_FORE_AFT_MOVE = 356518790; // 0x15400b86
+    field public static final int ID_SEAT_FORE_AFT_POS = 356518789; // 0x15400b85
+    field public static final int ID_SEAT_HEADREST_ANGLE_MOVE = 356518808; // 0x15400b98
+    field public static final int ID_SEAT_HEADREST_ANGLE_POS = 356518807; // 0x15400b97
+    field public static final int ID_SEAT_HEADREST_FORE_AFT_MOVE = 356518810; // 0x15400b9a
+    field public static final int ID_SEAT_HEADREST_FORE_AFT_POS = 356518809; // 0x15400b99
+    field public static final int ID_SEAT_HEADREST_HEIGHT_MOVE = 356518806; // 0x15400b96
+    field public static final int ID_SEAT_HEADREST_HEIGHT_POS = 356518805; // 0x15400b95
+    field public static final int ID_SEAT_HEIGHT_MOVE = 356518796; // 0x15400b8c
+    field public static final int ID_SEAT_HEIGHT_POS = 356518795; // 0x15400b8b
+    field public static final int ID_SEAT_LUMBAR_FORE_AFT_MOVE = 356518802; // 0x15400b92
+    field public static final int ID_SEAT_LUMBAR_FORE_AFT_POS = 356518801; // 0x15400b91
+    field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 356518804; // 0x15400b94
+    field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_POS = 356518803; // 0x15400b93
+    field public static final int ID_SEAT_MEMORY_SELECT = 356518784; // 0x15400b80
+    field public static final int ID_SEAT_MEMORY_SET = 356518785; // 0x15400b81
+    field public static final int ID_SEAT_TILT_MOVE = 356518800; // 0x15400b90
+    field public static final int ID_SEAT_TILT_POS = 356518799; // 0x15400b8f
+    field public static final int ID_WINDOW_LOCK = 322964420; // 0x13400bc4
+    field public static final int ID_WINDOW_MOVE = 322964417; // 0x13400bc1
+    field public static final int ID_WINDOW_POS = 322964416; // 0x13400bc0
   }
 
   public static abstract interface CarCabinManager.CarCabinEventCallback {
@@ -638,7 +639,6 @@
     method public int getIntProperty(int, int) throws android.car.CarNotConnectedException;
     method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
     method public boolean isPropertyAvailable(int, int) throws android.car.CarNotConnectedException;
-    method public static boolean isZonedProperty(int);
     method public synchronized void registerCallback(android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback) throws android.car.CarNotConnectedException;
     method public void setBooleanProperty(int, int, boolean) throws android.car.CarNotConnectedException;
     method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
@@ -647,26 +647,26 @@
     field public static final int FAN_DIRECTION_DEFROST = 4; // 0x4
     field public static final int FAN_DIRECTION_FACE = 1; // 0x1
     field public static final int FAN_DIRECTION_FLOOR = 2; // 0x2
-    field public static final int ID_MIRROR_DEFROSTER_ON = 1; // 0x1
-    field public static final int ID_OUTSIDE_AIR_TEMP = 3; // 0x3
-    field public static final int ID_STEERING_WHEEL_HEAT = 2; // 0x2
-    field public static final int ID_TEMPERATURE_DISPLAY_UNITS = 4; // 0x4
-    field public static final int ID_WINDOW_DEFROSTER_ON = 20481; // 0x5001
-    field public static final int ID_ZONED_AC_ON = 16393; // 0x4009
-    field public static final int ID_ZONED_AIR_RECIRCULATION_ON = 16395; // 0x400b
-    field public static final int ID_ZONED_AUTOMATIC_MODE_ON = 16394; // 0x400a
-    field public static final int ID_ZONED_DUAL_ZONE_ON = 16397; // 0x400d
-    field public static final int ID_ZONED_FAN_DIRECTION = 16391; // 0x4007
-    field public static final int ID_ZONED_FAN_DIRECTION_AVAILABLE = 16390; // 0x4006
-    field public static final int ID_ZONED_FAN_SPEED_RPM = 16389; // 0x4005
-    field public static final int ID_ZONED_FAN_SPEED_SETPOINT = 16388; // 0x4004
-    field public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 16399; // 0x400f
-    field public static final int ID_ZONED_HVAC_POWER_ON = 16387; // 0x4003
-    field public static final int ID_ZONED_MAX_AC_ON = 16396; // 0x400c
-    field public static final int ID_ZONED_MAX_DEFROST_ON = 16398; // 0x400e
-    field public static final int ID_ZONED_SEAT_TEMP = 16392; // 0x4008
-    field public static final int ID_ZONED_TEMP_ACTUAL = 16386; // 0x4002
-    field public static final int ID_ZONED_TEMP_SETPOINT = 16385; // 0x4001
+    field public static final int ID_MIRROR_DEFROSTER_ON = 339739916; // 0x1440050c
+    field public static final int ID_OUTSIDE_AIR_TEMP = 291505923; // 0x11600703
+    field public static final int ID_STEERING_WHEEL_HEAT = 289408269; // 0x1140050d
+    field public static final int ID_TEMPERATURE_DISPLAY_UNITS = 289408270; // 0x1140050e
+    field public static final int ID_WINDOW_DEFROSTER_ON = 320865540; // 0x13200504
+    field public static final int ID_ZONED_AC_ON = 354419973; // 0x15200505
+    field public static final int ID_ZONED_AIR_RECIRCULATION_ON = 354419976; // 0x15200508
+    field public static final int ID_ZONED_AUTOMATIC_MODE_ON = 354419978; // 0x1520050a
+    field public static final int ID_ZONED_DUAL_ZONE_ON = 354419977; // 0x15200509
+    field public static final int ID_ZONED_FAN_DIRECTION = 356517121; // 0x15400501
+    field public static final int ID_ZONED_FAN_DIRECTION_AVAILABLE = 356582673; // 0x15410511
+    field public static final int ID_ZONED_FAN_SPEED_RPM = 356517135; // 0x1540050f
+    field public static final int ID_ZONED_FAN_SPEED_SETPOINT = 356517120; // 0x15400500
+    field public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 354419986; // 0x15200512
+    field public static final int ID_ZONED_HVAC_POWER_ON = 354419984; // 0x15200510
+    field public static final int ID_ZONED_MAX_AC_ON = 354419974; // 0x15200506
+    field public static final int ID_ZONED_MAX_DEFROST_ON = 354419975; // 0x15200507
+    field public static final int ID_ZONED_SEAT_TEMP = 356517131; // 0x1540050b
+    field public static final int ID_ZONED_TEMP_ACTUAL = 358614274; // 0x15600502
+    field public static final int ID_ZONED_TEMP_SETPOINT = 358614275; // 0x15600503
   }
 
   public static abstract interface CarHvacManager.CarHvacEventCallback {
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 5d16640..738a423 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -29,6 +29,7 @@
 import android.car.hardware.cabin.CarCabinManager;
 import android.car.hardware.hvac.CarHvacManager;
 import android.car.hardware.power.CarPowerManager;
+import android.car.hardware.property.CarPropertyManager;
 import android.car.media.CarAudioManager;
 import android.car.navigation.CarNavigationStatusManager;
 import android.car.settings.CarConfigurationManager;
@@ -126,6 +127,12 @@
      * @hide
      */
     @SystemApi
+    public static final String PROPERTY_SERVICE = "property";
+
+    /**
+     * @hide
+     */
+    @SystemApi
     public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension";
 
     /**
@@ -766,6 +773,10 @@
             case PROJECTION_SERVICE:
                 manager = new CarProjectionManager(binder, mEventHandler);
                 break;
+            case PROPERTY_SERVICE:
+                manager = new CarPropertyManager(binder, mEventHandler, false,
+                                                 "CarPropertyManager");
+                break;
             case VENDOR_EXTENSION_SERVICE:
                 manager = new CarVendorExtensionManager(binder, mEventHandler);
                 break;
diff --git a/car-lib/src/android/car/CarInfoManager.java b/car-lib/src/android/car/CarInfoManager.java
index 47d90c9..b228ca7 100644
--- a/car-lib/src/android/car/CarInfoManager.java
+++ b/car-lib/src/android/car/CarInfoManager.java
@@ -16,45 +16,46 @@
 
 package android.car;
 
+import static java.lang.Integer.toHexString;
+
 import android.annotation.Nullable;
-import android.car.EvConnectorType;
-import android.car.FuelType;
-import android.car.annotation.FutureFeature;
 import android.car.annotation.ValueTypeDef;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.ICarProperty;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-
-
-import com.android.internal.annotations.GuardedBy;
+import android.util.Log;
 
 
 /**
  * Utility to retrieve various static information from car. Each data are grouped as {@link Bundle}
  * and relevant data can be checked from {@link Bundle} using pre-specified keys.
  */
-public final class CarInfoManager implements CarManagerBase {
+public final class CarInfoManager implements CarManagerBase{
 
+    private static final boolean DBG = false;
+    private static final String TAG = "CarInfoManager";
     /**
      * Key for manufacturer of the car. Passed in basic info Bundle.
      * @hide
      */
-    @ValueTypeDef(type = String.class)
-    public static final String BASIC_INFO_KEY_MANUFACTURER = "android.car.manufacturer";
+    @ValueTypeDef(type = Integer.class)
+    public static final int BASIC_INFO_KEY_MANUFACTURER = 0x11100101;
     /**
      * Key for model name of the car. This information may not necessarily allow distinguishing
      * different car models as the same name may be used for different cars depending on
      * manufacturers. Passed in basic info Bundle.
      * @hide
      */
-    @ValueTypeDef(type = String.class)
-    public static final String BASIC_INFO_KEY_MODEL = "android.car.model";
+    @ValueTypeDef(type = Integer.class)
+    public static final int BASIC_INFO_KEY_MODEL = 0x11100102;
     /**
      * Key for model year of the car in AC. Passed in basic info Bundle.
      * @hide
      */
     @ValueTypeDef(type = Integer.class)
-    public static final String BASIC_INFO_KEY_MODEL_YEAR = "android.car.model-year";
+    public static final int BASIC_INFO_KEY_MODEL_YEAR = 0x11400103;
     /**
      * Key for unique identifier for the car. This is not VIN, and id is persistent until user
      * resets it. Passed in basic info Bundle.
@@ -92,38 +93,38 @@
      * @hide
      */
     @ValueTypeDef(type = Integer.class)
-    public static final String BASIC_INFO_FUEL_CAPACITY = "android.car.fuel-capacity";
+    public static final int BASIC_INFO_FUEL_CAPACITY = 0x11600104;
     /**
      * Key for Fuel Types.  This is an array of fuel types the vehicle supports.
      * Passed in basic info Bundle.
      * @hide
      */
     @ValueTypeDef(type = Integer.class)
-    public static final String BASIC_INFO_FUEL_TYPES = "android.car.fuel-types";
+    public static final int BASIC_INFO_FUEL_TYPES = 0x11410105;
     /**
      * Key for EV Battery Capacity in WH.  Passed in basic info Bundle.
      * @hide
      */
     @ValueTypeDef(type = Integer.class)
-    public static final String BASIC_INFO_EV_BATTERY_CAPACITY = "android.car.ev-battery-capacity";
+    public static final int BASIC_INFO_EV_BATTERY_CAPACITY = 0x11600106;
     /**
      * Key for EV Connector Types.  This is an array of connector types the vehicle supports.
      * Passed in basic info Bundle.
      * @hide
      */
     @ValueTypeDef(type = Integer.class)
-    public static final String BASIC_INFO_EV_CONNECTOR_TYPES = "android.car.ev-connector-types";
+    public static final int BASIC_INFO_EV_CONNECTOR_TYPES = 0x11410107;
 
-    private final ICarInfo mService;
-
-    @GuardedBy("this")
-    private Bundle mBasicInfo;
+    private final ICarProperty mService;
 
     /**
      * @return Manufacturer of the car.  Null if not available.
      */
-    public @android.annotation.Nullable String getManufacturer() throws CarNotConnectedException {
-        return getBasicInfo().getString(BASIC_INFO_KEY_MANUFACTURER);
+    @Nullable
+    public String getManufacturer() throws CarNotConnectedException {
+        CarPropertyValue<String> carProp = getProperty(String.class,
+                BASIC_INFO_KEY_MANUFACTURER, 0);
+        return carProp != null ? carProp.getValue() : null;
     }
 
     /**
@@ -131,24 +132,30 @@
      * may not necessarily allow distinguishing different car models as the same
      * name may be used for different cars depending on manufacturers.
      */
-    public @Nullable String getModel() throws CarNotConnectedException {
-        return getBasicInfo().getString(BASIC_INFO_KEY_MODEL);
+    @Nullable
+    public String getModel() throws CarNotConnectedException {
+        CarPropertyValue<String> carProp = getProperty(String.class, BASIC_INFO_KEY_MODEL, 0);
+        return carProp != null ? carProp.getValue() : null;
     }
 
     /**
      * @return Model year of the car in AC.  Null if not available.
      */
-    public @Nullable String getModelYear() throws CarNotConnectedException {
-        return getBasicInfo().getString(BASIC_INFO_KEY_MODEL_YEAR);
+    @Nullable
+    public String getModelYear() throws CarNotConnectedException {
+        CarPropertyValue<String> carProp = getProperty(String.class,
+                BASIC_INFO_KEY_MODEL_YEAR, 0);
+        return carProp != null ? carProp.getValue() : null;
     }
 
     /**
      * @return Unique identifier for the car. This is not VIN, and vehicle id is
      * persistent until user resets it. This ID is guaranteed to be always
      * available.
+     * TODO: BASIC_INFO_KEY_VEHICLE_ID property?
      */
     public String getVehicleId() throws CarNotConnectedException {
-        return getBasicInfo().getString(BASIC_INFO_KEY_VEHICLE_ID);
+        return "";
     }
 
     /**
@@ -156,7 +163,9 @@
      *         fuel.
      */
     public float getFuelCapacity() throws CarNotConnectedException {
-        return getBasicInfo().getFloat(BASIC_INFO_FUEL_CAPACITY);
+        CarPropertyValue<Float> carProp = getProperty(Float.class,
+                BASIC_INFO_FUEL_CAPACITY, 0);
+        return carProp != null ? carProp.getValue() : 0f;
     }
 
     /**
@@ -164,7 +173,8 @@
      *         types available.
      */
     public @FuelType.Enum int[] getFuelTypes() throws CarNotConnectedException {
-        return getIntArray(BASIC_INFO_FUEL_TYPES);
+        CarPropertyValue<int[]> carProp = getProperty(int[].class, BASIC_INFO_FUEL_TYPES, 0);
+        return carProp != null ? carProp.getValue() : new int[0];
     }
 
     /**
@@ -172,7 +182,9 @@
      *         battery.
      */
     public float getEvBatteryCapacity() throws CarNotConnectedException {
-        return getBasicInfo().getFloat(BASIC_INFO_EV_BATTERY_CAPACITY);
+        CarPropertyValue<Float> carProp = getProperty(Float.class,
+                BASIC_INFO_EV_BATTERY_CAPACITY, 0);
+        return carProp != null ? carProp.getValue() : 0f;
     }
 
     /**
@@ -180,77 +192,42 @@
      *         no connector types available.
      */
     public @EvConnectorType.Enum int[] getEvConnectorTypes() throws CarNotConnectedException {
-        return getIntArray(BASIC_INFO_EV_CONNECTOR_TYPES);
-    }
-
-    /**
-     * Get product configuration string. Contents of this string is product specific but it should
-     * be composed of key-value pairs with the format of:
-     *   key1=value1;key2=value2;...
-     * @return null if such information is not available in this car.
-     * @throws CarNotConnectedException
-     * @hide
-     */
-    @FutureFeature
-    public @Nullable String getProductConfiguration() throws CarNotConnectedException {
-        try {
-            return mService.getStringInfo(INFO_KEY_PRODUCT_CONFIGURATION);
-        } catch (IllegalStateException e) {
-            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
-        } catch (RemoteException e) {
-            throw new CarNotConnectedException(e);
-        }
-        return null;
-    }
-
-    /**
-     * Get {@link android.os.Bundle} containing basic car information. Check
-     * {@link #BASIC_INFO_KEY_MANUFACTURER}, {@link #BASIC_INFO_KEY_MODEL},
-     * {@link #BASIC_INFO_KEY_MODEL_YEAR}, and {@link #BASIC_INFO_KEY_VEHICLE_ID} for supported
-     * keys in the {@link android.os.Bundle}.
-     * @return {@link android.os.Bundle} containing basic car info.
-     * @throws CarNotConnectedException
-     */
-    private synchronized Bundle getBasicInfo() throws CarNotConnectedException {
-        if (mBasicInfo != null) {
-            return mBasicInfo;
-        }
-        try {
-            mBasicInfo = mService.getBasicInfo();
-        } catch (IllegalStateException e) {
-            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
-        } catch (RemoteException e) {
-            throw new CarNotConnectedException(e);
-        }
-        return mBasicInfo;
-    }
-
-    /**
-     * Get int array from property ID.
-     * @param id property ID to get
-     * @return array of property values or empty array if the property isn't
-     *         available.
-     * @throws CarNotConnectedException
-     */
-    private int[] getIntArray(String id) throws CarNotConnectedException {
-        int[] retVal = getBasicInfo().getIntArray(id);
-        if (retVal == null) {
-            // Create an empty array
-            retVal = new int[0];
-        }
-        return retVal;
+        CarPropertyValue<int[]> carProp = getProperty(int[].class,
+                BASIC_INFO_EV_CONNECTOR_TYPES, 0);
+        return carProp != null ? carProp.getValue() : new int[0];
     }
 
     /** @hide */
     CarInfoManager(IBinder service) {
-        mService = ICarInfo.Stub.asInterface(service);
+        mService = ICarProperty.Stub.asInterface(service);
     }
 
     /** @hide */
-    @Override
     public void onCarDisconnected() {
-        synchronized (this) {
-            mBasicInfo = null;
+    }
+
+    private  <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area)
+            throws CarNotConnectedException {
+        if (DBG) {
+            Log.d(TAG, "getProperty, propId: 0x" + toHexString(propId)
+                    + ", area: 0x" + toHexString(area) + ", class: " + clazz);
+        }
+        try {
+            CarPropertyValue<E> propVal = mService.getProperty(propId, area);
+            if (propVal != null && propVal.getValue() != null) {
+                Class<?> actualClass = propVal.getValue().getClass();
+                if (actualClass != clazz) {
+                    throw new IllegalArgumentException("Invalid property type. " + "Expected: "
+                            + clazz + ", but was: " + actualClass);
+                }
+            }
+            return propVal;
+        } catch (RemoteException e) {
+            Log.e(TAG, "getProperty failed with " + e.toString()
+                    + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
+            throw new CarNotConnectedException(e);
+        } catch (IllegalArgumentException e)  {
+            return null;
         }
     }
 }
diff --git a/car-lib/src/android/car/VehicleAreaType.java b/car-lib/src/android/car/VehicleAreaType.java
index f484487..971f000 100644
--- a/car-lib/src/android/car/VehicleAreaType.java
+++ b/car-lib/src/android/car/VehicleAreaType.java
@@ -31,7 +31,7 @@
 @SystemApi
 public final class VehicleAreaType {
     /** Used for global properties */
-    public static final int VEHICLE_AREA_TYPE_NONE = 0;
+    public static final int VEHICLE_AREA_TYPE_GLOBAL = 0;
     public static final int VEHICLE_AREA_TYPE_WINDOW = 2;
     public static final int VEHICLE_AREA_TYPE_SEAT = 3;
     public static final int VEHICLE_AREA_TYPE_DOOR = 4;
@@ -41,7 +41,7 @@
 
     /** @hide */
     @IntDef({
-        VEHICLE_AREA_TYPE_NONE,
+        VEHICLE_AREA_TYPE_GLOBAL,
         VEHICLE_AREA_TYPE_WINDOW,
         VEHICLE_AREA_TYPE_SEAT,
         VEHICLE_AREA_TYPE_DOOR,
diff --git a/car-lib/src/android/car/VehiclePropertyType.java b/car-lib/src/android/car/VehiclePropertyType.java
new file mode 100644
index 0000000..b236d2d
--- /dev/null
+++ b/car-lib/src/android/car/VehiclePropertyType.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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 android.car;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Value type of VehicleProperty
+ * @hide
+ */
+public class VehiclePropertyType {
+    public static final int STRING          = 0x00100000;
+    public static final int BOOLEAN         = 0x00200000;
+    public static final int INT32           = 0x00400000;
+    public static final int INT32_VEC       = 0x00410000;
+    public static final int INT64           = 0x00500000;
+    public static final int INT64_VEC       = 0x00510000;
+    public static final int FLOAT           = 0x00600000;
+    public static final int FLOAT_VEC       = 0x00610000;
+    public static final int BYTES           = 0x00700000;
+    public static final int MIXED           = 0x00e00000;
+    public static final int MASK            = 0x00ff0000;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STRING,
+            BOOLEAN,
+            INT32,
+            INT32_VEC,
+            INT64,
+            INT64_VEC,
+            FLOAT,
+            FLOAT_VEC,
+            BYTES,
+            /**
+             * Any combination of scalar or vector types. The exact format must be
+             * provided in the description of the property.
+            */
+            MIXED,
+            MASK
+    })
+    public @interface Enum {}
+    private VehiclePropertyType() {}
+}
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index 1c2c334..57e7d60 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -45,7 +45,7 @@
     private final Context mContext;
     private final ICarUxRestrictionsManager mUxRService;
     private final EventCallbackHandler mEventCallbackHandler;
-    private onUxRestrictionsChangedListener mUxRListener;
+    private OnUxRestrictionsChangedListener mUxRListener;
     private CarUxRestrictionsChangeListenerToService mListenerToService;
 
 
@@ -67,7 +67,7 @@
      * Listener Interface for clients to implement to get updated on driving state related
      * changes.
      */
-    public interface onUxRestrictionsChangedListener {
+    public interface OnUxRestrictionsChangedListener {
         /**
          * Called when the UX restrictions due to a car's driving state changes.
          *
@@ -77,15 +77,15 @@
     }
 
     /**
-     * Register a {@link onUxRestrictionsChangedListener} for listening to changes in the
+     * Register a {@link OnUxRestrictionsChangedListener} for listening to changes in the
      * UX Restrictions to adhere to.
      * <p>
      * If a listener has already been registered, it has to be unregistered before registering
      * the new one.
      *
-     * @param listener {@link onUxRestrictionsChangedListener}
+     * @param listener {@link OnUxRestrictionsChangedListener}
      */
-    public synchronized void registerListener(@NonNull onUxRestrictionsChangedListener listener)
+    public synchronized void registerListener(@NonNull OnUxRestrictionsChangedListener listener)
             throws CarNotConnectedException, IllegalArgumentException {
         if (listener == null) {
             if (VDBG) {
@@ -117,7 +117,7 @@
     }
 
     /**
-     * Unregister the registered {@link onUxRestrictionsChangedListener}
+     * Unregister the registered {@link OnUxRestrictionsChangedListener}
      */
     public synchronized void unregisterListener()
             throws CarNotConnectedException {
@@ -218,7 +218,7 @@
         if (restrictionInfo == null) {
             return;
         }
-        onUxRestrictionsChangedListener listener;
+        OnUxRestrictionsChangedListener listener;
         synchronized (this) {
             listener = mUxRListener;
         }
@@ -226,5 +226,4 @@
             listener.onUxRestrictionsChanged(restrictionInfo);
         }
     }
-
 }
diff --git a/car-lib/src/android/car/hardware/CarPropertyConfig.java b/car-lib/src/android/car/hardware/CarPropertyConfig.java
index 965253a..be2b86c 100644
--- a/car-lib/src/android/car/hardware/CarPropertyConfig.java
+++ b/car-lib/src/android/car/hardware/CarPropertyConfig.java
@@ -98,7 +98,7 @@
 
     /** Returns true if this property doesn't hold car area-specific configuration */
     public boolean isGlobalProperty() {
-        return mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
+        return mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
     }
 
     public int getAreaCount() {
diff --git a/car-lib/src/android/car/hardware/CarSensorEvent.java b/car-lib/src/android/car/hardware/CarSensorEvent.java
index abf2b2a..86af063 100644
--- a/car-lib/src/android/car/hardware/CarSensorEvent.java
+++ b/car-lib/src/android/car/hardware/CarSensorEvent.java
@@ -37,39 +37,39 @@
      *  sensor type.
      *  GEAR_NEUTRAL means transmission gear is in neutral state, and the car may be moving.
      */
-    public static final int GEAR_NEUTRAL    = 0;
+    public static final int GEAR_NEUTRAL    = 0x0001;
     /**
      * intValues[0] from 1 to 99 represents transmission gear number for moving forward.
      * GEAR_FIRST is for gear number 1.
      */
-    public static final int GEAR_FIRST      = 1;
+    public static final int GEAR_FIRST      = 0x0010;
     /** Gear number 2. */
-    public static final int GEAR_SECOND     = 2;
+    public static final int GEAR_SECOND     = 0x0020;
     /** Gear number 3. */
-    public static final int GEAR_THIRD      = 3;
+    public static final int GEAR_THIRD      = 0x0040;
     /** Gear number 4. */
-    public static final int GEAR_FOURTH     = 4;
+    public static final int GEAR_FOURTH     = 0x0080;
     /** Gear number 5. */
-    public static final int GEAR_FIFTH      = 5;
+    public static final int GEAR_FIFTH      = 0x0100;
     /** Gear number 6. */
-    public static final int GEAR_SIXTH      = 6;
+    public static final int GEAR_SIXTH      = 0x0200;
     /** Gear number 7. */
-    public static final int GEAR_SEVENTH    = 7;
+    public static final int GEAR_SEVENTH    = 0x0400;
     /** Gear number 8. */
-    public static final int GEAR_EIGHTH     = 8;
+    public static final int GEAR_EIGHTH     = 0x0800;
     /** Gear number 9. */
-    public static final int GEAR_NINTH      = 9;
+    public static final int GEAR_NINTH      = 0x1000;
     /** Gear number 10. */
-    public static final int GEAR_TENTH      = 10;
+    public static final int GEAR_TENTH      = 0x2000;
     /**
      * This is for transmission without specific gear number for moving forward like CVT. It tells
      * that car is in a transmission state to move it forward.
      */
-    public static final int GEAR_DRIVE      = 100;
+    public static final int GEAR_DRIVE      = 0x0008;
     /** Gear in parking state */
-    public static final int GEAR_PARK       = 101;
+    public static final int GEAR_PARK       = 0x0004;
     /** Gear in reverse */
-    public static final int GEAR_REVERSE    = 102;
+    public static final int GEAR_REVERSE    = 0x0002;
 
     /**
      * Ignition state is unknown.
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index dd05bc8..2536604 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -24,28 +24,31 @@
 import android.car.CarLibLog;
 import android.car.CarManagerBase;
 import android.car.CarNotConnectedException;
+import android.car.VehiclePropertyType;
+import android.car.hardware.property.CarPropertyManager;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.ArraySet;
 import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.car.internal.CarRatedListeners;
-import com.android.car.internal.SingleMessageHandler;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
-import java.util.function.Consumer;
+
 
 /**
  *  API for monitoring car sensor data.
  */
 public final class CarSensorManager implements CarManagerBase {
+    private static final  boolean DBG = false;
+    private static final String TAG = "CarSensorManager";
+    private final CarPropertyManager mCarPropertyMgr;
     /** @hide */
     public static final int SENSOR_TYPE_RESERVED1                   = 1;
     /**
@@ -53,41 +56,41 @@
      * Sensor data in {@link CarSensorEvent} is a float which will be >= 0.
      * This requires {@link Car#PERMISSION_SPEED} permission.
      */
-    public static final int SENSOR_TYPE_CAR_SPEED                   = 2;
+    public static final int SENSOR_TYPE_CAR_SPEED                   = 0x11600207;
     /**
      * Represents engine RPM of the car. Sensor data in {@link CarSensorEvent} is a float.
      */
-    public static final int SENSOR_TYPE_RPM                         = 3;
+    public static final int SENSOR_TYPE_RPM                         = 0x11600305;
     /**
      * Total travel distance of the car in Kilometer. Sensor data is a float.
      * This requires {@link Car#PERMISSION_MILEAGE} permission.
      */
-    public static final int SENSOR_TYPE_ODOMETER                    = 4;
+    public static final int SENSOR_TYPE_ODOMETER                    = 0x11600204;
     /**
      * Indicates fuel level of the car.
      * In {@link CarSensorEvent}, represents fuel level in milliliters.
      * This requires {@link Car#PERMISSION_ENERGY} permission.
      */
-    public static final int SENSOR_TYPE_FUEL_LEVEL                  = 5;
+    public static final int SENSOR_TYPE_FUEL_LEVEL                  = 0x11600307;
     /**
      * Represents the current status of parking brake. Sensor data in {@link CarSensorEvent} is an
      * intValues[0]. Value of 1 represents parking brake applied while 0 means the other way
      * around. For this sensor, rate in {@link #registerListener(OnSensorChangedListener, int, int)}
      * will be ignored and all changes will be notified.
      */
-    public static final int SENSOR_TYPE_PARKING_BRAKE               = 6;
+    public static final int SENSOR_TYPE_PARKING_BRAKE               = 0x11200402;
     /**
      * This represents the current position of transmission gear. Sensor data in
      * {@link CarSensorEvent} is an intValues[0]. For the meaning of the value, check
      * {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
      */
-    public static final int SENSOR_TYPE_GEAR                        = 7;
+    public static final int SENSOR_TYPE_GEAR                        = 0x11400400;
     /** @hide */
     public static final int SENSOR_TYPE_RESERVED8                   = 8;
     /**
      * Day/night sensor. Sensor data is intValues[0].
      */
-    public static final int SENSOR_TYPE_NIGHT                       = 9;
+    public static final int SENSOR_TYPE_NIGHT                       = 0x11200407;
     /** @hide */
     public static final int SENSOR_TYPE_RESERVED10                  = 10;
     /** @hide */
@@ -118,7 +121,7 @@
      * Represents ignition state. The value should be one of the constants that starts with
      * IGNITION_STATE_* in {@link CarSensorEvent}.
      */
-    public static final int SENSOR_TYPE_IGNITION_STATE              = 22;
+    public static final int SENSOR_TYPE_IGNITION_STATE              = 0x11400409;
     /**
      * Represents wheel distance in millimeters.  Some cars may not have individual sensors on each
      * wheel.  If a value is not available, Long.MAX_VALUE will be reported.  The wheel distance
@@ -126,23 +129,23 @@
      * distance shall be reset to zero each time a vehicle is started by the user.
      * This requires {@link Car#PERMISSION_SPEED} permission.
      */
-    public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE         = 23;
+    public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE         = 0x11510306;
     /**
      * Set to true when ABS is active.  This sensor is event driven.
      * This requires {@link Car#PERMISSION_CAR_DYNAMICS_STATE} permission.
      */
-    public static final int SENSOR_TYPE_ABS_ACTIVE                  = 24;
+    public static final int SENSOR_TYPE_ABS_ACTIVE                  = 0x1120040a;
     /**
      * Set to true when traction control is active.  This sensor is event driven.
      * This requires {@link Car#PERMISSION_CAR_DYNAMICS_STATE} permission.
      */
-    public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE     = 25;
+    public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE     = 0x1120040b;
     /** @hide */
     public static final int SENSOR_TYPE_RESERVED26                  = 26;
     /**
      * Set to true if the fuel door is open.
      */
-    public static final int SENSOR_TYPE_FUEL_DOOR_OPEN              = 27;
+    public static final int SENSOR_TYPE_FUEL_DOOR_OPEN              = 0x11200308;
 
     /**
      * Indicates battery level of the car.
@@ -152,129 +155,154 @@
      * of the vehicle.
      * This requires {@link Car#PERMISSION_ENERGY} permission.
      */
-    public static final int SENSOR_TYPE_EV_BATTERY_LEVEL            = 28;
+    public static final int SENSOR_TYPE_EV_BATTERY_LEVEL            = 0x11600309;
     /**
      * Set to true if EV charging port is open.
      */
-    public static final int SENSOR_TYPE_EV_CHARGE_PORT_OPEN         = 29;
+    public static final int SENSOR_TYPE_EV_CHARGE_PORT_OPEN         = 0x1120030a;
     /**
      * Set to true if EV charging port is connected.
      */
-    public static final int SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED    = 30;
+    public static final int SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED    = 0x1120030b;
     /**
      *  Indicates the instantaneous battery charging rate in mW.
      *  This requires {@link Car#PERMISSION_ENERGY} permission.
      */
-    public static final int SENSOR_TYPE_EV_BATTERY_CHARGE_RATE      = 31;
+    public static final int SENSOR_TYPE_EV_BATTERY_CHARGE_RATE      = 0x1160030c;
     /**
      * Oil level sensor.
      * This requires {@link Car#PERMISSION_CAR_ENGINE_DETAILED} permission
      * @hide
      */
-    public static final int SENSOR_TYPE_ENGINE_OIL_LEVEL            = 32;
+    public static final int SENSOR_TYPE_ENGINE_OIL_LEVEL            = 0x11400303;
 
-    /**
-     * Sensor type bigger than this is invalid. Always update this after adding a new sensor.
-     * @hide
-     */
-    private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_ENGINE_OIL_LEVEL;
-
-    /**
-     * Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
-     * {@link #SENSOR_TYPE_VENDOR_EXTENSION_END}] is for each car vendor's to use.
-     * This should be only used for system app to access sensors not defined as standard types.
-     * So the sensor supported in this range can vary depending on car models / manufacturers.
-     * 3rd party apps should not use sensors in this range as they are not compatible across
-     * different cars. Additionally 3rd party apps trying to access sensor in this range will get
-     * security exception as their access is restricted to system apps.
-     *
-     * @hide
-     */
-    public static final int SENSOR_TYPE_VENDOR_EXTENSION_START = 0x60000000;
-    public static final int SENSOR_TYPE_VENDOR_EXTENSION_END   = 0x6fffffff;
 
     /** @hide */
     @IntDef({
-        SENSOR_TYPE_CAR_SPEED,
-        SENSOR_TYPE_RPM,
-        SENSOR_TYPE_ODOMETER,
-        SENSOR_TYPE_FUEL_LEVEL,
-        SENSOR_TYPE_PARKING_BRAKE,
-        SENSOR_TYPE_GEAR,
-        SENSOR_TYPE_NIGHT,
-        SENSOR_TYPE_ENVIRONMENT,
-        SENSOR_TYPE_IGNITION_STATE,
-        SENSOR_TYPE_WHEEL_TICK_DISTANCE,
-        SENSOR_TYPE_ABS_ACTIVE,
-        SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
-        SENSOR_TYPE_FUEL_DOOR_OPEN,
-        SENSOR_TYPE_EV_BATTERY_LEVEL,
-        SENSOR_TYPE_EV_CHARGE_PORT_OPEN,
-        SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED,
-        SENSOR_TYPE_EV_BATTERY_CHARGE_RATE,
-        SENSOR_TYPE_ENGINE_OIL_LEVEL,
+            SENSOR_TYPE_CAR_SPEED,
+            SENSOR_TYPE_RPM,
+            SENSOR_TYPE_ODOMETER,
+            SENSOR_TYPE_FUEL_LEVEL,
+            SENSOR_TYPE_PARKING_BRAKE,
+            SENSOR_TYPE_GEAR,
+            SENSOR_TYPE_NIGHT,
+            SENSOR_TYPE_ENVIRONMENT,
+            SENSOR_TYPE_IGNITION_STATE,
+            SENSOR_TYPE_WHEEL_TICK_DISTANCE,
+            SENSOR_TYPE_ABS_ACTIVE,
+            SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
+            SENSOR_TYPE_FUEL_DOOR_OPEN,
+            SENSOR_TYPE_EV_BATTERY_LEVEL,
+            SENSOR_TYPE_EV_CHARGE_PORT_OPEN,
+            SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED,
+            SENSOR_TYPE_EV_BATTERY_CHARGE_RATE,
+            SENSOR_TYPE_ENGINE_OIL_LEVEL,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SensorType {}
 
+    private final ArraySet<Integer> mSensorConfigIds = new ArraySet<>(Arrays.asList(new Integer[]{
+            SENSOR_TYPE_CAR_SPEED,
+            SENSOR_TYPE_RPM,
+            SENSOR_TYPE_ODOMETER,
+            SENSOR_TYPE_FUEL_LEVEL,
+            SENSOR_TYPE_PARKING_BRAKE,
+            SENSOR_TYPE_GEAR,
+            SENSOR_TYPE_NIGHT,
+            SENSOR_TYPE_ENVIRONMENT,
+            SENSOR_TYPE_IGNITION_STATE,
+            SENSOR_TYPE_WHEEL_TICK_DISTANCE,
+            SENSOR_TYPE_ABS_ACTIVE,
+            SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
+            SENSOR_TYPE_FUEL_DOOR_OPEN,
+            SENSOR_TYPE_EV_BATTERY_LEVEL,
+            SENSOR_TYPE_EV_CHARGE_PORT_OPEN,
+            SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED,
+            SENSOR_TYPE_EV_BATTERY_CHARGE_RATE,
+            SENSOR_TYPE_ENGINE_OIL_LEVEL,
+    }));
+
     /** Read sensor in default normal rate set for each sensors. This is default rate. */
-    public static final int SENSOR_RATE_NORMAL  = 3;
-    public static final int SENSOR_RATE_UI = 2;
-    public static final int SENSOR_RATE_FAST = 1;
+    public static final int SENSOR_RATE_NORMAL  = 1;
+    public static final int SENSOR_RATE_UI = 5;
+    public static final int SENSOR_RATE_FAST = 10;
     /** Read sensor at the maximum rate. Actual rate will be different depending on the sensor. */
-    public static final int SENSOR_RATE_FASTEST = 0;
+    public static final int SENSOR_RATE_FASTEST = 100;
 
     /** @hide */
     @IntDef({
-        SENSOR_RATE_NORMAL,
-        SENSOR_RATE_UI,
-        SENSOR_RATE_FAST,
-        SENSOR_RATE_FASTEST
+            SENSOR_RATE_NORMAL,
+            SENSOR_RATE_UI,
+            SENSOR_RATE_FAST,
+            SENSOR_RATE_FASTEST
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SensorRate {}
 
-    private static final int MSG_SENSOR_EVENTS = 0;
-
-    private final ICarSensor mService;
-
-    private CarSensorEventListenerToService mCarSensorEventListenerToService;
+    private CarPropertyEventListenerToBase mCarPropertyEventListener = null;
 
     /**
-     * To keep record of locally active sensors. Key is sensor type. This is used as a basic lock
-     * for all client accesses.
+     * To keep record of CarPropertyEventListenerToBase
      */
-    private final SparseArray<CarSensorListeners> mActiveSensorListeners = new SparseArray<>();
+    private final HashMap<OnSensorChangedListener, CarPropertyEventListenerToBase> mListenerMap =
+            new HashMap<>();
+    /**
+     * Listener for car sensor data change.
+     * Callbacks are called in the Looper context.
+     */
+    public interface OnSensorChangedListener {
+        /**
+         * Called when there is a new sensor data from car.
+         * @param event Incoming sensor event for the given sensor type.
+         */
+        void onSensorChanged(CarSensorEvent event);
+    }
 
-    /** Handles call back into clients. */
-    private final SingleMessageHandler<CarSensorEvent> mHandlerCallback;
+    private static class CarPropertyEventListenerToBase implements
+            CarPropertyManager.CarPropertyEventListener{
+        private final WeakReference<CarSensorManager> mManager;
+        private final OnSensorChangedListener mListener;
+        CarPropertyEventListenerToBase(CarSensorManager manager, OnSensorChangedListener listener) {
+            mManager = new WeakReference<>(manager);
+            mListener = listener;
+        }
 
+        @Override
+        public void onChangeEvent(CarPropertyValue value) {
+            CarSensorManager manager = mManager.get();
+            if (manager != null) {
+                manager.handleOnChangeEvent(value, mListener);
+            }
+        }
 
+        @Override
+        public void onErrorEvent(int propertyId, int zone) {
+
+        }
+    }
+
+    private void handleOnChangeEvent(CarPropertyValue value, OnSensorChangedListener listener) {
+        synchronized (mListenerMap) {
+            CarSensorEvent event = createCarSensorEvent(value);
+            listener.onSensorChanged(event);
+        }
+    }
+
+    private void handleOnErrorEvent(int propertyId, int zone) {
+
+    }
     /** @hide */
     public CarSensorManager(IBinder service, Context context, Handler handler) {
-        mService = ICarSensor.Stub.asInterface(service);
-        mHandlerCallback = new SingleMessageHandler<CarSensorEvent>(handler.getLooper(),
-                MSG_SENSOR_EVENTS) {
-            @Override
-            protected void handleEvent(CarSensorEvent event) {
-                CarSensorListeners listeners;
-                synchronized (mActiveSensorListeners) {
-                    listeners = mActiveSensorListeners.get(event.sensorType);
-                }
-                if (listeners != null) {
-                    listeners.onSensorChanged(event);
-                }
-            }
-        };
+        mCarPropertyMgr = new CarPropertyManager(service, handler, DBG, TAG);
     }
 
     /** @hide */
     @Override
     public void onCarDisconnected() {
-        synchronized(mActiveSensorListeners) {
-            mActiveSensorListeners.clear();
-            mCarSensorEventListenerToService = null;
+        synchronized (mListenerMap) {
+            mListenerMap.clear();
         }
+        mCarPropertyMgr.onCarDisconnected();
     }
 
     /**
@@ -284,16 +312,28 @@
      */
     public int[] getSupportedSensors() throws CarNotConnectedException {
         try {
-            return mService.getSupportedSensors();
+            List<CarPropertyConfig> carPropertyConfigList = getPropertyList();
+            int[] supportedSensors = new int[carPropertyConfigList.size()];
+            for (int i = 0; i < supportedSensors.length; i++) {
+                supportedSensors[i] = carPropertyConfigList.get(i).getPropertyId();
+            }
+            return supportedSensors;
         } catch (IllegalStateException e) {
             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
-        } catch (RemoteException e) {
-            throw new CarNotConnectedException(e);
         }
         return new int[0];
     }
 
     /**
+     * Get list of properties represented by CarSensorManager for this car.
+     * @return List of CarPropertyConfig objects available via Car Cabin Manager.
+     * @throws CarNotConnectedException if the connection to the car service has been lost.
+     */
+    public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
+        return mCarPropertyMgr.getPropertyList(mSensorConfigIds);
+    }
+
+    /**
      * Tells if given sensor is supported or not.
      * @param sensorType
      * @return true if the sensor is supported.
@@ -325,18 +365,6 @@
     }
 
     /**
-     * Listener for car sensor data change.
-     * Callbacks are called in the Looper context.
-     */
-    public interface OnSensorChangedListener {
-        /**
-         * Called when there is a new sensor data from car.
-         * @param event Incoming sensor event for the given sensor type.
-         */
-        void onSensorChanged(final CarSensorEvent event);
-    }
-
-    /**
      * Register {@link OnSensorChangedListener} to get repeated sensor updates. Multiple listeners
      * can be registered for a single sensor or the same listener can be used for different sensors.
      * If the same listener is registered again for the same sensor, it will be either ignored or
@@ -368,33 +396,21 @@
             conditional=true)
     public boolean registerListener(OnSensorChangedListener listener, @SensorType int sensorType,
             @SensorRate int rate) throws CarNotConnectedException, IllegalArgumentException {
-        assertSensorType(sensorType);
         if (rate != SENSOR_RATE_FASTEST && rate != SENSOR_RATE_NORMAL
                 && rate != SENSOR_RATE_UI && rate != SENSOR_RATE_FAST) {
             throw new IllegalArgumentException("wrong rate " + rate);
         }
-        synchronized(mActiveSensorListeners) {
-            if (mCarSensorEventListenerToService == null) {
-                mCarSensorEventListenerToService = new CarSensorEventListenerToService(this);
-            }
-            boolean needsServerUpdate = false;
-            CarSensorListeners listeners;
-            listeners = mActiveSensorListeners.get(sensorType);
-            if (listeners == null) {
-                listeners = new CarSensorListeners(rate);
-                mActiveSensorListeners.put(sensorType, listeners);
-                needsServerUpdate = true;
-            }
-            if (listeners.addAndUpdateRate(listener, rate)) {
-                needsServerUpdate = true;
-            }
-            if (needsServerUpdate) {
-                if (!registerOrUpdateSensorListener(sensorType, rate)) {
-                    return false;
-                }
-            }
+        if (mListenerMap.get(listener) == null) {
+            mCarPropertyEventListener = new CarPropertyEventListenerToBase(this, listener);
+        } else {
+            mCarPropertyEventListener = mListenerMap.get(listener);
         }
-        return true;
+        if (mCarPropertyMgr.registerListener(mCarPropertyEventListener, sensorType, rate)) {
+            mListenerMap.put(listener, mCarPropertyEventListener);
+            return true;
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -404,10 +420,10 @@
      */
     public void unregisterListener(OnSensorChangedListener listener) {
         //TODO: removing listener should reset update rate, bug: 32060307
-        synchronized(mActiveSensorListeners) {
-            for (int i = 0; i < mActiveSensorListeners.size(); i++) {
-                doUnregisterListenerLocked(listener, mActiveSensorListeners.keyAt(i));
-            }
+        synchronized (mListenerMap) {
+            mCarPropertyEventListener = mListenerMap.get(listener);
+            mCarPropertyMgr.unregisterListener(mCarPropertyEventListener);
+            mListenerMap.remove(listener);
         }
     }
 
@@ -418,49 +434,10 @@
      * @param sensorType
      */
     public void unregisterListener(OnSensorChangedListener listener, @SensorType int sensorType) {
-        synchronized(mActiveSensorListeners) {
-            doUnregisterListenerLocked(listener, sensorType);
+        synchronized (mListenerMap) {
+            mCarPropertyEventListener = mListenerMap.get(listener);
         }
-    }
-
-    private void doUnregisterListenerLocked(OnSensorChangedListener listener, Integer sensor) {
-        CarSensorListeners listeners = mActiveSensorListeners.get(sensor);
-        if (listeners != null) {
-            boolean needsServerUpdate = false;
-            if (listeners.contains(listener)) {
-                needsServerUpdate = listeners.remove(listener);
-            }
-            if (listeners.isEmpty()) {
-                try {
-                    mService.unregisterSensorListener(sensor.intValue(),
-                            mCarSensorEventListenerToService);
-                } catch (RemoteException e) {
-                    //ignore
-                }
-                mActiveSensorListeners.remove(sensor);
-            } else if (needsServerUpdate) {
-                try {
-                    registerOrUpdateSensorListener(sensor, listeners.getRate());
-                } catch (CarNotConnectedException e) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    private boolean registerOrUpdateSensorListener(int sensor, int rate)
-            throws CarNotConnectedException {
-        try {
-            if (!mService.registerOrUpdateSensorListener(sensor, rate,
-                    mCarSensorEventListenerToService)) {
-                return false;
-            }
-        } catch (IllegalStateException e) {
-            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
-        } catch (RemoteException e) {
-            throw new CarNotConnectedException(e);
-        }
-        return true;
+        mCarPropertyMgr.unregisterListener(mCarPropertyEventListener, sensorType);
     }
 
     /**
@@ -473,13 +450,11 @@
      */
     public CarSensorEvent getLatestSensorEvent(@SensorType int type)
             throws CarNotConnectedException {
-        assertSensorType(type);
         try {
-            return mService.getLatestSensorEvent(type);
+            CarPropertyValue propertyValue = mCarPropertyMgr.getProperty(type, 0);
+            return createCarSensorEvent(propertyValue);
         } catch (IllegalStateException e) {
             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
-        } catch(RemoteException e) {
-            handleCarServiceRemoteExceptionAndThrow(e);
         }
         return null;
     }
@@ -492,58 +467,38 @@
         throw new CarNotConnectedException();
     }
 
-    private void assertSensorType(int sensorType) {
-        if (sensorType == 0 || !((sensorType <= SENSOR_TYPE_MAX) ||
-                ((sensorType >= SENSOR_TYPE_VENDOR_EXTENSION_START) &&
-                        (sensorType <= SENSOR_TYPE_VENDOR_EXTENSION_END)))) {
-            throw new IllegalArgumentException("invalid sensor type " + sensorType);
-        }
-    }
-
-    private void handleOnSensorChanged(List<CarSensorEvent> events) {
-        mHandlerCallback.sendEvents(events);
-    }
-
-    private static class CarSensorEventListenerToService extends ICarSensorEventListener.Stub {
-        private final WeakReference<CarSensorManager> mManager;
-
-        public CarSensorEventListenerToService(CarSensorManager manager) {
-            mManager = new WeakReference<>(manager);
-        }
-
-        @Override
-        public void onSensorChanged(List<CarSensorEvent> events) {
-            CarSensorManager manager = mManager.get();
-            if (manager != null) {
-                manager.handleOnSensorChanged(events);
-            }
-        }
-    }
-
-    private class CarSensorListeners extends CarRatedListeners<OnSensorChangedListener> {
-        CarSensorListeners(int rate) {
-            super(rate);
-        }
-
-        void onSensorChanged(final CarSensorEvent event) {
-            // throw away old sensor data as oneway binder call can change order.
-            long updateTime = event.timestamp;
-            if (updateTime < mLastUpdateTime) {
-                Log.w(CarLibLog.TAG_SENSOR, "dropping old sensor data");
-                return;
-            }
-            mLastUpdateTime = updateTime;
-            List<OnSensorChangedListener> listeners;
-            synchronized (mActiveSensorListeners) {
-                listeners = new ArrayList<>(getListeners());
-            }
-            listeners.forEach(new Consumer<OnSensorChangedListener>() {
-                @Override
-                public void accept(OnSensorChangedListener listener) {
-                    listener.onSensorChanged(event);
+    private CarSensorEvent createCarSensorEvent(CarPropertyValue propertyValue) {
+        CarSensorEvent event = null;
+        switch (propertyValue.getPropertyId() & VehiclePropertyType.MASK) {
+            case VehiclePropertyType.FLOAT:
+                event = new CarSensorEvent(propertyValue.getPropertyId(),
+                        propertyValue.getTimestamp(), 1, 0, 0);
+                event.floatValues[0] = (float) propertyValue.getValue();
+                break;
+            case VehiclePropertyType.INT32:
+                event = new CarSensorEvent(propertyValue.getPropertyId(),
+                        propertyValue.getTimestamp(), 0, 1, 0);
+                event.intValues[0] = (int) propertyValue.getValue();
+                break;
+            case VehiclePropertyType.BOOLEAN:
+                event = new CarSensorEvent(propertyValue.getPropertyId(),
+                        propertyValue.getTimestamp(), 0, 1, 0);
+                event.intValues[0] = (boolean) propertyValue.getValue() ? 1 : 0;
+                break;
+            case VehiclePropertyType.INT64_VEC:
+                Object[] value = (Object[]) propertyValue.getValue();
+                event = new CarSensorEvent(propertyValue.getPropertyId(),
+                        propertyValue.getTimestamp(), 0, 0, value.length);
+                for (int i = 0; i < value.length; i++) {
+                    event.longValues[i] = (Long) value[i];
                 }
-            });
+                break;
+            default:
+                Log.e(TAG, "unhandled VehiclePropertyType for propId="
+                        + propertyValue.getPropertyId());
+                break;
         }
+        return event;
     }
 
     /**
@@ -558,15 +513,44 @@
      * @hide
      */
     public CarSensorConfig getSensorConfig(@SensorType int type)
-        throws CarNotConnectedException {
-        assertSensorType(type);
-        try {
-            return mService.getSensorConfig(type);
-        } catch (IllegalStateException e) {
-            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
-        } catch(RemoteException e) {
-            handleCarServiceRemoteExceptionAndThrow(e);
+            throws CarNotConnectedException {
+        Bundle b = null;
+        switch (type) {
+            case SENSOR_TYPE_WHEEL_TICK_DISTANCE:
+                List<CarPropertyConfig> propertyConfigs = mCarPropertyMgr.getPropertyList();
+                for (CarPropertyConfig p : propertyConfigs) {
+                    if (p.getPropertyId() == type) {
+                        b = createWheelDistanceTickBundle(p.getConfigArray());
+                        break;
+                    }
+                }
+                break;
+            default:
+                b = Bundle.EMPTY;
+                break;
         }
-        return new CarSensorConfig(0, Bundle.EMPTY);
+        return new CarSensorConfig(type, b);
+    }
+
+    private static final int INDEX_WHEEL_DISTANCE_ENABLE_FLAG = 0;
+    private static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
+    private static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
+    private static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
+    private static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
+    private static final int WHEEL_TICK_DISTANCE_BUNDLE_SIZE = 6;
+
+    private Bundle createWheelDistanceTickBundle(List<Integer> configArray) {
+        Bundle b = new Bundle(WHEEL_TICK_DISTANCE_BUNDLE_SIZE);
+        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS,
+                configArray.get(INDEX_WHEEL_DISTANCE_ENABLE_FLAG));
+        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK,
+                configArray.get(INDEX_WHEEL_DISTANCE_FRONT_LEFT));
+        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK,
+                configArray.get(INDEX_WHEEL_DISTANCE_FRONT_RIGHT));
+        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK,
+                configArray.get(INDEX_WHEEL_DISTANCE_REAR_RIGHT));
+        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK,
+                configArray.get(INDEX_WHEEL_DISTANCE_REAR_LEFT));
+        return b;
     }
 }
diff --git a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
index bdaa8cd..9f06764 100644
--- a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
+++ b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
@@ -20,14 +20,16 @@
 import android.car.Car;
 import android.car.CarManagerBase;
 import android.car.CarNotConnectedException;
-import android.car.hardware.property.CarPropertyManagerBase;
-import android.car.hardware.property.CarPropertyManagerBase.CarPropertyEventCallback;
+import android.car.hardware.property.CarPropertyManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.ArraySet;
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -42,12 +44,36 @@
 
     private final static boolean DBG = false;
     private final static String TAG = CarVendorExtensionManager.class.getSimpleName();
-    private final CarPropertyManagerBase mPropertyManager;
+    private final CarPropertyManager mPropertyManager;
 
     @GuardedBy("mLock")
-    private ArraySet<CarVendorExtensionCallback> mCallbacks;
+    private final ArraySet<CarVendorExtensionCallback> mCallbacks = new ArraySet<>();
     private final Object mLock = new Object();
 
+    @GuardedBy("mLock")
+    private CarPropertyEventListenerToBase mListenerToBase = null;
+
+    private void handleOnChangeEvent(CarPropertyValue value) {
+        Collection<CarVendorExtensionCallback> callbacks;
+        synchronized (mLock) {
+            callbacks = new ArrayList<>(mCallbacks);
+        }
+        for (CarVendorExtensionCallback l: callbacks) {
+            l.onChangeEvent(value);
+        }
+    }
+
+    private void handleOnErrorEvent(int propertyId, int zone) {
+        Collection<CarVendorExtensionCallback> listeners;
+        synchronized (mLock) {
+            listeners = new ArrayList<>(mCallbacks);
+        }
+        for (CarVendorExtensionCallback l: listeners) {
+            l.onErrorEvent(propertyId, zone);
+        }
+
+    }
+
     /**
      * Creates an instance of the {@link CarVendorExtensionManager}.
      *
@@ -55,7 +81,7 @@
      * @hide
      */
     public CarVendorExtensionManager(IBinder service, Handler handler) {
-        mPropertyManager = new CarPropertyManagerBase(service, handler, DBG, TAG);
+        mPropertyManager = new CarPropertyManager(service, handler, DBG, TAG);
     }
 
     /**
@@ -77,43 +103,32 @@
     public void registerCallback(CarVendorExtensionCallback callback)
             throws CarNotConnectedException {
         synchronized (mLock) {
-            if (mCallbacks == null) {
-                mPropertyManager.registerCallback(new CarPropertyEventCallback() {
-                    @Override
-                    public void onChangeEvent(CarPropertyValue value) {
-                        for (CarVendorExtensionCallback listener: getCallbacks()) {
-                            listener.onChangeEvent(value);
-                        }
-                    }
+            if (mCallbacks.isEmpty()) {
+                mListenerToBase = new CarPropertyEventListenerToBase(this);
+            }
 
-                    @Override
-                    public void onErrorEvent(int propertyId, int zone) {
-                        for (CarVendorExtensionCallback listener: getCallbacks()) {
-                            listener.onErrorEvent(propertyId, zone);
-                        }
-                    }
-                });
-                mCallbacks = new ArraySet<>(1 /* We expect at least one element */);
+            List<CarPropertyConfig> configs = mPropertyManager.getPropertyList();
+            for (CarPropertyConfig c : configs) {
+                // Register each individual propertyId
+                mPropertyManager.registerListener(mListenerToBase, c.getPropertyId(), 0);
             }
             mCallbacks.add(callback);
         }
     }
 
     /** Unregisters listener that was previously registered. */
-    public void unregisterCallback(CarVendorExtensionCallback callback) {
+    public void unregisterCallback(CarVendorExtensionCallback callback)
+            throws CarNotConnectedException {
         synchronized (mLock) {
             mCallbacks.remove(callback);
-            if (mCallbacks.isEmpty()) {
-                mPropertyManager.unregisterCallback();
-                mCallbacks = null;
+            List<CarPropertyConfig> configs = mPropertyManager.getPropertyList();
+            for (CarPropertyConfig c : configs) {
+                // Register each individual propertyId
+                mPropertyManager.unregisterListener(mListenerToBase, c.getPropertyId());
             }
-        }
-    }
-
-    /** Returns copy of listeners. Thread safe. */
-    private CarVendorExtensionCallback[] getCallbacks() {
-        synchronized (mLock) {
-            return mCallbacks.toArray(new CarVendorExtensionCallback[mCallbacks.size()]);
+            if (mCallbacks.isEmpty()) {
+                mListenerToBase = null;
+            }
         }
     }
 
@@ -204,4 +219,28 @@
     public void onCarDisconnected() {
         mPropertyManager.onCarDisconnected();
     }
+    private static class CarPropertyEventListenerToBase implements
+            CarPropertyManager.CarPropertyEventListener {
+        private final WeakReference<CarVendorExtensionManager> mManager;
+
+        CarPropertyEventListenerToBase(CarVendorExtensionManager manager) {
+            mManager = new WeakReference<>(manager);
+        }
+
+        @Override
+        public void onChangeEvent(CarPropertyValue value) {
+            CarVendorExtensionManager manager = mManager.get();
+            if (manager != null) {
+                manager.handleOnChangeEvent(value);
+            }
+        }
+
+        @Override
+        public void onErrorEvent(int propertyId, int zone) {
+            CarVendorExtensionManager manager = mManager.get();
+            if (manager != null) {
+                manager.handleOnErrorEvent(propertyId, zone);
+            }
+        }
+    }
 }
diff --git a/car-lib/src/android/car/hardware/cabin/CarCabinManager.java b/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
index c27b414..1dee2b0 100644
--- a/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
+++ b/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
@@ -23,8 +23,7 @@
 import android.car.CarNotConnectedException;
 import android.car.hardware.CarPropertyConfig;
 import android.car.hardware.CarPropertyValue;
-import android.car.hardware.property.CarPropertyManagerBase;
-import android.car.hardware.property.CarPropertyManagerBase.CarPropertyEventCallback;
+import android.car.hardware.property.CarPropertyManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
@@ -33,6 +32,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
@@ -57,7 +57,7 @@
 public final class CarCabinManager implements CarManagerBase {
     private final static boolean DBG = false;
     private final static String TAG = "CarCabinManager";
-    private final CarPropertyManagerBase mMgr;
+    private final CarPropertyManager mCarPropertyMgr;
     private final ArraySet<CarCabinEventCallback> mCallbacks = new ArraySet<>();
     private CarPropertyEventListenerToBase mListenerToBase = null;
 
@@ -69,45 +69,45 @@
      * Some vehicles (minivans) can open the door electronically.  Hence, the ability
      * to write this property.
      */
-    public static final int ID_DOOR_POS = 0x0001;
+    public static final int ID_DOOR_POS = 0x16400b00;
     /** door move, int type
      * Positive values open the door, negative values close it.
      */
-    public static final int ID_DOOR_MOVE = 0x0002;
+    public static final int ID_DOOR_MOVE = 0x16400b01;
     /** door lock, bool type
      * 'true' indicates door is locked.
      */
-    public static final int ID_DOOR_LOCK = 0x0003;
+    public static final int ID_DOOR_LOCK = 0x16200b02;
 
     /** Mirror properties are zoned by VehicleAreaMirror */
     /**
      * mirror z position, int type
      * Positive value indicates tilt upwards, negative value tilt downwards.
      */
-    public static final int ID_MIRROR_Z_POS = 0x1001;
+    public static final int ID_MIRROR_Z_POS = 0x14400b40;
     /** mirror z move, int type
      * Positive value tilts the mirror upwards, negative value tilts downwards.
      */
-    public static final int ID_MIRROR_Z_MOVE = 0x1002;
+    public static final int ID_MIRROR_Z_MOVE = 0x14400b41;
     /**
      * mirror y position, int type
      * Positive value indicates tilt right, negative value tilt left
      */
-    public static final int ID_MIRROR_Y_POS = 0x1003;
+    public static final int ID_MIRROR_Y_POS = 0x14400b42;
     /** mirror y move, int type
      * Positive value tilts the mirror right, negative value tilts left.
      */
-    public static final int ID_MIRROR_Y_MOVE = 0x1004;
+    public static final int ID_MIRROR_Y_MOVE = 0x14400b43;
     /**
      * mirror lock, bool type
      * True indicates mirror positions are locked and not changeable.
      */
-    public static final int ID_MIRROR_LOCK = 0x1005;
+    public static final int ID_MIRROR_LOCK = 0x11200b44;
     /**
      * mirror fold, bool type
      * True indicates mirrors are folded.
      */
-    public static final int ID_MIRROR_FOLD = 0x1006;
+    public static final int ID_MIRROR_FOLD = 0x11200b45;
 
     /** Seat properties are zoned by VehicleAreaSeat */
     /**
@@ -120,173 +120,173 @@
      * When the user wants to select a preset, the desired preset number (1, 2, or 3)
      * is set.
      */
-    public static final int ID_SEAT_MEMORY_SELECT = 0x2001;
+    public static final int ID_SEAT_MEMORY_SELECT = 0x15400b80;
     /**
      * seat memory set, int type
      * This setting allows the user to save the current seat position settings into
      * the selected preset slot.  The maxValue for each seat position shall match
      * the maxValue for VEHICLE_PROPERTY_SEAT_MEMORY_SELECT.
      */
-    public static final int ID_SEAT_MEMORY_SET = 0x2002;
+    public static final int ID_SEAT_MEMORY_SET = 0x15400b81;
     /**
      * seat belt buckled, bool type
      * True indicates belt is buckled.
      */
-    public static final int ID_SEAT_BELT_BUCKLED = 0x2003;
+    public static final int ID_SEAT_BELT_BUCKLED = 0x15200b82;
     /**
      * seat belt height position, int type
      * Adjusts the shoulder belt anchor point.
      * Max value indicates highest position.
      * Min value indicates lowest position.
      */
-    public static final int ID_SEAT_BELT_HEIGHT_POS = 0x2004;
+    public static final int ID_SEAT_BELT_HEIGHT_POS = 0x15400b83;
     /** seat belt height move, int type
      * Adjusts the shoulder belt anchor point.
      * Positive value moves towards highest point.
      * Negative value moves towards lowest point.
      */
-    public static final int ID_SEAT_BELT_HEIGHT_MOVE = 0x2005;
+    public static final int ID_SEAT_BELT_HEIGHT_MOVE = 0x15400b84;
     /**
      * seat fore/aft position, int type
      * Sets the seat position forward (closer to steering wheel) and backwards.
      * Max value indicates closest to wheel, min value indicates most rearward position.
      */
-    public static final int ID_SEAT_FORE_AFT_POS = 0x2006;
+    public static final int ID_SEAT_FORE_AFT_POS = 0x15400b85;
     /**
      * seat fore/aft move, int type
      * Positive value moves seat forward (closer to steering wheel).
      * Negative value moves seat rearward.
      */
-    public static final int ID_SEAT_FORE_AFT_MOVE = 0x2007;
+    public static final int ID_SEAT_FORE_AFT_MOVE = 0x15400b86;
     /**
      * seat backrest angle #1 position, int type
      * Backrest angle 1 is the actuator closest to the bottom of the seat.
      * Max value indicates angling forward towards the steering wheel.
      * Min value indicates full recline.
      */
-    public static final int ID_SEAT_BACKREST_ANGLE_1_POS = 0x2008;
+    public static final int ID_SEAT_BACKREST_ANGLE_1_POS = 0x15400b87;
     /** seat backrest angle #1 move, int type
      * Backrest angle 1 is the actuator closest to the bottom of the seat.
      * Positive value angles seat towards the steering wheel.
      * Negatie value angles away from steering wheel.
      */
-    public static final int ID_SEAT_BACKREST_ANGLE_1_MOVE = 0x2009;
+    public static final int ID_SEAT_BACKREST_ANGLE_1_MOVE = 0x15400b88;
     /**
      * seat backrest angle #2 position, int type
      * Backrest angle 2 is the next actuator up from the bottom of the seat.
      * Max value indicates angling forward towards the steering wheel.
      * Min value indicates full recline.
      */
-    public static final int ID_SEAT_BACKREST_ANGLE_2_POS = 0x200A;
+    public static final int ID_SEAT_BACKREST_ANGLE_2_POS = 0x15400b89;
     /** seat backrest angle #2 move, int type
      * Backrest angle 2 is the next actuator up from the bottom of the seat.
      * Positive value tilts forward towards the steering wheel.
      * Negative value tilts backwards.
      */
-    public static final int ID_SEAT_BACKREST_ANGLE_2_MOVE = 0x200B;
+    public static final int ID_SEAT_BACKREST_ANGLE_2_MOVE = 0x15400b8a;
     /**
      * seat height position, int type
      * Sets the seat height.
      * Max value indicates highest position.
      * Min value indicates lowest position.
      */
-    public static final int ID_SEAT_HEIGHT_POS = 0x200C;
+    public static final int ID_SEAT_HEIGHT_POS = 0x15400b8b;
     /** seat height move, int type
      * Sets the seat height.
      * Positive value raises the seat.
      * Negative value lowers the seat.
      * */
-    public static final int ID_SEAT_HEIGHT_MOVE = 0x200D;
+    public static final int ID_SEAT_HEIGHT_MOVE = 0x15400b8c;
     /**
      * seat depth position, int type
      * Sets the seat depth, distance from back rest to front edge of seat.
      * Max value indicates longest depth position.
      * Min value indicates shortest position.
      */
-    public static final int ID_SEAT_DEPTH_POS = 0x200E;
+    public static final int ID_SEAT_DEPTH_POS = 0x15400b8d;
     /** seat depth move, int type
      * Adjusts the seat depth, distance from back rest to front edge of seat.
      * Positive value increases the distance from back rest to front edge of seat.
      * Negative value decreases this distance.
      */
-    public static final int ID_SEAT_DEPTH_MOVE = 0x200F;
+    public static final int ID_SEAT_DEPTH_MOVE = 0x15400b8e;
     /**
      * seat tilt position, int type
      * Sets the seat tilt.
      * Max value indicates front edge of seat higher than back edge.
      * Min value indicates front edge of seat lower than back edge.
      */
-    public static final int ID_SEAT_TILT_POS = 0x2010;
+    public static final int ID_SEAT_TILT_POS = 0x15400b8f;
     /** seat tilt move, int type
      * Adjusts the seat tilt.
      * Positive value lifts front edge of seat higher than back edge.
      * Negative value lowers front edge of seat in relation to back edge.
      */
-    public static final int ID_SEAT_TILT_MOVE = 0x2011;
+    public static final int ID_SEAT_TILT_MOVE = 0x15400b90;
     /**
      * seat lumbar fore/aft position, int type
      * Pushes the lumbar support forward and backwards.
      * Max value indicates most forward position.
      * Min value indicates most rearward position.
      */
-    public static final int ID_SEAT_LUMBAR_FORE_AFT_POS = 0x2012;
+    public static final int ID_SEAT_LUMBAR_FORE_AFT_POS = 0x15400b91;
     /** seat lumbar fore/aft move, int type
      * Adjusts the lumbar support forwards and backwards.
      * Positive value moves lumbar support forward.
      * Negative value moves lumbar support rearward.
      */
-    public static final int ID_SEAT_LUMBAR_FORE_AFT_MOVE = 0x2013;
+    public static final int ID_SEAT_LUMBAR_FORE_AFT_MOVE = 0x15400b92;
     /**
      * seat lumbar side support position, int type
      * Sets the amount of lateral lumbar support.
      * Max value indicates widest lumbar setting (i.e. least support)
      * Min value indicates thinnest lumbar setting.
      */
-    public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_POS = 0x2014;
+    public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_POS = 0x15400b93;
     /** seat lumbar side support move, int type
      * Adjusts the amount of lateral lumbar support.
      * Positive value widens the lumbar area.
      * Negative value makes the lumbar area thinner.
      */
-    public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 0x2015;
+    public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 0x15400b94;
     /**
      * seat headrest height position, int type
      * Sets the headrest height.
      * Max value indicates tallest setting.
      * Min value indicates shortest setting.
      */
-    public static final int ID_SEAT_HEADREST_HEIGHT_POS = 0x2016;
+    public static final int ID_SEAT_HEADREST_HEIGHT_POS = 0x15400b95;
     /** seat headrest height move, int type
      * Postive value moves the headrest higher.
      * Negative value moves the headrest lower.
      */
-    public static final int ID_SEAT_HEADREST_HEIGHT_MOVE = 0x2017;
+    public static final int ID_SEAT_HEADREST_HEIGHT_MOVE = 0x15400b96;
     /**
      * seat headrest angle position, int type
      * Sets the angle of the headrest.
      * Max value indicates most upright angle.
      * Min value indicates shallowest headrest angle.
      */
-    public static final int ID_SEAT_HEADREST_ANGLE_POS = 0x2018;
+    public static final int ID_SEAT_HEADREST_ANGLE_POS = 0x15400b97;
     /** seat headrest angle move, int type
      * Adjusts the angle of the headrest.
      * Positive value angles headrest towards most upright angle.
      * Negative value angles headrest towards shallowest headrest angle.
      */
-    public static final int ID_SEAT_HEADREST_ANGLE_MOVE = 0x2019;
+    public static final int ID_SEAT_HEADREST_ANGLE_MOVE = 0x15400b98;
     /**
      * seat headrest fore/aft position, int type
      * Sets the headrest forwards and backwards.
      * Max value indicates position closest to front of car.
      * Min value indicates position closest to rear of car.
      */
-    public static final int ID_SEAT_HEADREST_FORE_AFT_POS = 0x201A;
+    public static final int ID_SEAT_HEADREST_FORE_AFT_POS = 0x15400b99;
     /** seat headrest fore/aft move, int type
      * Adjsuts the headrest forwards and backwards.
      * Positive value moves the headrest closer to front of car.
      * Negative value moves the headrest closer to rear of car.
      */
-    public static final int ID_SEAT_HEADREST_FORE_AFT_MOVE = 0x201B;
+    public static final int ID_SEAT_HEADREST_FORE_AFT_MOVE = 0x15400b9a;
 
     /** Window properties are zoned by VehicleAreaWindow */
     /**
@@ -294,62 +294,103 @@
      * Max = window down / open.
      * Min = window up / closed.
      */
-    public static final int ID_WINDOW_POS = 0x3001;
+    public static final int ID_WINDOW_POS = 0x13400bc0;
     /** window move, int type
      * Positive value moves window down / opens window.
      * Negative value moves window up / closes window.
      */
-    public static final int ID_WINDOW_MOVE = 0x3002;
+    public static final int ID_WINDOW_MOVE = 0x13400bc1;
     /**
      * window lock, bool type
      * True indicates windows are locked and can't be moved.
      */
-    public static final int ID_WINDOW_LOCK = 0x3003;
+    public static final int ID_WINDOW_LOCK = 0x13400bc4;
 
     /** @hide */
     @IntDef({
-        ID_DOOR_POS,
-        ID_DOOR_MOVE,
-        ID_DOOR_LOCK,
-        ID_MIRROR_Z_POS,
-        ID_MIRROR_Z_MOVE,
-        ID_MIRROR_Y_POS,
-        ID_MIRROR_Y_MOVE,
-        ID_MIRROR_LOCK,
-        ID_MIRROR_FOLD,
-        ID_SEAT_MEMORY_SELECT,
-        ID_SEAT_MEMORY_SET,
-        ID_SEAT_BELT_BUCKLED,
-        ID_SEAT_BELT_HEIGHT_POS,
-        ID_SEAT_BELT_HEIGHT_MOVE,
-        ID_SEAT_FORE_AFT_POS,
-        ID_SEAT_FORE_AFT_MOVE,
-        ID_SEAT_BACKREST_ANGLE_1_POS,
-        ID_SEAT_BACKREST_ANGLE_1_MOVE,
-        ID_SEAT_BACKREST_ANGLE_2_POS,
-        ID_SEAT_BACKREST_ANGLE_2_MOVE,
-        ID_SEAT_HEIGHT_POS,
-        ID_SEAT_HEIGHT_MOVE,
-        ID_SEAT_DEPTH_POS,
-        ID_SEAT_DEPTH_MOVE,
-        ID_SEAT_TILT_POS,
-        ID_SEAT_TILT_MOVE,
-        ID_SEAT_LUMBAR_FORE_AFT_POS,
-        ID_SEAT_LUMBAR_FORE_AFT_MOVE,
-        ID_SEAT_LUMBAR_SIDE_SUPPORT_POS,
-        ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE,
-        ID_SEAT_HEADREST_HEIGHT_POS,
-        ID_SEAT_HEADREST_HEIGHT_MOVE,
-        ID_SEAT_HEADREST_ANGLE_POS,
-        ID_SEAT_HEADREST_ANGLE_MOVE,
-        ID_SEAT_HEADREST_FORE_AFT_POS,
-        ID_SEAT_HEADREST_FORE_AFT_MOVE,
-        ID_WINDOW_POS,
-        ID_WINDOW_MOVE,
-        ID_WINDOW_LOCK
+            ID_DOOR_POS,
+            ID_DOOR_MOVE,
+            ID_DOOR_LOCK,
+            ID_MIRROR_Z_POS,
+            ID_MIRROR_Z_MOVE,
+            ID_MIRROR_Y_POS,
+            ID_MIRROR_Y_MOVE,
+            ID_MIRROR_LOCK,
+            ID_MIRROR_FOLD,
+            ID_SEAT_MEMORY_SELECT,
+            ID_SEAT_MEMORY_SET,
+            ID_SEAT_BELT_BUCKLED,
+            ID_SEAT_BELT_HEIGHT_POS,
+            ID_SEAT_BELT_HEIGHT_MOVE,
+            ID_SEAT_FORE_AFT_POS,
+            ID_SEAT_FORE_AFT_MOVE,
+            ID_SEAT_BACKREST_ANGLE_1_POS,
+            ID_SEAT_BACKREST_ANGLE_1_MOVE,
+            ID_SEAT_BACKREST_ANGLE_2_POS,
+            ID_SEAT_BACKREST_ANGLE_2_MOVE,
+            ID_SEAT_HEIGHT_POS,
+            ID_SEAT_HEIGHT_MOVE,
+            ID_SEAT_DEPTH_POS,
+            ID_SEAT_DEPTH_MOVE,
+            ID_SEAT_TILT_POS,
+            ID_SEAT_TILT_MOVE,
+            ID_SEAT_LUMBAR_FORE_AFT_POS,
+            ID_SEAT_LUMBAR_FORE_AFT_MOVE,
+            ID_SEAT_LUMBAR_SIDE_SUPPORT_POS,
+            ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE,
+            ID_SEAT_HEADREST_HEIGHT_POS,
+            ID_SEAT_HEADREST_HEIGHT_MOVE,
+            ID_SEAT_HEADREST_ANGLE_POS,
+            ID_SEAT_HEADREST_ANGLE_MOVE,
+            ID_SEAT_HEADREST_FORE_AFT_POS,
+            ID_SEAT_HEADREST_FORE_AFT_MOVE,
+            ID_WINDOW_POS,
+            ID_WINDOW_MOVE,
+            ID_WINDOW_LOCK
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PropertyId {}
+    private final ArraySet<Integer> mCabinPropertyIds = new ArraySet<>(Arrays.asList(new Integer[]{
+            ID_DOOR_POS,
+            ID_DOOR_MOVE,
+            ID_DOOR_LOCK,
+            ID_MIRROR_Z_POS,
+            ID_MIRROR_Z_MOVE,
+            ID_MIRROR_Y_POS,
+            ID_MIRROR_Y_MOVE,
+            ID_MIRROR_LOCK,
+            ID_MIRROR_FOLD,
+            ID_SEAT_MEMORY_SELECT,
+            ID_SEAT_MEMORY_SET,
+            ID_SEAT_BELT_BUCKLED,
+            ID_SEAT_BELT_HEIGHT_POS,
+            ID_SEAT_BELT_HEIGHT_MOVE,
+            ID_SEAT_FORE_AFT_POS,
+            ID_SEAT_FORE_AFT_MOVE,
+            ID_SEAT_BACKREST_ANGLE_1_POS,
+            ID_SEAT_BACKREST_ANGLE_1_MOVE,
+            ID_SEAT_BACKREST_ANGLE_2_POS,
+            ID_SEAT_BACKREST_ANGLE_2_MOVE,
+            ID_SEAT_HEIGHT_POS,
+            ID_SEAT_HEIGHT_MOVE,
+            ID_SEAT_DEPTH_POS,
+            ID_SEAT_DEPTH_MOVE,
+            ID_SEAT_TILT_POS,
+            ID_SEAT_TILT_MOVE,
+            ID_SEAT_LUMBAR_FORE_AFT_POS,
+            ID_SEAT_LUMBAR_FORE_AFT_MOVE,
+            ID_SEAT_LUMBAR_SIDE_SUPPORT_POS,
+            ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE,
+            ID_SEAT_HEADREST_HEIGHT_POS,
+            ID_SEAT_HEADREST_HEIGHT_MOVE,
+            ID_SEAT_HEADREST_ANGLE_POS,
+            ID_SEAT_HEADREST_ANGLE_MOVE,
+            ID_SEAT_HEADREST_FORE_AFT_POS,
+            ID_SEAT_HEADREST_FORE_AFT_MOVE,
+            ID_WINDOW_POS,
+            ID_WINDOW_MOVE,
+            ID_WINDOW_LOCK
+    }));
 
     /**
      * Application registers CarCabinEventCallback object to receive updates and changes to
@@ -370,7 +411,8 @@
         void onErrorEvent(@PropertyId int propertyId, int zone);
     }
 
-    private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
+    private static class CarPropertyEventListenerToBase implements
+            CarPropertyManager.CarPropertyEventListener{
         private final WeakReference<CarCabinManager> mManager;
 
         public CarPropertyEventListenerToBase(CarCabinManager manager) {
@@ -426,7 +468,7 @@
      * @hide
      */
     public CarCabinManager(IBinder service, Context context, Handler handler) {
-        mMgr = new CarPropertyManagerBase(service, handler, DBG, TAG);
+        mCarPropertyMgr = new CarPropertyManager(service, handler, DBG, TAG);
     }
 
     /**
@@ -447,9 +489,15 @@
             CarNotConnectedException {
         if (mCallbacks.isEmpty()) {
             mListenerToBase = new CarPropertyEventListenerToBase(this);
-            mMgr.registerCallback(mListenerToBase);
+        }
+        List<CarPropertyConfig> configs = getPropertyList();
+        for (CarPropertyConfig c : configs) {
+            // Register each individual propertyId
+            mCarPropertyMgr.registerListener(mListenerToBase, c.getPropertyId(), 0);
         }
         mCallbacks.add(callback);
+
+
     }
 
     /**
@@ -457,10 +505,15 @@
      * this listener, all listening will be stopped.
      * @param callback
      */
-    public synchronized void unregisterCallback(CarCabinEventCallback callback) {
+    public synchronized void unregisterCallback(CarCabinEventCallback callback)
+            throws CarNotConnectedException {
         mCallbacks.remove(callback);
+        List<CarPropertyConfig> configs = getPropertyList();
+        for (CarPropertyConfig c : configs) {
+            // Register each individual propertyId
+            mCarPropertyMgr.unregisterListener(mListenerToBase, c.getPropertyId());
+        }
         if (mCallbacks.isEmpty()) {
-            mMgr.unregisterCallback();
             mListenerToBase = null;
         }
     }
@@ -471,7 +524,7 @@
      * @throws CarNotConnectedException if the connection to the car service has been lost.
      */
     public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
-        return mMgr.getPropertyList();
+        return mCarPropertyMgr.getPropertyList(mCabinPropertyIds);
     }
 
     /**
@@ -483,7 +536,7 @@
      */
     public boolean getBooleanProperty(@PropertyId int propertyId, int area)
             throws CarNotConnectedException {
-        return mMgr.getBooleanProperty(propertyId, area);
+        return mCarPropertyMgr.getBooleanProperty(propertyId, area);
     }
 
     /**
@@ -495,7 +548,7 @@
      */
     public float getFloatProperty(@PropertyId int propertyId, int area)
             throws CarNotConnectedException {
-        return mMgr.getFloatProperty(propertyId, area);
+        return mCarPropertyMgr.getFloatProperty(propertyId, area);
     }
 
     /**
@@ -507,7 +560,7 @@
      */
     public int getIntProperty(@PropertyId int propertyId, int area)
             throws CarNotConnectedException {
-        return mMgr.getIntProperty(propertyId, area);
+        return mCarPropertyMgr.getIntProperty(propertyId, area);
     }
 
     /**
@@ -519,7 +572,9 @@
      */
     public void setBooleanProperty(@PropertyId int propertyId, int area, boolean val)
             throws CarNotConnectedException {
-        mMgr.setBooleanProperty(propertyId, area, val);
+        if (mCabinPropertyIds.contains(propertyId)) {
+            mCarPropertyMgr.setBooleanProperty(propertyId, area, val);
+        }
     }
 
     /**
@@ -531,7 +586,9 @@
      */
     public void setFloatProperty(@PropertyId int propertyId, int area, float val)
             throws CarNotConnectedException {
-        mMgr.setFloatProperty(propertyId, area, val);
+        if (mCabinPropertyIds.contains(propertyId)) {
+            mCarPropertyMgr.setFloatProperty(propertyId, area, val);
+        }
     }
 
     /**
@@ -543,12 +600,14 @@
      */
     public void setIntProperty(@PropertyId int propertyId, int area, int val)
             throws CarNotConnectedException {
-        mMgr.setIntProperty(propertyId, area, val);
+        if (mCabinPropertyIds.contains(propertyId)) {
+            mCarPropertyMgr.setIntProperty(propertyId, area, val);
+        }
     }
 
     /** @hide */
     @Override
     public void onCarDisconnected() {
-        mMgr.onCarDisconnected();
+        mCarPropertyMgr.onCarDisconnected();
     }
 }
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
index af4e190..c953d6f 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
+++ b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
@@ -23,16 +23,17 @@
 import android.car.CarNotConnectedException;
 import android.car.hardware.CarPropertyConfig;
 import android.car.hardware.CarPropertyValue;
-import android.car.hardware.property.CarPropertyManagerBase;
-import android.car.hardware.property.CarPropertyManagerBase.CarPropertyEventCallback;
+import android.car.hardware.property.CarPropertyManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.ArraySet;
+import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
@@ -44,10 +45,11 @@
 public final class CarHvacManager implements CarManagerBase {
     private final static boolean DBG = false;
     private final static String TAG = "CarHvacManager";
-    private final CarPropertyManagerBase mMgr;
+    private final CarPropertyManager mCarPropertyMgr;
     private final ArraySet<CarHvacEventCallback> mCallbacks = new ArraySet<>();
     private CarPropertyEventListenerToBase mListenerToBase = null;
 
+
     /**
      * HVAC property IDs for get/set methods
      */
@@ -59,31 +61,24 @@
      * Mirror defrosters state, bool type
      * true indicates mirror defroster is on
      */
-    public static final int ID_MIRROR_DEFROSTER_ON = 0x0001;
+    public static final int ID_MIRROR_DEFROSTER_ON = 0x1440050c;
     /**
      * Steering wheel temp, int type
      * Positive values indicate heating.
      * Negative values indicate cooling
      */
-    public static final int ID_STEERING_WHEEL_HEAT = 0x0002;
+    public static final int ID_STEERING_WHEEL_HEAT = 0x1140050d;
     /**
      * Outside air temperature, float type
      * Value is in degrees Celsius
      */
-    public static final int ID_OUTSIDE_AIR_TEMP = 0x0003;
+    public static final int ID_OUTSIDE_AIR_TEMP = 0x11600703;
     /**
      * Temperature units being used, int type
      *  0x30 = Celsius
      *  0x31 = Fahrenheit
      */
-    public static final int ID_TEMPERATURE_DISPLAY_UNITS = 0x0004;
-
-
-    /**
-     * The maximum id that can be assigned to global (non-zoned) property.
-     * @hide
-     */
-    public static final int ID_MAX_GLOBAL_PROPERTY_ID = 0x3fff;
+    public static final int ID_TEMPERATURE_DISPLAY_UNITS = 0x1140050e;
 
     /**
      * ID_ZONED_* represents properties available on a per-zone basis.  All zones in a car are
@@ -93,85 +88,85 @@
      * Temperature setpoint, float type
      * Temperature set by the user, units are in degrees Celsius.
      */
-    public static final int ID_ZONED_TEMP_SETPOINT = 0x4001;
+    public static final int ID_ZONED_TEMP_SETPOINT = 0x15600503;
     /**
      * Actual temperature, float type
      * Actual zone temperature is read only value, in terms of F or C.
      */
-    public static final int ID_ZONED_TEMP_ACTUAL = 0x4002;
+    public static final int ID_ZONED_TEMP_ACTUAL = 0x15600502;
     /**
      * HVAC system powered on / off, bool type
      * In many vehicles, if the HVAC system is powered off, the SET and GET command will
      * throw an IllegalStateException.  To correct this, need to turn on the HVAC module first
      * before manipulating a parameter.
      */
-    public static final int ID_ZONED_HVAC_POWER_ON = 0x4003;
+    public static final int ID_ZONED_HVAC_POWER_ON = 0x15200510;
     /**
      * Fan speed setpoint, int type
      * Fan speed is an integer from 0-n, depending on number of fan speeds available.
      */
-    public static final int ID_ZONED_FAN_SPEED_SETPOINT = 0x4004;
+    public static final int ID_ZONED_FAN_SPEED_SETPOINT = 0x15400500;
     /**
      * Actual fan speed, int type
      * Actual fan speed is a read-only value, expressed in RPM.
      */
-    public static final int ID_ZONED_FAN_SPEED_RPM = 0x4005;
+    public static final int ID_ZONED_FAN_SPEED_RPM = 0x1540050f;
     /**
      *  Fan direction available, int vector type
      *  Fan direction is a bitmask of directions available for each zone.
      */
-    public static final int ID_ZONED_FAN_DIRECTION_AVAILABLE = 0x4006;
+    public static final int ID_ZONED_FAN_DIRECTION_AVAILABLE = 0x15410511;
     /**
      * Current fan direction setting, int type. The value must be one of the FAN_DIRECTION_AVAILABLE
      * values declared above.
      */
-    public static final int ID_ZONED_FAN_DIRECTION = 0x4007;
+    public static final int ID_ZONED_FAN_DIRECTION = 0x15400501;
     /**
      * Seat temperature, int type
      * Seat temperature is negative for cooling, positive for heating.  Temperature is a
      * setting, i.e. -3 to 3 for 3 levels of cooling and 3 levels of heating.
      */
-    public static final int ID_ZONED_SEAT_TEMP = 0x4008;
+    public static final int ID_ZONED_SEAT_TEMP = 0x1540050b;
     /**
      * Air ON, bool type
      * true indicates AC is ON.
      */
-    public static final int ID_ZONED_AC_ON = 0x4009;
+    public static final int ID_ZONED_AC_ON = 0x15200505;
     /**
      * Automatic Mode ON, bool type
      * true indicates HVAC is in automatic mode
      */
-    public static final int ID_ZONED_AUTOMATIC_MODE_ON = 0x400A;
+    public static final int ID_ZONED_AUTOMATIC_MODE_ON = 0x1520050A;
     /**
      * Air recirculation ON, bool type
      * true indicates recirculation is active.
      */
-    public static final int ID_ZONED_AIR_RECIRCULATION_ON = 0x400B;
+    public static final int ID_ZONED_AIR_RECIRCULATION_ON = 0x15200508;
     /**
      * Max AC ON, bool type
      * true indicates MAX AC is ON
      */
-    public static final int ID_ZONED_MAX_AC_ON = 0x400C;
+    public static final int ID_ZONED_MAX_AC_ON = 0x15200506;
     /** Dual zone ON, bool type
      * true indicates dual zone mode is ON
      */
-    public static final int ID_ZONED_DUAL_ZONE_ON = 0x400D;
+    public static final int ID_ZONED_DUAL_ZONE_ON = 0x15200509;
     /**
      * Max Defrost ON, bool type
      * true indicates max defrost is active.
      */
-    public static final int ID_ZONED_MAX_DEFROST_ON = 0x400E;
+    public static final int ID_ZONED_MAX_DEFROST_ON = 0x15200507;
     /**
      * Automatic recirculation mode ON
      * true indicates recirculation is in automatic mode
      */
-    public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 0x400F;
+    public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 0x15200512;
     /**
      * Defroster ON, bool type
      * Defroster controls are based on window position.
      * True indicates the defroster is ON.
      */
-    public static final int ID_WINDOW_DEFROSTER_ON = 0x5001;
+    public static final int ID_WINDOW_DEFROSTER_ON = 0x13200504;
 
     /** @hide */
     @IntDef({
@@ -194,10 +189,33 @@
             ID_ZONED_MAX_DEFROST_ON,
             ID_ZONED_HVAC_POWER_ON,
             ID_ZONED_HVAC_AUTO_RECIRC_ON,
-            ID_WINDOW_DEFROSTER_ON,
+            ID_WINDOW_DEFROSTER_ON
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PropertyId {}
+    private final ArraySet<Integer> mHvacPropertyIds = new ArraySet<>(Arrays.asList(new Integer [] {
+            ID_MIRROR_DEFROSTER_ON,
+            ID_STEERING_WHEEL_HEAT,
+            ID_OUTSIDE_AIR_TEMP,
+            ID_TEMPERATURE_DISPLAY_UNITS,
+            ID_ZONED_TEMP_SETPOINT,
+            ID_ZONED_TEMP_ACTUAL,
+            ID_ZONED_FAN_SPEED_SETPOINT,
+            ID_ZONED_FAN_SPEED_RPM,
+            ID_ZONED_FAN_DIRECTION_AVAILABLE,
+            ID_ZONED_FAN_DIRECTION,
+            ID_ZONED_SEAT_TEMP,
+            ID_ZONED_AC_ON,
+            ID_ZONED_AUTOMATIC_MODE_ON,
+            ID_ZONED_AIR_RECIRCULATION_ON,
+            ID_ZONED_MAX_AC_ON,
+            ID_ZONED_DUAL_ZONE_ON,
+            ID_ZONED_MAX_DEFROST_ON,
+            ID_ZONED_HVAC_POWER_ON,
+            ID_ZONED_HVAC_AUTO_RECIRC_ON,
+            ID_WINDOW_DEFROSTER_ON
+    }));
+
 
     /**
      * Represents fan direction when air flows through face directed vents.
@@ -234,7 +252,8 @@
         void onErrorEvent(@PropertyId int propertyId, int zone);
     }
 
-    private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
+    private static class CarPropertyEventListenerToBase implements
+            CarPropertyManager.CarPropertyEventListener {
         private final WeakReference<CarHvacManager> mManager;
 
         public CarPropertyEventListenerToBase(CarHvacManager manager) {
@@ -292,28 +311,22 @@
      * @hide
      */
     public CarHvacManager(IBinder service, Context context, Handler handler) {
-        mMgr = new CarPropertyManagerBase(service, handler, DBG, TAG);
+        mCarPropertyMgr = new CarPropertyManager(service, handler, DBG, TAG);
     }
-
     /**
-     * Determine if a property is zoned or not.
-     * @param propertyId
-     * @return true if property is a zoned type.
-     */
-    public static boolean isZonedProperty(@PropertyId int propertyId) {
-        return propertyId > ID_MAX_GLOBAL_PROPERTY_ID;
-    }
-
-    /**
-     * Implement wrappers for contained CarPropertyManagerBase object
+     * Implement wrappers for contained CarPropertyManager object
      * @param callback
      * @throws CarNotConnectedException
      */
-    public synchronized void registerCallback(CarHvacEventCallback callback) throws
-            CarNotConnectedException {
+    public synchronized void registerCallback(CarHvacEventCallback callback)
+            throws CarNotConnectedException {
         if (mCallbacks.isEmpty()) {
             mListenerToBase = new CarPropertyEventListenerToBase(this);
-            mMgr.registerCallback(mListenerToBase);
+        }
+        List<CarPropertyConfig> configs = getPropertyList();
+        for (CarPropertyConfig c : configs) {
+            // Register each individual propertyId
+            mCarPropertyMgr.registerListener(mListenerToBase, c.getPropertyId(), 0);
         }
         mCallbacks.add(callback);
     }
@@ -325,8 +338,17 @@
      */
     public synchronized void unregisterCallback(CarHvacEventCallback callback) {
         mCallbacks.remove(callback);
+        try {
+            List<CarPropertyConfig> configs = getPropertyList();
+            for (CarPropertyConfig c : configs) {
+                // Register each individual propertyId
+                mCarPropertyMgr.unregisterListener(mListenerToBase, c.getPropertyId());
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "getPropertyList exception ", e);
+        }
         if (mCallbacks.isEmpty()) {
-            mMgr.unregisterCallback();
+            mCarPropertyMgr.unregisterListener(mListenerToBase);
             mListenerToBase = null;
         }
     }
@@ -337,7 +359,7 @@
      * @throws CarNotConnectedException if the connection to the car service has been lost.
      */
     public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
-        return mMgr.getPropertyList();
+        return mCarPropertyMgr.getPropertyList(mHvacPropertyIds);
     }
 
     /**
@@ -346,7 +368,7 @@
      */
     public boolean isPropertyAvailable(@PropertyId int propertyId, int area)
             throws CarNotConnectedException {
-        return mMgr.isPropertyAvailable(propertyId, area);
+        return mCarPropertyMgr.isPropertyAvailable(propertyId, area);
     }
 
     /**
@@ -358,7 +380,7 @@
      */
     public boolean getBooleanProperty(@PropertyId int propertyId, int area)
             throws CarNotConnectedException {
-        return mMgr.getBooleanProperty(propertyId, area);
+        return mCarPropertyMgr.getBooleanProperty(propertyId, area);
     }
 
     /**
@@ -370,7 +392,7 @@
      */
     public float getFloatProperty(@PropertyId int propertyId, int area)
             throws CarNotConnectedException {
-        return mMgr.getFloatProperty(propertyId, area);
+        return mCarPropertyMgr.getFloatProperty(propertyId, area);
     }
 
     /**
@@ -382,7 +404,7 @@
      */
     public int getIntProperty(@PropertyId int propertyId, int area)
             throws CarNotConnectedException {
-        return mMgr.getIntProperty(propertyId, area);
+        return mCarPropertyMgr.getIntProperty(propertyId, area);
     }
 
     /**
@@ -394,7 +416,9 @@
      */
     public void setBooleanProperty(@PropertyId int propertyId, int area, boolean val)
             throws CarNotConnectedException {
-        mMgr.setBooleanProperty(propertyId, area, val);
+        if (mHvacPropertyIds.contains(propertyId)) {
+            mCarPropertyMgr.setBooleanProperty(propertyId, area, val);
+        }
     }
 
     /**
@@ -406,7 +430,9 @@
      */
     public void setFloatProperty(@PropertyId int propertyId, int area, float val)
             throws CarNotConnectedException {
-        mMgr.setFloatProperty(propertyId, area, val);
+        if (mHvacPropertyIds.contains(propertyId)) {
+            mCarPropertyMgr.setFloatProperty(propertyId, area, val);
+        }
     }
 
     /**
@@ -418,12 +444,13 @@
      */
     public void setIntProperty(@PropertyId int propertyId, int area, int val)
             throws CarNotConnectedException {
-        mMgr.setIntProperty(propertyId, area, val);
+        if (mHvacPropertyIds.contains(propertyId)) {
+            mCarPropertyMgr.setIntProperty(propertyId, area, val);
+        }
     }
 
     /** @hide */
-    @Override
     public void onCarDisconnected() {
-        mMgr.onCarDisconnected();
+        mCarPropertyMgr.onCarDisconnected();
     }
 }
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
new file mode 100644
index 0000000..5c094e7
--- /dev/null
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2018 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 android.car.hardware.property;
+
+import static java.lang.Integer.toHexString;
+
+import android.car.CarApiUtil;
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.car.internal.CarRatedFloatListeners;
+import com.android.car.internal.SingleMessageHandler;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+
+/**
+ * API for creating Car*Manager
+ * @hide
+ */
+public class CarPropertyManager implements CarManagerBase {
+    private final boolean mDbg;
+    private final SingleMessageHandler<CarPropertyEvent> mHandler;
+    private final ICarProperty mService;
+    private final String mTag;
+    private static final int MSG_GENERIC_EVENT = 0;
+
+    private CarPropertyEventListenerToService mCarPropertyEventToService;
+
+
+    /** Record of locally active properties. Key is propertyId */
+    private final SparseArray<CarPropertyListeners> mActivePropertyListener =
+            new SparseArray<>();
+
+    /** Callback functions for property events */
+    public interface CarPropertyEventListener {
+        /** Called when a property is updated */
+        void onChangeEvent(CarPropertyValue value);
+
+        /** Called when an error is detected with a property */
+        void onErrorEvent(int propId, int zone);
+    }
+
+    /**
+     * Get an instance of the CarPropertyManager.
+     */
+    public CarPropertyManager(IBinder service, Handler handler, boolean dbg, String tag) {
+        mDbg = dbg;
+        mTag = tag;
+        mService = ICarProperty.Stub.asInterface(service);
+        mHandler = new SingleMessageHandler<CarPropertyEvent>(handler.getLooper(),
+                MSG_GENERIC_EVENT) {
+            @Override
+            protected void handleEvent(CarPropertyEvent event) {
+                CarPropertyListeners listeners;
+                synchronized (mActivePropertyListener) {
+                    listeners = mActivePropertyListener.get(
+                            event.getCarPropertyValue().getPropertyId());
+                }
+                if (listeners != null) {
+                    switch (event.getEventType()) {
+                        case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
+                            listeners.onPropertyChanged(event);
+                            break;
+                        case CarPropertyEvent.PROPERTY_EVENT_ERROR:
+                            listeners.onErrorEvent(event);
+                            break;
+                        default:
+                            throw new IllegalArgumentException();
+                    }
+                }
+            }
+        };
+    }
+
+    /** Use to register or update Callback for properties */
+    public boolean registerListener(CarPropertyEventListener listener, int propertyId, float rate)
+            throws CarNotConnectedException {
+        synchronized (mActivePropertyListener) {
+            if (mCarPropertyEventToService == null) {
+                mCarPropertyEventToService = new CarPropertyEventListenerToService(this);
+            }
+            boolean needsServerUpdate = false;
+            CarPropertyListeners listeners;
+            listeners = mActivePropertyListener.get(propertyId);
+            if (listeners == null) {
+                listeners = new CarPropertyListeners(rate);
+                mActivePropertyListener.put(propertyId, listeners);
+                needsServerUpdate = true;
+            }
+            if (listeners.addAndUpdateRate(listener, rate)) {
+                needsServerUpdate = true;
+            }
+            if (needsServerUpdate) {
+                if (!registerOrUpdatePropertyListener(propertyId, rate)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean registerOrUpdatePropertyListener(int propertyId, float rate)
+            throws CarNotConnectedException {
+        try {
+            mService.registerListener(propertyId, rate, mCarPropertyEventToService);
+        } catch (IllegalStateException e) {
+            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+        } catch (RemoteException e) {
+            throw new CarNotConnectedException(e);
+        }
+        return true;
+    }
+
+    private class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub{
+        private final WeakReference<CarPropertyManager> mMgr;
+
+        CarPropertyEventListenerToService(CarPropertyManager mgr) {
+            mMgr = new WeakReference<>(mgr);
+        }
+
+        @Override
+        public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
+            CarPropertyManager manager = mMgr.get();
+            if (manager != null) {
+                manager.handleEvent(events);
+            }
+        }
+    }
+
+    private void handleEvent(List<CarPropertyEvent> events) {
+        mHandler.sendEvents(events);
+    }
+
+    /**
+     * Stop getting sensor update for the given listener. If there are multiple registrations for
+     * this listener, all listening will be stopped.
+     * @param listener
+     */
+    public void unregisterListener(CarPropertyEventListener listener) {
+        synchronized (mActivePropertyListener) {
+            for (int i = 0; i < mActivePropertyListener.size(); i++) {
+                doUnregisterListenerLocked(listener, mActivePropertyListener.keyAt(i));
+            }
+        }
+    }
+
+    /**
+     * Stop getting sensor update for the given listener and sensor. If the same listener is used
+     * for other sensors, those subscriptions will not be affected.
+     * @param listener
+     * @param propertyId
+     */
+    public void unregisterListener(CarPropertyEventListener listener, int propertyId) {
+        synchronized (mActivePropertyListener) {
+            doUnregisterListenerLocked(listener, propertyId);
+        }
+    }
+
+    private void doUnregisterListenerLocked(CarPropertyEventListener listener, int propertyId) {
+        CarPropertyListeners listeners = mActivePropertyListener.get(propertyId);
+        if (listeners != null) {
+            boolean needsServerUpdate = false;
+            if (listeners.contains(listener)) {
+                needsServerUpdate = listeners.remove(listener);
+            }
+            if (listeners.isEmpty()) {
+                try {
+                    mService.unregisterListener(propertyId, mCarPropertyEventToService);
+                } catch (RemoteException e) {
+                    //ignore
+                }
+                mActivePropertyListener.remove(propertyId);
+            } else if (needsServerUpdate) {
+                try {
+                    registerOrUpdatePropertyListener(propertyId, listeners.getRate());
+                } catch (CarNotConnectedException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the list of properties implemented by this car.
+     *
+     * @return Caller must check the property type and typecast to the appropriate subclass
+     * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
+     */
+    public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
+        try {
+            return mService.getPropertyList();
+        } catch (RemoteException e) {
+            Log.e(mTag, "getPropertyList exception ", e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Returns the list of properties implemented by this car in given property id list.
+     *
+     * @return Caller must check the property type and typecast to the appropriate subclass
+     * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
+     */
+    public List<CarPropertyConfig> getPropertyList(ArraySet<Integer> propertyIds)
+            throws CarNotConnectedException {
+        try {
+            List<CarPropertyConfig> configs = new ArrayList<>();
+            for (CarPropertyConfig c : mService.getPropertyList()) {
+                if (propertyIds.contains(c.getPropertyId())) {
+                    configs.add(c);
+                }
+            }
+            return configs;
+        } catch (RemoteException e) {
+            Log.e(mTag, "getPropertyList exception ", e);
+            throw new CarNotConnectedException(e);
+        }
+
+    }
+
+    /**
+     * Check whether a given property is available or disabled based on the car's current state.
+     * @return true if STATUS_AVAILABLE, false otherwise (eg STATUS_UNAVAILABLE)
+     * @throws CarNotConnectedException
+     */
+    public boolean isPropertyAvailable(int propId, int area) throws CarNotConnectedException {
+        try {
+            CarPropertyValue propValue = mService.getProperty(propId, area);
+            return (propValue != null)
+                    && (propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);
+        } catch (RemoteException e) {
+            Log.e(mTag, "isPropertyAvailable failed with " + e.toString()
+                    + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Returns value of a bool property
+     *
+     * @param prop Property ID to get
+     * @param area Area of the property to get
+     */
+    public boolean getBooleanProperty(int prop, int area) throws CarNotConnectedException {
+        CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, prop, area);
+        return carProp != null ? carProp.getValue() : false;
+    }
+
+    /**
+     * Returns value of a float property
+     *
+     * @param prop Property ID to get
+     * @param area Area of the property to get
+     */
+    public float getFloatProperty(int prop, int area) throws CarNotConnectedException {
+        CarPropertyValue<Float> carProp = getProperty(Float.class, prop, area);
+        return carProp != null ? carProp.getValue() : 0f;
+    }
+
+    /**
+     * Returns value of a integer property
+     *
+     * @param prop Property ID to get
+     * @param area Zone of the property to get
+     */
+    public int getIntProperty(int prop, int area) throws CarNotConnectedException {
+        CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area);
+        return carProp != null ? carProp.getValue() : 0;
+    }
+
+    /** Return CarPropertyValue */
+    @SuppressWarnings("unchecked")
+    public <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area)
+            throws CarNotConnectedException {
+        if (mDbg) {
+            Log.d(mTag, "getProperty, propId: 0x" + toHexString(propId)
+                    + ", area: 0x" + toHexString(area) + ", class: " + clazz);
+        }
+        try {
+            CarPropertyValue<E> propVal = mService.getProperty(propId, area);
+            if (propVal != null && propVal.getValue() != null) {
+                Class<?> actualClass = propVal.getValue().getClass();
+                if (actualClass != clazz) {
+                    throw new IllegalArgumentException("Invalid property type. " + "Expected: "
+                            + clazz + ", but was: " + actualClass);
+                }
+            }
+            return propVal;
+        } catch (RemoteException e) {
+            Log.e(mTag, "getProperty failed with " + e.toString()
+                    + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /** Return raw CarPropertyValue */
+    public <E> CarPropertyValue<E> getProperty(int propId, int area)
+            throws CarNotConnectedException {
+        try {
+            CarPropertyValue<E> propVal = mService.getProperty(propId, area);
+            return propVal;
+        } catch (RemoteException e) {
+            Log.e(mTag, "getProperty failed with " + e.toString()
+                    + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /** Set CarPropertyValue */
+    public <E> void setProperty(Class<E> clazz, int propId, int area, E val)
+            throws CarNotConnectedException {
+        if (mDbg) {
+            Log.d(mTag, "setProperty, propId: 0x" + toHexString(propId)
+                    + ", area: 0x" + toHexString(area) + ", class: " + clazz + ", val: " + val);
+        }
+        try {
+            mService.setProperty(new CarPropertyValue<>(propId, area, val));
+        } catch (RemoteException e) {
+            Log.e(mTag, "setProperty failed with " + e.toString(), e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Modifies a property.  If the property modification doesn't occur, an error event shall be
+     * generated and propagated back to the application.
+     *
+     * @param prop Property ID to modify
+     * @param area Area to apply the modification.
+     * @param val Value to set
+     */
+    public void setBooleanProperty(int prop, int area, boolean val)
+            throws CarNotConnectedException {
+        setProperty(Boolean.class, prop, area, val);
+    }
+
+    /** Set float value of property*/
+    public void setFloatProperty(int prop, int area, float val) throws CarNotConnectedException {
+        setProperty(Float.class, prop, area, val);
+    }
+    /** Set int value of property*/
+    public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {
+        setProperty(Integer.class, prop, area, val);
+    }
+
+
+    private class CarPropertyListeners extends CarRatedFloatListeners<CarPropertyEventListener> {
+        CarPropertyListeners(float rate) {
+            super(rate);
+        }
+        void onPropertyChanged(final CarPropertyEvent event) {
+            // throw away old sensor data as oneway binder call can change order.
+            long updateTime = event.getCarPropertyValue().getTimestamp();
+            if (updateTime < mLastUpdateTime) {
+                Log.w(mTag, "dropping old property data");
+                return;
+            }
+            mLastUpdateTime = updateTime;
+            List<CarPropertyEventListener> listeners;
+            synchronized (mActivePropertyListener) {
+                listeners = new ArrayList<>(getListeners());
+            }
+            listeners.forEach(new Consumer<CarPropertyEventListener>() {
+                @Override
+                public void accept(CarPropertyEventListener listener) {
+                    listener.onChangeEvent(event.getCarPropertyValue());
+                }
+            });
+        }
+
+        void onErrorEvent(final CarPropertyEvent event) {
+            List<CarPropertyEventListener> listeners;
+            CarPropertyValue value = event.getCarPropertyValue();
+            synchronized (mActivePropertyListener) {
+                listeners = new ArrayList<>(getListeners());
+            }
+            listeners.forEach(new Consumer<CarPropertyEventListener>() {
+                @Override
+                public void accept(CarPropertyEventListener listener) {
+                    listener.onErrorEvent(value.getPropertyId(), value.getAreaId());
+                }
+            });
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void onCarDisconnected() {
+        synchronized (mActivePropertyListener) {
+            mActivePropertyListener.clear();
+            mCarPropertyEventToService = null;
+        }
+    }
+}
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManagerBase.java b/car-lib/src/android/car/hardware/property/CarPropertyManagerBase.java
deleted file mode 100644
index 002284d..0000000
--- a/car-lib/src/android/car/hardware/property/CarPropertyManagerBase.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.car.hardware.property;
-
-import static java.lang.Integer.toHexString;
-
-import android.annotation.Nullable;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.hardware.CarPropertyConfig;
-import android.car.hardware.CarPropertyValue;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-/**
- * API for creating Car*Manager
- * @hide
- */
-public class CarPropertyManagerBase {
-    private final boolean mDbg;
-    private final Handler mHandler;
-    private final ICarProperty mService;
-    private final String mTag;
-
-    @GuardedBy("mLock")
-    private ICarPropertyEventListener mListenerToService;
-    @GuardedBy("mLock")
-    private CarPropertyEventCallback mCallback;
-
-    private final Object mLock = new Object();
-
-    /** Callback functions for property events */
-    public interface CarPropertyEventCallback {
-        /** Called when a property is updated */
-        void onChangeEvent(CarPropertyValue value);
-
-        /** Called when an error is detected with a property */
-        void onErrorEvent(int propertyId, int zone);
-    }
-
-    private final static class EventCallbackHandler extends Handler {
-        /** Constants handled in the handler */
-        private static final int MSG_GENERIC_EVENT = 0;
-
-        private final WeakReference<CarPropertyManagerBase> mMgr;
-
-        EventCallbackHandler(CarPropertyManagerBase mgr, Looper looper) {
-            super(looper);
-            mMgr = new WeakReference<>(mgr);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_GENERIC_EVENT:
-                    CarPropertyManagerBase mgr = mMgr.get();
-                    if (mgr != null) {
-                        mgr.dispatchEventToClient((CarPropertyEvent) msg.obj);
-                    }
-                    break;
-                default:
-                    Log.e("EventtCallbackHandler", "Event type not handled:  " + msg);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Get an instance of the CarPropertyManagerBase.
-     */
-    public CarPropertyManagerBase(IBinder service, Handler handler, boolean dbg,
-            String tag) {
-        mDbg = dbg;
-        mTag = tag;
-        mService = ICarProperty.Stub.asInterface(service);
-        mHandler = new EventCallbackHandler(this, handler.getLooper());
-    }
-
-    public void registerCallback(CarPropertyEventCallback callback)
-            throws CarNotConnectedException {
-        synchronized (mLock) {
-            if (mCallback != null) {
-                throw new IllegalStateException("Callback is already registered.");
-            }
-
-            mCallback = callback;
-            mListenerToService = new ICarPropertyEventListener.Stub() {
-                @Override
-                public void onEvent(CarPropertyEvent event) throws RemoteException {
-                    handleEvent(event);
-                }
-            };
-        }
-
-        try {
-            mService.registerListener(mListenerToService);
-        } catch (RemoteException ex) {
-            Log.e(mTag, "Could not connect: ", ex);
-            throw new CarNotConnectedException(ex);
-        } catch (IllegalStateException ex) {
-            Car.checkCarNotConnectedExceptionFromCarService(ex);
-        }
-    }
-
-    public void unregisterCallback() {
-        ICarPropertyEventListener listenerToService;
-        synchronized (mLock) {
-            listenerToService = mListenerToService;
-            mCallback = null;
-            mListenerToService = null;
-        }
-
-        if (listenerToService == null) {
-            Log.w(mTag, "unregisterListener: listener was not registered");
-            return;
-        }
-
-        try {
-            mService.unregisterListener(listenerToService);
-        } catch (RemoteException ex) {
-            Log.e(mTag, "Failed to unregister listener", ex);
-            //ignore
-        } catch (IllegalStateException ex) {
-            Car.hideCarNotConnectedExceptionFromCarService(ex);
-        }
-    }
-
-    /**
-     * Returns the list of properties implemented by this car.
-     *
-     * @return Caller must check the property type and typecast to the appropriate subclass
-     * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
-     */
-    public List<CarPropertyConfig> getPropertyList()  throws CarNotConnectedException {
-        try {
-            return mService.getPropertyList();
-        } catch (RemoteException e) {
-            Log.e(mTag, "Exception in getPropertyList", e);
-            throw new CarNotConnectedException(e);
-        }
-    }
-
-    /**
-     * Check whether a given property is available or disabled based on the car's current state.
-     * @return true if STATUS_AVAILABLE, false otherwise (eg STATUS_UNAVAILABLE)
-     * @throws CarNotConnectedException
-     */
-    public boolean isPropertyAvailable(int propId, int area) throws CarNotConnectedException {
-        try {
-            CarPropertyValue propValue = mService.getProperty(propId, area);
-            return (propValue != null) &&
-                   (propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);
-        } catch (RemoteException e) {
-            Log.e(mTag, "isPropertyAvailable failed with " + e.toString()
-                + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
-            throw new CarNotConnectedException(e);
-        }
-    }
-
-    /**
-     * Returns value of a bool property
-     *
-     * @param prop Property ID to get
-     * @param area Area of the property to get
-     */
-    public boolean getBooleanProperty(int prop, int area) throws CarNotConnectedException {
-        CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, prop, area);
-        return carProp != null ? carProp.getValue() : false;
-    }
-
-    /**
-     * Returns value of a float property
-     *
-     * @param prop Property ID to get
-     * @param area Area of the property to get
-     */
-    public float getFloatProperty(int prop, int area) throws CarNotConnectedException {
-        CarPropertyValue<Float> carProp = getProperty(Float.class, prop, area);
-        return carProp != null ? carProp.getValue() : 0f;
-    }
-
-    /**
-     * Returns value of a integer property
-     *
-     * @param prop Property ID to get
-     * @param area Zone of the property to get
-     */
-    public int getIntProperty(int prop, int area) throws CarNotConnectedException {
-        CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area);
-        return carProp != null ? carProp.getValue() : 0;
-    }
-
-    @Nullable
-    @SuppressWarnings("unchecked")
-    public <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area)
-            throws CarNotConnectedException {
-        if (mDbg) {
-            Log.d(mTag, "getProperty, propId: 0x" + toHexString(propId)
-                    + ", area: 0x" + toHexString(area) + ", class: " + clazz);
-        }
-        try {
-            CarPropertyValue<E> propVal = mService.getProperty(propId, area);
-            if (propVal != null && propVal.getValue() != null) {
-                Class<?> actualClass = propVal.getValue().getClass();
-                if (actualClass != clazz) {
-                    throw new IllegalArgumentException("Invalid property type. " + "Expected: "
-                            + clazz + ", but was: " + actualClass);
-                }
-            }
-            return propVal;
-        } catch (RemoteException e) {
-            Log.e(mTag, "getProperty failed with " + e.toString()
-                    + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
-            throw new CarNotConnectedException(e);
-        }
-    }
-
-    public <E> void setProperty(Class<E> clazz, int propId, int area, E val)
-            throws CarNotConnectedException {
-        if (mDbg) {
-            Log.d(mTag, "setProperty, propId: 0x" + toHexString(propId)
-                    + ", area: 0x" + toHexString(area) + ", class: " + clazz + ", val: " + val);
-        }
-        try {
-            mService.setProperty(new CarPropertyValue<>(propId, area, val));
-        } catch (RemoteException e) {
-            Log.e(mTag, "setProperty failed with " + e.toString(), e);
-            throw new CarNotConnectedException(e);
-        }
-    }
-
-    /**
-     * Modifies a property.  If the property modification doesn't occur, an error event shall be
-     * generated and propagated back to the application.
-     *
-     * @param prop Property ID to modify
-     * @param area Area to apply the modification.
-     * @param val Value to set
-     */
-    public void setBooleanProperty(int prop, int area, boolean val)
-            throws CarNotConnectedException {
-        setProperty(Boolean.class, prop, area, val);
-    }
-
-    public void setFloatProperty(int prop, int area, float val) throws CarNotConnectedException {
-        setProperty(Float.class, prop, area, val);
-    }
-
-    public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {
-        setProperty(Integer.class, prop, area, val);
-    }
-
-    private void dispatchEventToClient(CarPropertyEvent event) {
-        CarPropertyEventCallback listener;
-        synchronized (mLock) {
-            listener = mCallback;
-        }
-
-        if (listener == null) {
-            Log.e(mTag, "Listener died, not dispatching event.");
-            return;
-        }
-
-        CarPropertyValue propVal = event.getCarPropertyValue();
-        switch(event.getEventType()) {
-            case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
-                listener.onChangeEvent(propVal);
-                break;
-            case CarPropertyEvent.PROPERTY_EVENT_ERROR:
-                listener.onErrorEvent(propVal.getPropertyId(), propVal.getAreaId());
-                break;
-            default:
-                throw new IllegalArgumentException();
-        }
-    }
-
-    private void handleEvent(CarPropertyEvent event) {
-        mHandler.sendMessage(mHandler.obtainMessage(EventCallbackHandler.MSG_GENERIC_EVENT, event));
-    }
-
-    /** @hide */
-    public void onCarDisconnected() {
-
-        ICarPropertyEventListener listenerToService;
-        synchronized (mLock) {
-            listenerToService = mListenerToService;
-        }
-
-        if (listenerToService != null) {
-            unregisterCallback();
-        }
-    }
-}
diff --git a/car-lib/src/android/car/hardware/property/ICarProperty.aidl b/car-lib/src/android/car/hardware/property/ICarProperty.aidl
index 2121ebb..3e7e5c9 100644
--- a/car-lib/src/android/car/hardware/property/ICarProperty.aidl
+++ b/car-lib/src/android/car/hardware/property/ICarProperty.aidl
@@ -25,9 +25,9 @@
  */
 interface ICarProperty {
 
-    void registerListener(in ICarPropertyEventListener callback) = 0;
+    void registerListener(int propId, float rate, in ICarPropertyEventListener callback) = 0;
 
-    void unregisterListener(in ICarPropertyEventListener callback) = 1;
+    void unregisterListener(int propId, in ICarPropertyEventListener callback) = 1;
 
     List<CarPropertyConfig> getPropertyList() = 2;
 
diff --git a/car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl b/car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl
index b0d57fc..ebda47c 100644
--- a/car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl
+++ b/car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl
@@ -28,6 +28,6 @@
      * Called when an event is triggered in response to one of the calls (such as on tune) or
      * asynchronously (such as on announcement).
      */
-    void onEvent(in CarPropertyEvent event) = 0;
+    void onEvent(in List<CarPropertyEvent> events) = 0;
 }
 
diff --git a/car-lib/src/android/car/media/ICarVolumeCallback.aidl b/car-lib/src/android/car/media/ICarVolumeCallback.aidl
index 10a5ddc..8540680 100644
--- a/car-lib/src/android/car/media/ICarVolumeCallback.aidl
+++ b/car-lib/src/android/car/media/ICarVolumeCallback.aidl
@@ -27,12 +27,12 @@
      * The changed-to volume index is not included, the caller is encouraged to
      * get the current group volume index via CarAudioManager.
      */
-    void onGroupVolumeChanged(int groupId);
+    void onGroupVolumeChanged(int groupId, int flags);
 
     /**
      * This is called whenever the master mute state is changed.
      * The changed-to master mute state is not included, the caller is encouraged to
      * get the current master mute state via AudioManager.
      */
-    void onMasterMuteChanged();
+    void onMasterMuteChanged(int flags);
 }
diff --git a/car-lib/src/android/car/user/CarUserManagerHelper.java b/car-lib/src/android/car/user/CarUserManagerHelper.java
index 45d2c7f..4a69f0b 100644
--- a/car-lib/src/android/car/user/CarUserManagerHelper.java
+++ b/car-lib/src/android/car/user/CarUserManagerHelper.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.car.settings.CarSettings;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -177,15 +178,6 @@
     }
 
     /**
-     * Temporary method: Gets all the users that includes system user.
-     *
-     * @return List of {@code UserInfo} for users that associated with a real person.
-     */
-    public List<UserInfo> getAllUsersIncludingSystemUser() {
-        return mUserManager.getUsers(/* excludeDying= */true);
-    }
-
-    /**
      * Get all the users except the one with userId passed in.
      *
      * @param userId of the user not to be returned.
@@ -236,6 +228,16 @@
     }
 
     /**
+     * Checks whether the user is default user.
+     *
+     * @param userInfo User to check against system user.
+     * @return {@code true} if is default user, {@code false} otherwise.
+     */
+    public boolean isDefaultUser(UserInfo userInfo) {
+        return userInfo.id == CarSettings.DEFAULT_USER_ID_TO_BOOT_INTO;
+    }
+
+    /**
      * Checks whether passed in user is the foreground user.
      *
      * @param userInfo User to check.
@@ -315,6 +317,14 @@
         return mUserManager.isGuestUser();
     }
 
+    /**
+     * Check is the calling app is running as a restricted profile user (ie. a LinkedUser).
+     * Restricted profiles are only available when {@link #isHeadlessSystemUser()} is false.
+     */
+    public boolean isCurrentProcessRestrictedProfileUser() {
+        return mUserManager.isRestrictedProfile();
+    }
+
     // Current process user restriction accessors
 
     /**
@@ -410,6 +420,13 @@
             return false;
         }
 
+        // Not allow to delete the default user for now. Since default user is the one to
+        // boot into.
+        if (isHeadlessSystemUser() && isDefaultUser(userInfo)) {
+            Log.w(TAG, "User " + userInfo.id + " is the default user, could not be removed.");
+            return false;
+        }
+
         if (userInfo.id == getCurrentForegroundUserId()) {
             startNewGuestSession(guestUserName);
         }
diff --git a/car-lib/src/com/android/car/internal/CarRatedFloatListeners.java b/car-lib/src/com/android/car/internal/CarRatedFloatListeners.java
new file mode 100644
index 0000000..c59a2fa
--- /dev/null
+++ b/car-lib/src/com/android/car/internal/CarRatedFloatListeners.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represent listeners for a property grouped by their rate.
+ * T is a type of EventListener such as CarPropertyEventListener
+ * in {@link android.car.hardware.property.CarPropertyManager}
+ * @param <T>
+ * @hide
+ */
+public class CarRatedFloatListeners<T> {
+    private final Map<T, Float> mListenersToRate = new HashMap<>(4);
+
+    private float mUpdateRate;
+
+    protected long mLastUpdateTime = -1;
+
+    protected CarRatedFloatListeners(float rate) {
+        mUpdateRate = rate;
+    }
+
+    /** Check listener */
+    public boolean contains(T listener) {
+        return mListenersToRate.containsKey(listener);
+    }
+    /** Return current rate after updating */
+    public float getRate() {
+        return mUpdateRate;
+    }
+
+    /**
+     * Remove given listener from the list and update rate if necessary.
+     *
+     * @param listener
+     * @return true if rate was updated. Otherwise, returns false.
+     */
+    public boolean remove(T listener) {
+        mListenersToRate.remove(listener);
+        if (mListenersToRate.isEmpty()) {
+            return false;
+        }
+        Float updateRate = Collections.max(mListenersToRate.values());
+        if (updateRate != mUpdateRate) {
+            mUpdateRate = updateRate;
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isEmpty() {
+        return mListenersToRate.isEmpty();
+    }
+
+    /**
+     * Add given listener to the list and update rate if necessary.
+     *
+     * @param listener if null, add part is skipped.
+     * @param updateRate
+     * @return true if rate was updated. Otherwise, returns false.
+     */
+    public boolean addAndUpdateRate(T listener, float updateRate) {
+        Float oldUpdateRate = mListenersToRate.put(listener, updateRate);
+        if (mUpdateRate < updateRate) {
+            mUpdateRate = updateRate;
+            return true;
+        } else if (oldUpdateRate != null && oldUpdateRate == mUpdateRate) {
+            mUpdateRate = Collections.max(mListenersToRate.values());
+        }
+        return false;
+    }
+
+    public Collection<T> getListeners() {
+        return mListenersToRate.keySet();
+    }
+}
+
diff --git a/car-lib/src/com/android/car/internal/SingleMessageHandler.java b/car-lib/src/com/android/car/internal/SingleMessageHandler.java
index f61309c..57788a7 100644
--- a/car-lib/src/com/android/car/internal/SingleMessageHandler.java
+++ b/car-lib/src/com/android/car/internal/SingleMessageHandler.java
@@ -20,6 +20,7 @@
 import android.os.Handler.Callback;
 import android.os.Looper;
 import android.os.Message;
+
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -60,4 +61,5 @@
     public void sendEvents(List<EventType> events) {
         mHandler.sendMessage(mHandler.obtainMessage(mHandledMessageWhat, events));
     }
+
 }
diff --git a/car-support-lib/api/current.txt b/car-support-lib/api/current.txt
index 347c25d..7ee1246 100644
--- a/car-support-lib/api/current.txt
+++ b/car-support-lib/api/current.txt
@@ -152,8 +152,8 @@
     method public abstract boolean isSensorSupported(int) throws android.support.car.CarNotConnectedException;
     method public abstract void removeListener(android.support.car.hardware.CarSensorManager.OnSensorChangedListener);
     method public abstract void removeListener(android.support.car.hardware.CarSensorManager.OnSensorChangedListener, int);
-    field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
-    field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
+    field public static final int SENSOR_RATE_FASTEST = 100; // 0x64
+    field public static final int SENSOR_RATE_NORMAL = 1; // 0x1
     field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
     field public static final int SENSOR_TYPE_COMPASS = 1; // 0x1
     field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
diff --git a/car-support-lib/proguard-release.flags b/car-support-lib/proguard-release.flags
index c6cdd2b..91cab7e 100644
--- a/car-support-lib/proguard-release.flags
+++ b/car-support-lib/proguard-release.flags
@@ -2067,6 +2067,7 @@
 
 -keep class android.app.AppOpsManager$OpEntry {
     <init>(int, int, long, long, int, int, java.lang.String);
+    <init>(int, int, long[], long[], int, boolean, int, java.lang.String);
     <init>(int, int, long[], long[], int, int, java.lang.String);
 
     public int describeContents();
@@ -5133,9 +5134,9 @@
     public static int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
     public static int SYNC_ERROR_TOO_MANY_DELETIONS;
     public static int SYNC_ERROR_TOO_MANY_RETRIES;
-    public static int SYNC_EXEMPTION_ACTIVE;
-    public static int SYNC_EXEMPTION_ACTIVE_WITH_TEMP;
     public static int SYNC_EXEMPTION_NONE;
+    public static int SYNC_EXEMPTION_PROMOTE_BUCKET;
+    public static int SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP;
     public static java.lang.String SYNC_EXTRAS_ACCOUNT;
     public static java.lang.String SYNC_EXTRAS_DISALLOW_METERED;
     public static java.lang.String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS;
@@ -13403,28 +13404,29 @@
 
 -keep class android.media.session.ISessionCallback {
 
-    public abstract void onAdjustVolume(java.lang.String, int, int, int);
-    public abstract void onCommand(java.lang.String, int, int, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
-    public abstract void onCustomAction(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public abstract void onFastForward(java.lang.String, int, int);
+    public abstract void onAdjustVolume(java.lang.String, int, int, android.media.session.ISessionControllerCallback, int);
+    public abstract void onCommand(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    public abstract void onCustomAction(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void onFastForward(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
     public abstract void onMediaButton(java.lang.String, int, int, android.content.Intent, int, android.os.ResultReceiver);
-    public abstract void onNext(java.lang.String, int, int);
-    public abstract void onPause(java.lang.String, int, int);
-    public abstract void onPlay(java.lang.String, int, int);
-    public abstract void onPlayFromMediaId(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public abstract void onPlayFromSearch(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public abstract void onPlayFromUri(java.lang.String, int, int, android.net.Uri, android.os.Bundle);
-    public abstract void onPrepare(java.lang.String, int, int);
-    public abstract void onPrepareFromMediaId(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public abstract void onPrepareFromSearch(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public abstract void onPrepareFromUri(java.lang.String, int, int, android.net.Uri, android.os.Bundle);
-    public abstract void onPrevious(java.lang.String, int, int);
-    public abstract void onRate(java.lang.String, int, int, android.media.Rating);
-    public abstract void onRewind(java.lang.String, int, int);
-    public abstract void onSeekTo(java.lang.String, int, int, long);
-    public abstract void onSetVolumeTo(java.lang.String, int, int, int);
-    public abstract void onSkipToTrack(java.lang.String, int, int, long);
-    public abstract void onStop(java.lang.String, int, int);
+    public abstract void onMediaButtonFromController(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.content.Intent);
+    public abstract void onNext(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public abstract void onPause(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public abstract void onPlay(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public abstract void onPlayFromMediaId(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void onPlayFromSearch(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void onPlayFromUri(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.net.Uri, android.os.Bundle);
+    public abstract void onPrepare(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public abstract void onPrepareFromMediaId(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void onPrepareFromSearch(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void onPrepareFromUri(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.net.Uri, android.os.Bundle);
+    public abstract void onPrevious(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public abstract void onRate(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.media.Rating);
+    public abstract void onRewind(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public abstract void onSeekTo(java.lang.String, int, int, android.media.session.ISessionControllerCallback, long);
+    public abstract void onSetVolumeTo(java.lang.String, int, int, android.media.session.ISessionControllerCallback, int);
+    public abstract void onSkipToTrack(java.lang.String, int, int, android.media.session.ISessionControllerCallback, long);
+    public abstract void onStop(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
 
 
 }
@@ -13441,8 +13443,8 @@
 
 -keep class android.media.session.ISessionController {
 
-    public abstract void adjustVolume(java.lang.String, boolean, int, int);
-    public abstract void fastForward(java.lang.String);
+    public abstract void adjustVolume(java.lang.String, android.media.session.ISessionControllerCallback, boolean, int, int);
+    public abstract void fastForward(java.lang.String, android.media.session.ISessionControllerCallback);
     public abstract android.os.Bundle getExtras();
     public abstract long getFlags();
     public abstract android.app.PendingIntent getLaunchPendingIntent();
@@ -13455,27 +13457,27 @@
     public abstract java.lang.String getTag();
     public abstract android.media.session.ParcelableVolumeInfo getVolumeAttributes();
     public abstract boolean isTransportControlEnabled();
-    public abstract void next(java.lang.String);
-    public abstract void pause(java.lang.String);
-    public abstract void play(java.lang.String);
-    public abstract void playFromMediaId(java.lang.String, java.lang.String, android.os.Bundle);
-    public abstract void playFromSearch(java.lang.String, java.lang.String, android.os.Bundle);
-    public abstract void playFromUri(java.lang.String, android.net.Uri, android.os.Bundle);
-    public abstract void prepare(java.lang.String);
-    public abstract void prepareFromMediaId(java.lang.String, java.lang.String, android.os.Bundle);
-    public abstract void prepareFromSearch(java.lang.String, java.lang.String, android.os.Bundle);
-    public abstract void prepareFromUri(java.lang.String, android.net.Uri, android.os.Bundle);
-    public abstract void previous(java.lang.String);
-    public abstract void rate(java.lang.String, android.media.Rating);
-    public abstract void registerCallbackListener(android.media.session.ISessionControllerCallback);
-    public abstract void rewind(java.lang.String);
-    public abstract void seekTo(java.lang.String, long);
-    public abstract void sendCommand(java.lang.String, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
-    public abstract void sendCustomAction(java.lang.String, java.lang.String, android.os.Bundle);
-    public abstract boolean sendMediaButton(java.lang.String, boolean, android.view.KeyEvent);
-    public abstract void setVolumeTo(java.lang.String, int, int);
-    public abstract void skipToQueueItem(java.lang.String, long);
-    public abstract void stop(java.lang.String);
+    public abstract void next(java.lang.String, android.media.session.ISessionControllerCallback);
+    public abstract void pause(java.lang.String, android.media.session.ISessionControllerCallback);
+    public abstract void play(java.lang.String, android.media.session.ISessionControllerCallback);
+    public abstract void playFromMediaId(java.lang.String, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void playFromSearch(java.lang.String, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void playFromUri(java.lang.String, android.media.session.ISessionControllerCallback, android.net.Uri, android.os.Bundle);
+    public abstract void prepare(java.lang.String, android.media.session.ISessionControllerCallback);
+    public abstract void prepareFromMediaId(java.lang.String, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void prepareFromSearch(java.lang.String, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract void prepareFromUri(java.lang.String, android.media.session.ISessionControllerCallback, android.net.Uri, android.os.Bundle);
+    public abstract void previous(java.lang.String, android.media.session.ISessionControllerCallback);
+    public abstract void rate(java.lang.String, android.media.session.ISessionControllerCallback, android.media.Rating);
+    public abstract void registerCallbackListener(java.lang.String, android.media.session.ISessionControllerCallback);
+    public abstract void rewind(java.lang.String, android.media.session.ISessionControllerCallback);
+    public abstract void seekTo(java.lang.String, android.media.session.ISessionControllerCallback, long);
+    public abstract void sendCommand(java.lang.String, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    public abstract void sendCustomAction(java.lang.String, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public abstract boolean sendMediaButton(java.lang.String, android.media.session.ISessionControllerCallback, boolean, android.view.KeyEvent);
+    public abstract void setVolumeTo(java.lang.String, android.media.session.ISessionControllerCallback, int, int);
+    public abstract void skipToQueueItem(java.lang.String, android.media.session.ISessionControllerCallback, long);
+    public abstract void stop(java.lang.String, android.media.session.ISessionControllerCallback);
     public abstract void unregisterCallbackListener(android.media.session.ISessionControllerCallback);
 
 
@@ -13646,28 +13648,29 @@
 -keep class android.media.session.MediaSession$CallbackStub {
     <init>(android.media.session.MediaSession);
 
-    public void onAdjustVolume(java.lang.String, int, int, int);
-    public void onCommand(java.lang.String, int, int, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
-    public void onCustomAction(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public void onFastForward(java.lang.String, int, int);
+    public void onAdjustVolume(java.lang.String, int, int, android.media.session.ISessionControllerCallback, int);
+    public void onCommand(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    public void onCustomAction(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public void onFastForward(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
     public void onMediaButton(java.lang.String, int, int, android.content.Intent, int, android.os.ResultReceiver);
-    public void onNext(java.lang.String, int, int);
-    public void onPause(java.lang.String, int, int);
-    public void onPlay(java.lang.String, int, int);
-    public void onPlayFromMediaId(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public void onPlayFromSearch(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public void onPlayFromUri(java.lang.String, int, int, android.net.Uri, android.os.Bundle);
-    public void onPrepare(java.lang.String, int, int);
-    public void onPrepareFromMediaId(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public void onPrepareFromSearch(java.lang.String, int, int, java.lang.String, android.os.Bundle);
-    public void onPrepareFromUri(java.lang.String, int, int, android.net.Uri, android.os.Bundle);
-    public void onPrevious(java.lang.String, int, int);
-    public void onRate(java.lang.String, int, int, android.media.Rating);
-    public void onRewind(java.lang.String, int, int);
-    public void onSeekTo(java.lang.String, int, int, long);
-    public void onSetVolumeTo(java.lang.String, int, int, int);
-    public void onSkipToTrack(java.lang.String, int, int, long);
-    public void onStop(java.lang.String, int, int);
+    public void onMediaButtonFromController(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.content.Intent);
+    public void onNext(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public void onPause(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public void onPlay(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public void onPlayFromMediaId(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public void onPlayFromSearch(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public void onPlayFromUri(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.net.Uri, android.os.Bundle);
+    public void onPrepare(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public void onPrepareFromMediaId(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public void onPrepareFromSearch(java.lang.String, int, int, android.media.session.ISessionControllerCallback, java.lang.String, android.os.Bundle);
+    public void onPrepareFromUri(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.net.Uri, android.os.Bundle);
+    public void onPrevious(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public void onRate(java.lang.String, int, int, android.media.session.ISessionControllerCallback, android.media.Rating);
+    public void onRewind(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
+    public void onSeekTo(java.lang.String, int, int, android.media.session.ISessionControllerCallback, long);
+    public void onSetVolumeTo(java.lang.String, int, int, android.media.session.ISessionControllerCallback, int);
+    public void onSkipToTrack(java.lang.String, int, int, android.media.session.ISessionControllerCallback, long);
+    public void onStop(java.lang.String, int, int, android.media.session.ISessionControllerCallback);
 
 
 }
@@ -13776,6 +13779,7 @@
 
 -keep class android.media.session.MediaSessionManager$RemoteUserInfo {
     <init>(java.lang.String, int, int);
+    <init>(java.lang.String, int, int, android.os.IBinder);
 
     public boolean equals(java.lang.Object);
     public java.lang.String getPackageName();
@@ -15459,6 +15463,7 @@
     <init>(android.os.Parcel);
 
     public android.content.Intent buildBrowseIntent();
+    public android.content.Intent buildBrowseIntentForUser(int);
     public static int buildStableMtpStorageId(java.lang.String);
     public android.os.storage.StorageVolume buildStorageVolume(android.content.Context, int, boolean);
     public android.os.storage.VolumeInfo clone();
@@ -20804,7 +20809,6 @@
     public void dispatchInvalidateOnAnimation(android.view.View);
     public void dispatchInvalidateRectDelayed(android.view.View$AttachInfo$InvalidateInfo, long);
     public void dispatchInvalidateRectOnAnimation(android.view.View$AttachInfo$InvalidateInfo);
-    public boolean dispatchKeyFallbackEvent(android.view.KeyEvent);
     public void dispatchKeyFromAutofill(android.view.KeyEvent);
     public void dispatchKeyFromIme(android.view.KeyEvent);
     public void dispatchMoved(int, int);
@@ -20812,6 +20816,7 @@
     public void dispatchRequestKeyboardShortcuts(com.android.internal.os.IResultReceiver, int);
     public void dispatchSystemUiVisibilityChanged(int, int, int, int);
     public void dispatchUnhandledInputEvent(android.view.InputEvent);
+    public boolean dispatchUnhandledKeyEvent(android.view.KeyEvent);
     public void dispatchWindowShown();
     public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     public void dumpGfxInfo(int[]);
@@ -21481,6 +21486,7 @@
     public android.view.WindowInsets consumeSystemWindowInsets(boolean, boolean, boolean, boolean);
     public android.view.WindowInsets consumeWindowDecorInsets();
     public android.view.WindowInsets consumeWindowDecorInsets(boolean, boolean, boolean, boolean);
+    public boolean equals(java.lang.Object);
     public android.view.DisplayCutout getDisplayCutout();
     public int getStableInsetBottom();
     public int getStableInsetLeft();
@@ -21499,6 +21505,9 @@
     public boolean hasStableInsets();
     public boolean hasSystemWindowInsets();
     public boolean hasWindowDecorInsets();
+    public int hashCode();
+    public android.view.WindowInsets inset(android.graphics.Rect);
+    public android.view.WindowInsets inset(int, int, int, int);
     public boolean isConsumed();
     public boolean isRound();
     public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int);
diff --git a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
index 3a21b1e..cfd598a 100644
--- a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
+++ b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
@@ -22,7 +22,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.support.car.content.pm.CarPackageManagerEmbedded;
-import android.support.car.hardware.CarSensorManagerEmbedded;
 import android.support.car.media.CarAudioManagerEmbedded;
 import android.support.car.navigation.CarNavigationStatusManagerEmbedded;
 
@@ -92,8 +91,6 @@
         switch (serviceName) {
             case Car.AUDIO_SERVICE:
                 return new CarAudioManagerEmbedded(manager);
-            case Car.SENSOR_SERVICE:
-                return new CarSensorManagerEmbedded(manager, getContext());
             case Car.INFO_SERVICE:
                 return new CarInfoManagerEmbedded(manager);
             case Car.APP_FOCUS_SERVICE:
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
index 9c13478..29d6f40 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
@@ -215,13 +215,13 @@
     public @interface SensorType {}
 
     /** Read sensor at the default normal rate set for each sensors. This is default rate. */
-    public static final int SENSOR_RATE_NORMAL  = 3;
+    public static final int SENSOR_RATE_NORMAL  = 1;
     /**@hide*/
-    public static final int SENSOR_RATE_UI = 2;
+    public static final int SENSOR_RATE_UI = 5;
     /**@hide*/
-    public static final int SENSOR_RATE_FAST = 1;
+    public static final int SENSOR_RATE_FAST = 10;
     /** Read sensor at the maximum rate. Actual rate will be different depending on the sensor. */
-    public static final int SENSOR_RATE_FASTEST = 0;
+    public static final int SENSOR_RATE_FASTEST = 100;
 
     /** @hide */
     @IntDef({
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
index ac98448..2adc535 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
@@ -140,5 +140,5 @@
     <dimen name="car_keyline_4">182dp</dimen>
 
     <!-- Make the dots in lock pattern thicker in Car -->
-    <dimen name="lock_pattern_dot_size">16dp</dimen>
+    <dimen name="lock_pattern_dot_size">20dp</dimen>
 </resources>
diff --git a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
index eb2a981..f9c8a1e 100644
--- a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
+++ b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
@@ -38,16 +38,14 @@
 import android.car.drivingstate.CarUxRestrictions;
 import android.car.drivingstate.ICarUxRestrictionsChangeListener;
 import android.car.hardware.CarPropertyValue;
-import android.car.hardware.CarSensorEvent;
-import android.car.hardware.CarSensorManager;
-import android.car.hardware.ICarSensorEventListener;
-import android.car.hardware.cabin.CarCabinManager;
 import android.car.hardware.property.CarPropertyEvent;
 import android.car.hardware.property.ICarPropertyEventListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -118,12 +116,9 @@
     private ReentrantLock mCarUserServiceAccessLock;
 
     // Events that are listened to for triggering an auto-connect:
-    // Cabin events like Door unlock coming from the Cabin Service.
-    private final CarCabinService mCarCabinService;
-    private final CarPropertyListener mCabinEventListener;
-    // Sensor events like Ignition switch ON from the Car Sensor Service
-    private final CarSensorService mCarSensorService;
-    private final CarSensorEventListener mCarSensorEventListener;
+    //  Door unlock and ignition switch ON come from Car Property Service
+    private final CarPropertyService mCarPropertyService;
+    private final CarPropertyListener mPropertyEventListener;
 
     // PerUserCarService related listeners
     private final UserServiceConnectionCallback mServiceCallback;
@@ -151,19 +146,17 @@
     private Set<BluetoothDevice> mPairedButUnconnectedDevices = new HashSet<>();
 
     public static BluetoothDeviceConnectionPolicy create(Context context,
-            CarCabinService carCabinService, CarSensorService carSensorService,
-            PerUserCarServiceHelper userServiceHelper, CarUxRestrictionsManagerService uxrService,
-            CarBluetoothService bluetoothService) {
-        return new BluetoothDeviceConnectionPolicy(context, carCabinService, carSensorService,
-                userServiceHelper, uxrService, bluetoothService);
+            CarPropertyService carPropertyService, PerUserCarServiceHelper userServiceHelper,
+            CarUxRestrictionsManagerService uxrService, CarBluetoothService bluetoothService) {
+        return new BluetoothDeviceConnectionPolicy(context, carPropertyService, userServiceHelper,
+                uxrService, bluetoothService);
     }
 
-    private BluetoothDeviceConnectionPolicy(Context context, CarCabinService carCabinService,
-            CarSensorService carSensorService, PerUserCarServiceHelper userServiceHelper,
-            CarUxRestrictionsManagerService uxrService, CarBluetoothService bluetoothService) {
+    private BluetoothDeviceConnectionPolicy(Context context, CarPropertyService carPropertyService,
+            PerUserCarServiceHelper userServiceHelper, CarUxRestrictionsManagerService uxrService,
+            CarBluetoothService bluetoothService) {
         mContext = context;
-        mCarCabinService = carCabinService;
-        mCarSensorService = carSensorService;
+        mCarPropertyService = carPropertyService;
         mUserServiceHelper = userServiceHelper;
         mUxRService = uxrService;
         mCarBluetoothService = bluetoothService;
@@ -203,10 +196,8 @@
             }
         }
 
-        // Listen to Cabin events for triggering auto connect
-        mCabinEventListener = new CarPropertyListener();
-        // Listen to Sensor Events for triggering auto connect
-        mCarSensorEventListener = new CarSensorEventListener();
+        // Listen to events for triggering auto connect
+        mPropertyEventListener = new CarPropertyListener();
         // Listen to UX Restrictions to know when to enable fast-pairing
         mUxRListener = new CarUxRServiceListener();
         // Listen to User switching to connect to per User device.
@@ -606,12 +597,12 @@
      * Bluetooth connection attempts.
      */
     private void setupEventListenersLocked() {
-        // Setting up a listener for events from CarCabinService
-        // For now, we listen to door unlock signal coming from {@link CarCabinService},
-        // and Ignition state START from {@link CarSensorService}
-        mCarCabinService.registerListener(mCabinEventListener);
-        mCarSensorService.registerOrUpdateSensorListener(
-                CarSensorManager.SENSOR_TYPE_IGNITION_STATE, 0, mCarSensorEventListener);
+        // Setting up a listener for events from CarPropertyService
+        // For now, we listen to door unlock signal and Ignition state START coming from
+        // {@link CarPropertyService}
+        mCarPropertyService.registerListener(VehicleProperty.DOOR_LOCK, 0, mPropertyEventListener);
+        mCarPropertyService.registerListener(VehicleProperty.IGNITION_STATE, 0,
+                mPropertyEventListener);
         // Get Current restrictions and handle them
         handleUxRestrictionsChanged(mUxRService.getCurrentUxRestrictions());
         // Register for future changes to the UxRestrictions
@@ -620,58 +611,47 @@
     }
 
     /**
-     * Handles events coming in from the {@link CarCabinService}
-     * The events that can trigger Bluetooth Scanning from CarCabinService is Door Unlock.
-     * Upon receiving the event that is of interest, initiate a connection attempt by calling
+     * Handles events coming in from the {@link CarPropertyService}
+     * The events that can trigger Bluetooth Scanning from CarPropertyService are Door Unlock and
+     * Igntion START.  Upon an event of interest, initiate a connection attempt by calling
      * the policy {@link BluetoothDeviceConnectionPolicy}
      */
     @VisibleForTesting
     class CarPropertyListener extends ICarPropertyEventListener.Stub {
         @Override
-        public void onEvent(CarPropertyEvent event) throws RemoteException {
-            if (DBG) {
-                Log.d(TAG, "Cabin change Event : " + event.getEventType());
-            }
-            Boolean locked;
-            CarPropertyValue value = event.getCarPropertyValue();
-            Object o = value.getValue();
-
-            if (value.getPropertyId() == CarCabinManager.ID_DOOR_LOCK) {
-                if (o instanceof Boolean) {
-                    locked = (Boolean) o;
-                    if (DBG) {
-                        Log.d(TAG, "Door Lock: " + locked);
-                    }
-                    // Attempting a connection only on a door unlock
-                    if (!locked) {
-                        initiateConnection();
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Handles events coming in from the {@link CarSensorService}
-     * The events that can trigger Bluetooth Scanning from CarSensorService is Ignition START.
-     * Upon receiving the event that is of interest, initiate a connection attempt by calling
-     * the policy {@link BluetoothDeviceConnectionPolicy}
-     */
-    private class CarSensorEventListener extends ICarSensorEventListener.Stub {
-        @Override
-        public void onSensorChanged(List<CarSensorEvent> events) throws RemoteException {
-            if (events != null & !events.isEmpty()) {
-                CarSensorEvent event = events.get(0);
+        public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
+            for (CarPropertyEvent event : events) {
                 if (DBG) {
-                    Log.d(TAG, "Sensor event Type : " + event.sensorType);
+                    Log.d(TAG, "Cabin change Event : " + event.getEventType());
                 }
-                if (event.sensorType == CarSensorManager.SENSOR_TYPE_IGNITION_STATE) {
-                    if (DBG) {
-                        Log.d(TAG, "Sensor value : " + event.intValues[0]);
-                    }
-                    if (event.intValues[0] == CarSensorEvent.IGNITION_STATE_START) {
-                        initiateConnection();
-                    }
+                CarPropertyValue value = event.getCarPropertyValue();
+                Object o = value.getValue();
+
+                switch (value.getPropertyId()) {
+                    case VehicleProperty.DOOR_LOCK:
+                        if (o instanceof Boolean) {
+                            Boolean locked = (Boolean) o;
+                            if (DBG) {
+                                Log.d(TAG, "Door Lock: " + locked);
+                            }
+                            // Attempting a connection only on a door unlock
+                            if (!locked) {
+                                initiateConnection();
+                            }
+                        }
+                        break;
+                    case VehicleProperty.IGNITION_STATE:
+                        if (o instanceof Integer) {
+                            Integer state = (Integer) o;
+                            if (DBG) {
+                                Log.d(TAG, "Sensor value : " + state);
+                            }
+                            // Attempting a connection only on IgntionState START
+                            if (state == VehicleIgnitionState.START) {
+                                initiateConnection();
+                            }
+                        }
+                        break;
                 }
             }
         }
@@ -746,9 +726,9 @@
         if (DBG) {
             Log.d(TAG, "closeEventListeners()");
         }
-        mCarCabinService.unregisterListener(mCabinEventListener);
-        mCarSensorService.unregisterSensorListener(CarSensorManager.SENSOR_TYPE_IGNITION_STATE,
-                mCarSensorEventListener);
+        mCarPropertyService.unregisterListener(VehicleProperty.DOOR_LOCK, mPropertyEventListener);
+        mCarPropertyService.unregisterListener(VehicleProperty.IGNITION_STATE,
+                mPropertyEventListener);
         mUserServiceHelper.unregisterServiceCallback(mServiceCallback);
     }
 
@@ -781,7 +761,7 @@
 
     @VisibleForTesting
     CarPropertyListener getCarPropertyListener() {
-        return mCabinEventListener;
+        return mPropertyEventListener;
     }
 
     @VisibleForTesting
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index 77f0f09..1b7ee67 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -138,7 +138,7 @@
                             + " suggested usage: " + AudioAttributes.usageToString(usage));
             final int groupId = getVolumeGroupIdForUsage(usage);
             final int currentVolume = getGroupVolume(groupId);
-            final int flags = AudioManager.FLAG_FROM_KEY;
+            final int flags = AudioManager.FLAG_FROM_KEY | AudioManager.FLAG_SHOW_UI;
             switch (adjustment) {
                 case AudioManager.ADJUST_LOWER:
                     if (currentVolume > getGroupMinVolume(groupId)) {
@@ -152,15 +152,15 @@
                     break;
                 case AudioManager.ADJUST_MUTE:
                     mAudioManager.setMasterMute(true, flags);
-                    callbackMasterMuteChange();
+                    callbackMasterMuteChange(flags);
                     break;
                 case AudioManager.ADJUST_UNMUTE:
                     mAudioManager.setMasterMute(false, flags);
-                    callbackMasterMuteChange();
+                    callbackMasterMuteChange(flags);
                     break;
                 case AudioManager.ADJUST_TOGGLE_MUTE:
                     mAudioManager.setMasterMute(!mAudioManager.isMasterMute(), flags);
-                    callbackMasterMuteChange();
+                    callbackMasterMuteChange(flags);
                     break;
                 case AudioManager.ADJUST_SAME:
                 default:
@@ -185,11 +185,11 @@
                     if (groupId == -1) {
                         Log.w(CarLog.TAG_AUDIO, "Unknown stream type: " + streamType);
                     } else {
-                        callbackGroupVolumeChange(groupId);
+                        callbackGroupVolumeChange(groupId, 0);
                     }
                     break;
                 case AudioManager.MASTER_MUTE_CHANGED_ACTION:
-                    callbackMasterMuteChange();
+                    callbackMasterMuteChange(0);
                     break;
             }
         }
@@ -260,7 +260,7 @@
         synchronized (mImplLock) {
             enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME);
 
-            callbackGroupVolumeChange(groupId);
+            callbackGroupVolumeChange(groupId, flags);
             // For legacy stream type based volume control
             if (!mUseDynamicRouting) {
                 mAudioManager.setStreamVolume(STREAM_TYPES[groupId], index, flags);
@@ -272,22 +272,22 @@
         }
     }
 
-    private void callbackGroupVolumeChange(int groupId) {
+    private void callbackGroupVolumeChange(int groupId, int flags) {
         for (BinderInterfaceContainer.BinderInterface<ICarVolumeCallback> callback :
                 mVolumeCallbackContainer.getInterfaces()) {
             try {
-                callback.binderInterface.onGroupVolumeChanged(groupId);
+                callback.binderInterface.onGroupVolumeChanged(groupId, flags);
             } catch (RemoteException e) {
                 Log.e(CarLog.TAG_AUDIO, "Failed to callback onGroupVolumeChanged", e);
             }
         }
     }
 
-    private void callbackMasterMuteChange() {
+    private void callbackMasterMuteChange(int flags) {
         for (BinderInterfaceContainer.BinderInterface<ICarVolumeCallback> callback :
                 mVolumeCallbackContainer.getInterfaces()) {
             try {
-                callback.binderInterface.onMasterMuteChanged();
+                callback.binderInterface.onMasterMuteChanged(flags);
             } catch (RemoteException e) {
                 Log.e(CarLog.TAG_AUDIO, "Failed to callback onMasterMuteChanged", e);
             }
diff --git a/service/src/com/android/car/CarBluetoothService.java b/service/src/com/android/car/CarBluetoothService.java
index 0702045..d36db43 100644
--- a/service/src/com/android/car/CarBluetoothService.java
+++ b/service/src/com/android/car/CarBluetoothService.java
@@ -59,12 +59,11 @@
     private final BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicy;
     private static final boolean DBG = false;
 
-    public CarBluetoothService(Context context, CarCabinService carCabinService,
-            CarSensorService carSensorService, PerUserCarServiceHelper userSwitchService,
-            CarUxRestrictionsManagerService uxrService) {
+    public CarBluetoothService(Context context, CarPropertyService carPropertyService,
+            PerUserCarServiceHelper userSwitchService, CarUxRestrictionsManagerService uxrService) {
         mContext = context;
         mBluetoothDeviceConnectionPolicy = BluetoothDeviceConnectionPolicy.create(mContext,
-                carCabinService, carSensorService, userSwitchService, uxrService, this);
+                carPropertyService, userSwitchService, uxrService, this);
     }
 
     @Override
diff --git a/service/src/com/android/car/CarCabinService.java b/service/src/com/android/car/CarCabinService.java
deleted file mode 100644
index 316f926..0000000
--- a/service/src/com/android/car/CarCabinService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.car.Car;
-import android.content.Context;
-
-import com.android.car.hal.CabinHalService;
-
-public class CarCabinService extends CarPropertyServiceBase {
-	private final static boolean DBG = false;
-
-    public CarCabinService(Context context, CabinHalService cabinHal) {
-        super(context, cabinHal, Car.PERMISSION_ADJUST_CAR_CABIN, DBG, CarLog.TAG_CABIN);
-    }
-}
diff --git a/service/src/com/android/car/CarDrivingStateService.java b/service/src/com/android/car/CarDrivingStateService.java
index 575e1c6..bc0fbc1 100644
--- a/service/src/com/android/car/CarDrivingStateService.java
+++ b/service/src/com/android/car/CarDrivingStateService.java
@@ -17,14 +17,18 @@
 package com.android.car;
 
 import android.annotation.Nullable;
+import android.car.VehicleAreaType;
 import android.car.drivingstate.CarDrivingStateEvent;
 import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
 import android.car.drivingstate.ICarDrivingState;
 import android.car.drivingstate.ICarDrivingStateChangeListener;
-import android.car.hardware.CarSensorEvent;
-import android.car.hardware.CarSensorManager;
-import android.car.hardware.ICarSensorEventListener;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarPropertyEventListener;
 import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleGear;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -36,66 +40,83 @@
 import java.util.List;
 
 /**
- * A service that infers the current driving state of the vehicle.  It doesn't directly listen to
- * vehicle properties from VHAL to do so.  Instead, it computes the driving state from listening to
- * the relevant sensors from {@link CarSensorService}
+ * A service that infers the current driving state of the vehicle.  It computes the driving state
+ * from listening to relevant properties from {@link CarPropertyService}
  */
 public class CarDrivingStateService extends ICarDrivingState.Stub implements CarServiceBase {
     private static final String TAG = "CarDrivingState";
     private static final boolean DBG = false;
     private static final int MAX_TRANSITION_LOG_SIZE = 20;
+    private static final int PROPERTY_UPDATE_RATE = 5; // Update rate in Hz
+    private static final int NOT_RECEIVED = -1;
     private final Context mContext;
-    private CarSensorService mSensorService;
+    private CarPropertyService mPropertyService;
     // List of clients listening to driving state events.
-    private final List<DrivingStateClient> mDivingStateClients = new ArrayList<>();
-    // Array of sensors that the service needs to listen to from CarSensorService for deriving
-    // the driving state. ToDo (ramperry@) - fine tune this list - b/69859926
-    private static final int[] mRequiredSensors = {
-            CarSensorManager.SENSOR_TYPE_CAR_SPEED,
-            CarSensorManager.SENSOR_TYPE_GEAR};
+    private final List<DrivingStateClient> mDrivingStateClients = new ArrayList<>();
+    // Array of properties that the service needs to listen to from CarPropertyService for deriving
+    // the driving state.
+    private static final int[] REQUIRED_PROPERTIES = {
+            VehicleProperty.PERF_VEHICLE_SPEED,
+            VehicleProperty.GEAR_SELECTION,
+            VehicleProperty.PARKING_BRAKE_ON};
     private CarDrivingStateEvent mCurrentDrivingState;
-    private CarSensorEvent mLastGear;
-    private CarSensorEvent mLastSpeed;
     // For dumpsys logging
     private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>();
+    private int mLastGear;
+    private long mLastGearTimestamp = NOT_RECEIVED;
+    private float mLastSpeed;
+    private long mLastSpeedTimestamp = NOT_RECEIVED;
+    private boolean mLastParkingBrakeState;
+    private long mLastParkingBrakeTimestamp = NOT_RECEIVED;
+    private List<Integer> mSupportedGears;
 
-    public CarDrivingStateService(Context context, CarSensorService sensorService) {
+    public CarDrivingStateService(Context context, CarPropertyService propertyService) {
         mContext = context;
-        mSensorService = sensorService;
+        mPropertyService = propertyService;
         mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
     }
 
     @Override
-    public void init() {
-        if (!checkSensorSupport()) {
+    public synchronized void init() {
+        if (!checkPropertySupport()) {
             Log.e(TAG, "init failure.  Driving state will always be fully restrictive");
             return;
         }
-        subscribeToSensors();
+        subscribeToProperties();
+        mCurrentDrivingState = createDrivingStateEvent(inferDrivingStateLocked());
+        addTransitionLog(TAG + " Boot", CarDrivingStateEvent.DRIVING_STATE_UNKNOWN,
+                mCurrentDrivingState.eventValue, mCurrentDrivingState.timeStamp);
     }
 
     @Override
     public synchronized void release() {
-        for (int sensor : mRequiredSensors) {
-            mSensorService.unregisterSensorListener(sensor, mICarSensorEventListener);
+        for (int property : REQUIRED_PROPERTIES) {
+            mPropertyService.unregisterListener(property, mICarPropertyEventListener);
         }
-        for (DrivingStateClient client : mDivingStateClients) {
+        for (DrivingStateClient client : mDrivingStateClients) {
             client.listenerBinder.unlinkToDeath(client, 0);
         }
-        mDivingStateClients.clear();
+        mDrivingStateClients.clear();
         mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
     }
 
     /**
-     * Checks if the {@link CarSensorService} supports the required sensors.
+     * Checks if the {@link CarPropertyService} supports the required properties.
      *
      * @return {@code true} if supported, {@code false} if not
      */
-    private synchronized boolean checkSensorSupport() {
-        int sensorList[] = mSensorService.getSupportedSensors();
-        for (int sensor : mRequiredSensors) {
-            if (!CarSensorManager.isSensorSupported(sensorList, sensor)) {
-                Log.e(TAG, "Required sensor not supported: " + sensor);
+    private synchronized boolean checkPropertySupport() {
+        List<CarPropertyConfig> configs = mPropertyService.getPropertyList();
+        for (int propertyId : REQUIRED_PROPERTIES) {
+            boolean found = false;
+            for (CarPropertyConfig config : configs) {
+                if (config.getPropertyId() == propertyId) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                Log.e(TAG, "Required property not supported: " + propertyId);
                 return false;
             }
         }
@@ -103,13 +124,12 @@
     }
 
     /**
-     * Subscribe to the {@link CarSensorService} for required sensors.
+     * Subscribe to the {@link CarPropertyService} for required sensors.
      */
-    private synchronized void subscribeToSensors() {
-        for (int sensor : mRequiredSensors) {
-            mSensorService.registerOrUpdateSensorListener(sensor,
-                    CarSensorManager.SENSOR_RATE_UI,
-                    mICarSensorEventListener);
+    private synchronized void subscribeToProperties() {
+        for (int propertyId : REQUIRED_PROPERTIES) {
+            mPropertyService.registerListener(propertyId, PROPERTY_UPDATE_RATE,
+                    mICarPropertyEventListener);
         }
 
     }
@@ -142,7 +162,7 @@
                 Log.e(TAG, "Cannot link death recipient to binder " + e);
                 return;
             }
-            mDivingStateClients.add(client);
+            mDrivingStateClients.add(client);
         }
     }
 
@@ -158,7 +178,7 @@
             ICarDrivingStateChangeListener listener) {
         IBinder binder = listener.asBinder();
         // Find the listener by comparing the binder object they host.
-        for (DrivingStateClient client : mDivingStateClients) {
+        for (DrivingStateClient client : mDrivingStateClients) {
             if (client.isHoldingBinder(binder)) {
                 return client;
             }
@@ -186,7 +206,7 @@
             return;
         }
         listener.asBinder().unlinkToDeath(client, 0);
-        mDivingStateClients.remove(client);
+        mDrivingStateClients.remove(client);
     }
 
     /**
@@ -222,7 +242,7 @@
             }
             listenerBinder.unlinkToDeath(this, 0);
             synchronized (CarDrivingStateService.this) {
-                mDivingStateClients.remove(this);
+                mDrivingStateClients.remove(this);
             }
         }
 
@@ -257,37 +277,90 @@
 
     @Override
     public void dump(PrintWriter writer) {
-        writer.println("Driving state chane log:");
+        writer.println("Driving state change log:");
         for (Utils.TransitionLog tLog : mTransitionLogs) {
             writer.println(tLog);
         }
         writer.println("Current Driving State: " + mCurrentDrivingState.eventValue);
+        if (mSupportedGears != null) {
+            writer.println("Supported gears:");
+            for (Integer gear : mSupportedGears) {
+                writer.print("Gear:" + gear);
+            }
+        }
     }
 
     /**
-     * {@link CarSensorEvent} listener registered with the {@link CarSensorService} for getting
-     * sensor change notifications.
+     * {@link CarPropertyEvent} listener registered with the {@link CarPropertyService} for getting
+     * property change notifications.
      */
-    private final ICarSensorEventListener mICarSensorEventListener =
-            new ICarSensorEventListener.Stub() {
+    private final ICarPropertyEventListener mICarPropertyEventListener =
+            new ICarPropertyEventListener.Stub() {
                 @Override
-                public void onSensorChanged(List<CarSensorEvent> events) {
-                    for (CarSensorEvent event : events) {
-                        Log.d(TAG, "Sensor Changed:" + event.sensorType);
-                        handleSensorEvent(event);
+                public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
+                    for (CarPropertyEvent event : events) {
+                        handlePropertyEvent(event);
                     }
                 }
             };
 
     /**
-     * Handle the sensor events coming from the {@link CarSensorService}.
-     * Compute the driving state, map it to the corresponding UX Restrictions and dispatch the
-     * events to the registered clients.
+     * Handle events coming from {@link CarPropertyService}.  Compute the driving state, map it to
+     * the corresponding UX Restrictions and dispatch the events to the registered clients.
      */
-    private synchronized void handleSensorEvent(CarSensorEvent event) {
-        switch (event.sensorType) {
-            case CarSensorManager.SENSOR_TYPE_GEAR:
-            case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
+    private synchronized void handlePropertyEvent(CarPropertyEvent event) {
+        switch (event.getEventType()) {
+            case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
+                CarPropertyValue value = event.getCarPropertyValue();
+                int propId = value.getPropertyId();
+                long curTimestamp = value.getTimestamp();
+                Log.d(TAG, "Property Changed: propId=" + propId);
+                switch (propId) {
+                    case VehicleProperty.PERF_VEHICLE_SPEED:
+                        float curSpeed = (Float) value.getValue();
+                        if (DBG) {
+                            Log.d(TAG, "Speed: " + curSpeed + "@" + curTimestamp);
+                        }
+                        if (curTimestamp > mLastSpeedTimestamp) {
+                            mLastSpeedTimestamp = curTimestamp;
+                            mLastSpeed = curSpeed;
+                        } else if (DBG) {
+                            Log.d(TAG, "Ignoring speed with older timestamp:" + curTimestamp);
+                        }
+                        break;
+                    case VehicleProperty.GEAR_SELECTION:
+                        if (mSupportedGears == null) {
+                            mSupportedGears = getSupportedGears();
+                        }
+                        int curGear = (Integer) value.getValue();
+                        if (DBG) {
+                            Log.d(TAG, "Gear: " + curGear + "@" + curTimestamp);
+                        }
+                        if (curTimestamp > mLastGearTimestamp) {
+                            mLastGearTimestamp = curTimestamp;
+                            mLastGear = (Integer) value.getValue();
+                        } else if (DBG) {
+                            Log.d(TAG, "Ignoring Gear with older timestamp:" + curTimestamp);
+                        }
+                        break;
+                    case VehicleProperty.PARKING_BRAKE_ON:
+                        boolean curParkingBrake = (boolean) value.getValue();
+                        if (DBG) {
+                            Log.d(TAG, "Parking Brake: " + curParkingBrake + "@" + curTimestamp);
+                        }
+                        if (curTimestamp > mLastParkingBrakeTimestamp) {
+                            mLastParkingBrakeTimestamp = curTimestamp;
+                            mLastParkingBrakeState = curParkingBrake;
+                        } else if (DBG) {
+                            Log.d(TAG, "Ignoring Parking Brake status with an older timestamp:"
+                                    + curTimestamp);
+                        }
+                        break;
+                    default:
+                        Log.e(TAG, "Received property event for unhandled propId=" + propId);
+                        break;
+                }
+
                 int drivingState = inferDrivingStateLocked();
                 // Check if the driving state has changed.  If it has, update our records and
                 // dispatch the new events to the listeners.
@@ -295,26 +368,35 @@
                     Log.d(TAG, "Driving state new->old " + drivingState + "->"
                             + mCurrentDrivingState.eventValue);
                 }
-                if (drivingState == mCurrentDrivingState.eventValue) {
-                    break;
-                }
-                addTransitionLog(TAG, mCurrentDrivingState.eventValue, drivingState,
-                        System.currentTimeMillis());
-                // Update if there is a change in state.
-                mCurrentDrivingState = createDrivingStateEvent(drivingState);
-
-                if (DBG) {
-                    Log.d(TAG, "dispatching to " + mDivingStateClients.size() + " clients");
-                }
-                for (DrivingStateClient client : mDivingStateClients) {
-                    client.dispatchEventToClients(mCurrentDrivingState);
+                if (drivingState != mCurrentDrivingState.eventValue) {
+                    addTransitionLog(TAG, mCurrentDrivingState.eventValue, drivingState,
+                            System.currentTimeMillis());
+                    // Update if there is a change in state.
+                    mCurrentDrivingState = createDrivingStateEvent(drivingState);
+                    if (DBG) {
+                        Log.d(TAG, "dispatching to " + mDrivingStateClients.size() + " clients");
+                    }
+                    for (DrivingStateClient client : mDrivingStateClients) {
+                        client.dispatchEventToClients(mCurrentDrivingState);
+                    }
                 }
                 break;
             default:
+                // Unhandled event
                 break;
         }
     }
 
+    private List<Integer> getSupportedGears() {
+        List<CarPropertyConfig> properyList = mPropertyService.getPropertyList();
+        for (CarPropertyConfig p : properyList) {
+            if (p.getPropertyId() == VehicleProperty.GEAR_SELECTION) {
+                return p.getConfigArray();
+            }
+        }
+        return null;
+    }
+
     private void addTransitionLog(String name, int from, int to, long timestamp) {
         if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
             mTransitionLogs.remove();
@@ -327,80 +409,124 @@
     /**
      * Infers the current driving state of the car from the other Car Sensor properties like
      * Current Gear, Speed etc.
-     * ToDo (ramperry@) - Fine tune this - b/69859926
      *
      * @return Current driving state
      */
     @CarDrivingState
     private int inferDrivingStateLocked() {
-        int drivingState = CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
-        CarSensorEvent currentGear = mSensorService.getLatestSensorEvent(
-                CarSensorManager.SENSOR_TYPE_GEAR);
-        CarSensorEvent currentSpeed = mSensorService.getLatestSensorEvent(
-                CarSensorManager.SENSOR_TYPE_CAR_SPEED);
-
-        // Ignoring data with older timestamps if we get them out of order.
-        if (currentSpeed != null) {
-            if (DBG) {
-                Log.d(TAG, "Speed: " + currentSpeed.floatValues[0] + "@" + currentSpeed.timestamp);
-            }
-            if (mLastSpeed != null && currentSpeed.timestamp < mLastSpeed.timestamp) {
-                if (DBG) {
-                    Log.d(TAG, "Ignoring speed with older timestamp:" + currentSpeed.timestamp);
-                }
-                // assign the last speed to current speed, since that has a more recent timestamp
-                // and let the logic flow through.
-                currentSpeed = mLastSpeed;
-            } else {
-                mLastSpeed = currentSpeed;
-            }
+        updateVehiclePropertiesIfNeeded();
+        if (DBG) {
+            Log.d(TAG, "Last known Gear:" + mLastGear + " Last known speed:" + mLastSpeed);
         }
 
-        if (currentGear != null) {
-            if (DBG) {
-                Log.d(TAG, "Gear: " + currentGear.intValues[0] + "@" + currentGear.timestamp);
-            }
-            if (mLastGear != null && currentGear.timestamp < mLastGear.timestamp) {
-                if (DBG) {
-                    Log.d(TAG, "Ignoring Gear with older timestamp:" + currentGear.timestamp);
-                }
-                currentGear = mLastGear;
-            } else {
-                mLastGear = currentGear;
-            }
-        }
         /*
-            Simple logic to start off deriving driving state:
+            Logic to start off deriving driving state:
             1. If gear == parked, then Driving State is parked.
             2. If gear != parked,
-                2a. if speed == 0, then driving state is idling
-                2b. if speed != 0, then driving state is moving
-                2c. if speed unavailable, then driving state is unknown
-            This logic needs to be tested and iterated on.  Tracked in b/69859926
+                    2a. if parking brake is applied, then Driving state is parked.
+                    2b. if parking brake is not applied or unknown/unavailable, then driving state
+                    is still unknown.
+            3. If driving state is unknown at the end of step 2,
+                3a. if speed == 0, then driving state is idling
+                3b. if speed != 0, then driving state is moving
+                3c. if speed unavailable, then driving state is unknown
          */
-        if (currentGear != null) {
-            if (isGearInParking(currentGear)) {
-                drivingState = CarDrivingStateEvent.DRIVING_STATE_PARKED;
-            } else if (currentSpeed == null) {
-                drivingState = CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
-            } else {
-                if (currentSpeed.floatValues[0] == 0f) {
-                    drivingState = CarDrivingStateEvent.DRIVING_STATE_IDLING;
-                } else {
-                    drivingState = CarDrivingStateEvent.DRIVING_STATE_MOVING;
+
+        if (isVehicleKnownToBeParked()) {
+            return CarDrivingStateEvent.DRIVING_STATE_PARKED;
+        }
+
+        // We don't know if the vehicle is parked, let's look at the speed.
+        if (mLastSpeedTimestamp == NOT_RECEIVED || mLastSpeed < 0) {
+            return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
+        } else if (mLastSpeed == 0f) {
+            return CarDrivingStateEvent.DRIVING_STATE_IDLING;
+        } else {
+            return CarDrivingStateEvent.DRIVING_STATE_MOVING;
+        }
+    }
+
+    /**
+     * Find if we have signals to know if the vehicle is parked
+     *
+     * @return true if we have enough information to say the vehicle is parked.
+     * false, if the vehicle is either not parked or if we don't have any information.
+     */
+    private boolean isVehicleKnownToBeParked() {
+        // If we know the gear is in park, return true
+        if (mLastGearTimestamp != NOT_RECEIVED && mLastGear == VehicleGear.GEAR_PARK) {
+            return true;
+        } else if (mLastParkingBrakeTimestamp != NOT_RECEIVED) {
+            // if gear is not in park or unknown, look for status of parking brake if transmission
+            // type is manual.
+            if (isCarManualTransmissionType()) {
+                return mLastParkingBrakeState;
+            }
+        }
+        // if neither information is available, return false to indicate we can't determine
+        // if the vehicle is parked.
+        return false;
+    }
+
+    /**
+     * If Supported gears information is available and GEAR_PARK is not one of the supported gears,
+     * transmission type is considered to be Manual.  Automatic transmission is assumed otherwise.
+     */
+    private boolean isCarManualTransmissionType() {
+        if (mSupportedGears != null
+                && !mSupportedGears.isEmpty()
+                && !mSupportedGears.contains(VehicleGear.GEAR_PARK)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Try querying the gear selection and parking brake if we haven't received the event yet.
+     * This could happen if the gear change occurred before car service booted up like in the
+     * case of a HU restart in the middle of a drive.  Since gear and parking brake are
+     * on-change only properties, we could be in this situation where we will have to query
+     * VHAL.
+     */
+    private void updateVehiclePropertiesIfNeeded() {
+        if (mLastGearTimestamp == NOT_RECEIVED) {
+            CarPropertyValue propertyValue = mPropertyService.getProperty(
+                    VehicleProperty.GEAR_SELECTION,
+                    VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
+            if (propertyValue != null) {
+                mLastGear = (Integer) propertyValue.getValue();
+                mLastGearTimestamp = propertyValue.getTimestamp();
+                if (DBG) {
+                    Log.d(TAG, "updateVehiclePropertiesIfNeeded: gear:" + mLastGear);
                 }
             }
         }
-        return drivingState;
-    }
 
-    private boolean isSpeedZero(CarSensorEvent event) {
-        return event.floatValues[0] == 0f;
-    }
+        if (mLastParkingBrakeTimestamp == NOT_RECEIVED) {
+            CarPropertyValue propertyValue = mPropertyService.getProperty(
+                    VehicleProperty.PARKING_BRAKE_ON,
+                    VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
+            if (propertyValue != null) {
+                mLastParkingBrakeState = (boolean) propertyValue.getValue();
+                mLastParkingBrakeTimestamp = propertyValue.getTimestamp();
+                if (DBG) {
+                    Log.d(TAG, "updateVehiclePropertiesIfNeeded: brake:" + mLastParkingBrakeState);
+                }
+            }
+        }
 
-    private boolean isGearInParking(CarSensorEvent event) {
-        int gear = event.intValues[0];
-        return gear == CarSensorEvent.GEAR_PARK;
+        if (mLastSpeedTimestamp == NOT_RECEIVED) {
+            CarPropertyValue propertyValue = mPropertyService.getProperty(
+                    VehicleProperty.PERF_VEHICLE_SPEED,
+                    VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
+            if (propertyValue != null) {
+                mLastSpeed = (float) propertyValue.getValue();
+                mLastSpeedTimestamp = propertyValue.getTimestamp();
+                if (DBG) {
+                    Log.d(TAG, "updateVehiclePropertiesIfNeeded: speed:" + mLastSpeed);
+                }
+            }
+        }
     }
 
     private static CarDrivingStateEvent createDrivingStateEvent(int eventValue) {
diff --git a/service/src/com/android/car/CarHvacService.java b/service/src/com/android/car/CarHvacService.java
deleted file mode 100644
index cb5922b..0000000
--- a/service/src/com/android/car/CarHvacService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.car.Car;
-import android.content.Context;
-
-import com.android.car.hal.HvacHalService;
-
-public class CarHvacService extends CarPropertyServiceBase {
-    private final static boolean DBG = false;
-
-    public CarHvacService(Context context, HvacHalService hvacHal) {
-        super(context, hvacHal, Car.PERMISSION_CONTROL_CAR_CLIMATE, DBG, CarLog.TAG_HVAC);
-    }
-}
diff --git a/service/src/com/android/car/CarInfoService.java b/service/src/com/android/car/CarInfoService.java
deleted file mode 100644
index a05f343..0000000
--- a/service/src/com/android/car/CarInfoService.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.car.CarInfoManager;
-import android.car.ICarInfo;
-import android.content.Context;
-import android.os.Bundle;
-import android.provider.Settings;
-
-import com.android.car.hal.InfoHalService;
-import com.android.car.internal.FeatureConfiguration;
-import com.android.car.internal.FeatureUtil;
-
-import java.io.PrintWriter;
-
-public class CarInfoService extends ICarInfo.Stub implements CarServiceBase {
-
-    private final InfoHalService mInfoHal;
-    private final Context mContext;
-
-    public CarInfoService(Context context, InfoHalService infoHal) {
-        mInfoHal = infoHal;
-        mContext = context;
-    }
-
-    @Override
-    public Bundle getBasicInfo() {
-        return mInfoHal.getBasicInfo();
-    }
-
-    @Override
-    public String getStringInfo(String key) {
-        switch (key) {
-            case CarInfoManager.INFO_KEY_PRODUCT_CONFIGURATION:
-                FeatureUtil.assertFeature(
-                        FeatureConfiguration.ENABLE_PRODUCT_CONFIGURATION_INFO);
-                // still protect with if-feature code. code under if can be dropped by
-                // proguard if necessary.
-                if (FeatureConfiguration.ENABLE_PRODUCT_CONFIGURATION_INFO) {
-                    //TODO get it from HAL layer
-                    return null;
-                }
-                break;
-            default: // just throw exception
-                break;
-        }
-        throw new IllegalArgumentException("Unsupported key:" + key);
-    }
-
-    @Override
-    public void init() {
-        Bundle info = mInfoHal.getBasicInfo();
-        // do not update ID immediately even if user clears it.
-        info.putString(CarInfoManager.BASIC_INFO_KEY_VEHICLE_ID,
-                Settings.Secure.getString(mContext.getContentResolver(),
-                        Settings.Secure.ANDROID_ID));
-    }
-
-    @Override
-    public synchronized void release() {
-        //nothing to do
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-        writer.println("*CarInfoService*");
-        writer.println("**Check HAL dump");
-    }
-}
-
diff --git a/service/src/com/android/car/CarLocationService.java b/service/src/com/android/car/CarLocationService.java
index 92c83bc..6d93b40 100644
--- a/service/src/com/android/car/CarLocationService.java
+++ b/service/src/com/android/car/CarLocationService.java
@@ -16,13 +16,15 @@
 
 package com.android.car;
 
-import android.car.hardware.CarSensorEvent;
-import android.car.hardware.CarSensorManager;
-import android.car.hardware.ICarSensorEventListener;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarPropertyEventListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.location.Location;
 import android.location.LocationManager;
 import android.os.Handler;
@@ -64,19 +66,19 @@
 
     private final Context mContext;
     private final CarPowerManagementService mCarPowerManagementService;
-    private final CarSensorService mCarSensorService;
-    private final CarSensorEventListener mCarSensorEventListener;
+    private final CarPropertyService mCarPropertyService;
+    private final CarPropertyEventListener mCarPropertyEventListener;
     private int mTaskCount = 0;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
 
     public CarLocationService(Context context, CarPowerManagementService carPowerManagementService,
-            CarSensorService carSensorService) {
+            CarPropertyService carPropertyService) {
         logd("constructed");
         mContext = context;
         mCarPowerManagementService = carPowerManagementService;
-        mCarSensorService = carSensorService;
-        mCarSensorEventListener = new CarSensorEventListener();
+        mCarPropertyService = carPropertyService;
+        mCarPropertyEventListener = new CarPropertyEventListener();
     }
 
     @Override
@@ -87,16 +89,16 @@
         filter.addAction(LocationManager.MODE_CHANGED_ACTION);
         filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
         mContext.registerReceiver(this, filter);
-        mCarSensorService.registerOrUpdateSensorListener(
-                CarSensorManager.SENSOR_TYPE_IGNITION_STATE, 0, mCarSensorEventListener);
+        mCarPropertyService.registerListener(VehicleProperty.IGNITION_STATE, 0,
+                mCarPropertyEventListener);
         mCarPowerManagementService.registerPowerEventProcessingHandler(this);
     }
 
     @Override
     public void release() {
         logd("release");
-        mCarSensorService.unregisterSensorListener(CarSensorManager.SENSOR_TYPE_IGNITION_STATE,
-                mCarSensorEventListener);
+        mCarPropertyService.unregisterListener(VehicleProperty.IGNITION_STATE,
+                mCarPropertyEventListener);
         mContext.unregisterReceiver(this);
     }
 
@@ -104,7 +106,7 @@
     public void dump(PrintWriter writer) {
         writer.println(TAG);
         writer.println("Context: " + mContext);
-        writer.println("CarSensorService: " + mCarSensorService);
+        writer.println("CarPropertyService: " + mCarPropertyService);
     }
 
     @Override
@@ -313,15 +315,20 @@
         }
     }
 
-    private class CarSensorEventListener extends ICarSensorEventListener.Stub {
+    private class CarPropertyEventListener extends ICarPropertyEventListener.Stub {
         @Override
-        public void onSensorChanged(List<CarSensorEvent> events) throws RemoteException {
-            CarSensorEvent event = events.get(0);
-            if (event.sensorType == CarSensorManager.SENSOR_TYPE_IGNITION_STATE) {
-                logd("sensor ignition value: " + event.intValues[0]);
-                if (event.intValues[0] == CarSensorEvent.IGNITION_STATE_OFF) {
-                    logd("ignition off");
-                    asyncOperation(() -> storeLocation());
+        public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
+            for (CarPropertyEvent event : events) {
+                if (event.getEventType() == CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) {
+                    CarPropertyValue value = event.getCarPropertyValue();
+                    if (value.getPropertyId() == VehicleProperty.IGNITION_STATE) {
+                        int ignitionState = (Integer) value.getValue();
+                        logd("property ignition value: " + ignitionState);
+                        if (ignitionState == VehicleIgnitionState.OFF) {
+                            logd("ignition off");
+                            asyncOperation(() -> storeLocation());
+                        }
+                    }
                 }
             }
         }
diff --git a/service/src/com/android/car/CarNightService.java b/service/src/com/android/car/CarNightService.java
index 212bb1a..e43ed3d 100644
--- a/service/src/com/android/car/CarNightService.java
+++ b/service/src/com/android/car/CarNightService.java
@@ -18,10 +18,12 @@
 
 import android.annotation.IntDef;
 import android.app.UiModeManager;
-import android.car.hardware.CarSensorEvent;
-import android.car.hardware.CarSensorManager;
-import android.car.hardware.ICarSensorEventListener;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarPropertyEventListener;
 import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.RemoteException;
 import android.util.Log;
 
 import java.io.PrintWriter;
@@ -29,7 +31,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
-
 public class CarNightService implements CarServiceBase {
 
     public static final boolean DBG = false;
@@ -46,38 +47,44 @@
     private int mForcedMode = FORCED_SENSOR_MODE;
     private final Context mContext;
     private final UiModeManager mUiModeManager;
-    private CarSensorService mCarSensorService;
+    private CarPropertyService mCarPropertyService;
 
-    private final ICarSensorEventListener mICarSensorEventListener =
-            new ICarSensorEventListener.Stub() {
+    private final ICarPropertyEventListener mICarPropertyEventListener =
+            new ICarPropertyEventListener.Stub() {
         @Override
-        public void onSensorChanged(List<CarSensorEvent> events) {
-            if (!events.isEmpty()) {
-                CarSensorEvent event = events.get(events.size() - 1);
-                handleSensorEvent(event);
+        public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
+            for (CarPropertyEvent event : events) {
+                handlePropertyEvent(event);
             }
         }
     };
 
-    public synchronized void handleSensorEvent(CarSensorEvent event) {
+    /**
+     * Handle CarPropertyEvents
+     * @param event
+     */
+    public synchronized void handlePropertyEvent(CarPropertyEvent event) {
         if (event == null) {
             return;
         }
-        if (event.sensorType == CarSensorManager.SENSOR_TYPE_NIGHT) {
-            if (event.intValues[0] == 0) {
-                mNightSetting = UiModeManager.MODE_NIGHT_NO;
-                if (DBG)  Log.d(CarLog.TAG_SENSOR,"CAR dayNight handleSensorEvent DAY");
-            }
-            else {
-                mNightSetting = UiModeManager.MODE_NIGHT_YES;
-                if (DBG)  Log.d(CarLog.TAG_SENSOR,"CAR dayNight handleSensorEvent NIGHT");
-            }
-
-            if (mUiModeManager != null && (mForcedMode == FORCED_SENSOR_MODE)) {
-                mUiModeManager.setNightMode(mNightSetting);
-                if (DBG)  Log.d(CarLog.TAG_SENSOR,"CAR dayNight handleSensorEvent APPLIED");
-            } else {
-                if (DBG)  Log.d(CarLog.TAG_SENSOR,"CAR dayNight handleSensorEvent IGNORED");
+        if (event.getEventType() == CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) {
+            // Only handle onChange events
+            CarPropertyValue value = event.getCarPropertyValue();
+            if (value.getPropertyId() == VehicleProperty.NIGHT_MODE) {
+                boolean nightMode = (Boolean) value.getValue();
+                if (nightMode) {
+                    mNightSetting = UiModeManager.MODE_NIGHT_YES;
+                    if (DBG)  Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent NIGHT");
+                } else {
+                    mNightSetting = UiModeManager.MODE_NIGHT_NO;
+                    if (DBG)  Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent DAY");
+                }
+                if (mUiModeManager != null && (mForcedMode == FORCED_SENSOR_MODE)) {
+                    mUiModeManager.setNightMode(mNightSetting);
+                    if (DBG)  Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent APPLIED");
+                } else {
+                    if (DBG)  Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent IGNORED");
+                }
             }
         }
     }
@@ -108,9 +115,9 @@
         return mUiModeManager.getNightMode();
     }
 
-    CarNightService(Context context, CarSensorService sensorService) {
+    CarNightService(Context context, CarPropertyService propertyService) {
         mContext = context;
-        mCarSensorService = sensorService;
+        mCarPropertyService = propertyService;
         mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
         if (mUiModeManager == null) {
             Log.w(CarLog.TAG_SENSOR, "Failed to get UI_MODE_SERVICE");
@@ -122,11 +129,8 @@
         if (DBG) {
             Log.d(CarLog.TAG_SENSOR,"CAR dayNight init.");
         }
-        mCarSensorService.registerOrUpdateSensorListener(CarSensorManager.SENSOR_TYPE_NIGHT,
-                CarSensorManager.SENSOR_RATE_NORMAL, mICarSensorEventListener);
-        CarSensorEvent currentState = mCarSensorService.getLatestSensorEvent(
-                CarSensorManager.SENSOR_TYPE_NIGHT);
-        handleSensorEvent(currentState);
+        mCarPropertyService.registerListener(VehicleProperty.NIGHT_MODE, 0,
+                mICarPropertyEventListener);
     }
 
     @Override
diff --git a/service/src/com/android/car/CarPropertyService.java b/service/src/com/android/car/CarPropertyService.java
new file mode 100644
index 0000000..68c4b8c
--- /dev/null
+++ b/service/src/com/android/car/CarPropertyService.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2018 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 java.lang.Integer.toHexString;
+
+import android.car.Car;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarProperty;
+import android.car.hardware.property.ICarPropertyEventListener;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.android.car.hal.PropertyHalService;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * This class implements the binder interface for ICarProperty.aidl to make it easier to create
+ * multiple managers that deal with Vehicle Properties. To create a new service, simply extend
+ * this class and call the super() constructor with the appropriate arguments for the new service.
+ * {@link CarHvacService} shows the basic usage.
+ */
+public class CarPropertyService extends ICarProperty.Stub
+        implements CarServiceBase, PropertyHalService.PropertyHalListener {
+    private static final boolean DBG = true;
+    private static final String TAG = "Property.service";
+    private final Context mContext;
+    private final Map<IBinder, Client> mClientMap = new ConcurrentHashMap<>();
+    private Map<Integer, CarPropertyConfig<?>> mConfigs;
+    private final PropertyHalService mHal;
+    private boolean mListenerIsSet = false;
+    private final Map<Integer, List<Client>> mPropIdClientMap = new ConcurrentHashMap<>();
+    private final Object mLock = new Object();
+
+    public CarPropertyService(Context context, PropertyHalService hal) {
+        if (DBG) {
+            Log.d(TAG, "CarPropertyService started!");
+        }
+        mHal = hal;
+        mContext = context;
+    }
+
+    // Helper class to keep track of listeners to this service
+    private class Client implements IBinder.DeathRecipient {
+        private final ICarPropertyEventListener mListener;
+        private final IBinder mListenerBinder;
+        private final SparseArray<Float> mRateMap = new SparseArray<Float>();   // key is propId
+
+        Client(ICarPropertyEventListener listener) {
+            mListener = listener;
+            mListenerBinder = listener.asBinder();
+
+            try {
+                mListenerBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to link death for recipient. " + e);
+                throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
+            }
+            mClientMap.put(mListenerBinder, this);
+        }
+
+        void addProperty(int propId, float rate) {
+            mRateMap.put(propId, rate);
+        }
+
+        /**
+         * Client died. Remove the listener from HAL service and unregister if this is the last
+         * client.
+         */
+        @Override
+        public void binderDied() {
+            if (DBG) {
+                Log.d(TAG, "binderDied " + mListenerBinder);
+            }
+
+            for (int i = 0; i < mRateMap.size(); i++) {
+                int propId = mRateMap.keyAt(i);
+                CarPropertyService.this.unregisterListenerBinderLocked(propId, mListenerBinder);
+            }
+            this.release();
+        }
+
+        ICarPropertyEventListener getListener() {
+            return mListener;
+        }
+
+        IBinder getListenerBinder() {
+            return mListenerBinder;
+        }
+
+        float getRate(int propId) {
+            // Return 0 if no key found, since that is the slowest rate.
+            return mRateMap.get(propId, (float) 0);
+        }
+
+        void release() {
+            mListenerBinder.unlinkToDeath(this, 0);
+            mClientMap.remove(mListenerBinder);
+        }
+
+        void removeProperty(int propId) {
+            mRateMap.remove(propId);
+            if (mRateMap.size() == 0) {
+                // Last property was released, remove the client.
+                this.release();
+            }
+        }
+    }
+
+    @Override
+    public void init() {
+    }
+
+    @Override
+    public void release() {
+        for (Client c : mClientMap.values()) {
+            c.release();
+        }
+        mClientMap.clear();
+        mPropIdClientMap.clear();
+        mHal.setListener(null);
+        mListenerIsSet = false;
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+    }
+
+    @Override
+    public void registerListener(int propId, float rate, ICarPropertyEventListener listener) {
+        if (DBG) {
+            Log.d(TAG, "registerListener: propId=0x" + toHexString(propId) + " rate=" + rate);
+        }
+        if (mConfigs.get(propId) == null) {
+            // Do not attempt to register an invalid propId
+            Log.e(TAG, "registerListener:  propId is not in config list:  " + propId);
+            return;
+        }
+        ICarImpl.assertPermission(mContext, mHal.getReadPermission(propId));
+        if (listener == null) {
+            Log.e(TAG, "registerListener: Listener is null.");
+            throw new IllegalArgumentException("listener cannot be null.");
+        }
+
+        IBinder listenerBinder = listener.asBinder();
+
+        synchronized (mLock) {
+            // Get the client for this listener
+            Client client = mClientMap.get(listenerBinder);
+            if (client == null) {
+                client = new Client(listener);
+            }
+            client.addProperty(propId, rate);
+            // Insert the client into the propId --> clients map
+            List<Client> clients = mPropIdClientMap.get(propId);
+            if (clients == null) {
+                clients = new CopyOnWriteArrayList<Client>();
+                mPropIdClientMap.put(propId, clients);
+            }
+            if (!clients.contains(client)) {
+                clients.add(client);
+            }
+            // Set the HAL listener if necessary
+            if (!mListenerIsSet) {
+                mHal.setListener(this);
+            }
+            // Set the new rate
+            if (rate > mHal.getSampleRate(propId)) {
+                mHal.subscribeProperty(propId, rate);
+            }
+        }
+
+        // Send the latest value(s) to the registering listener only
+        List<CarPropertyEvent> events = new LinkedList<CarPropertyEvent>();
+        for (int areaId : mConfigs.get(propId).getAreaIds()) {
+            CarPropertyValue value = mHal.getProperty(propId, areaId);
+            CarPropertyEvent event = new CarPropertyEvent(
+                    CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
+            events.add(event);
+        }
+        try {
+            listener.onEvent(events);
+        } catch (RemoteException ex) {
+            // If we cannot send a record, its likely the connection snapped. Let the binder
+            // death handle the situation.
+            Log.e(TAG, "onEvent calling failed: " + ex);
+        }
+    }
+
+    @Override
+    public void unregisterListener(int propId, ICarPropertyEventListener listener) {
+        if (DBG) {
+            Log.d(TAG, "unregisterListener propId=0x" + toHexString(propId));
+        }
+        ICarImpl.assertPermission(mContext, mHal.getReadPermission(propId));
+        if (listener == null) {
+            Log.e(TAG, "unregisterListener: Listener is null.");
+            throw new IllegalArgumentException("Listener is null");
+        }
+
+        IBinder listenerBinder = listener.asBinder();
+        synchronized (mLock) {
+            unregisterListenerBinderLocked(propId, listenerBinder);
+        }
+    }
+
+    private void unregisterListenerBinderLocked(int propId, IBinder listenerBinder) {
+        Client client = mClientMap.get(listenerBinder);
+        List<Client> propertyClients = mPropIdClientMap.get(propId);
+        if (mConfigs.get(propId) == null) {
+            // Do not attempt to register an invalid propId
+            Log.e(TAG, "unregisterListener: propId is not in config list:0x" + toHexString(propId));
+            return;
+        }
+        if ((client == null) || (propertyClients == null)) {
+            Log.e(TAG, "unregisterListenerBinderLocked: Listener was not previously registered.");
+        } else {
+            if (propertyClients.remove(client)) {
+                client.removeProperty(propId);
+            } else {
+                Log.e(TAG, "unregisterListenerBinderLocked: Listener was not registered for "
+                           + "propId=0x" + toHexString(propId));
+            }
+
+            if (propertyClients.isEmpty()) {
+                // Last listener for this property unsubscribed.  Clean up
+                mHal.unsubscribeProperty(propId);
+                mPropIdClientMap.remove(propId);
+                if (mPropIdClientMap.isEmpty()) {
+                    // No more properties are subscribed.  Turn off the listener.
+                    mHal.setListener(null);
+                    mListenerIsSet = false;
+                }
+            } else {
+                // Other listeners are still subscribed.  Calculate the new rate
+                float maxRate = 0;
+                for (Client c : propertyClients) {
+                    float rate = c.getRate(propId);
+                    if (rate > maxRate) {
+                        maxRate = rate;
+                    }
+                }
+                // Set the new rate
+                mHal.subscribeProperty(propId, maxRate);
+            }
+        }
+    }
+
+    /**
+     * Return the list of properties that the caller may access.
+     */
+    @Override
+    public List<CarPropertyConfig> getPropertyList() {
+        List<CarPropertyConfig> returnList = new ArrayList<CarPropertyConfig>();
+        if (mConfigs == null) {
+            // Cache the configs list to avoid subsequent binder calls
+            mConfigs = mHal.getPropertyList();
+        }
+        for (CarPropertyConfig c : mConfigs.values()) {
+            if (ICarImpl.hasPermission(mContext, mHal.getReadPermission(c.getPropertyId()))) {
+                // Only add properties the list if the process has permissions to read it
+                returnList.add(c);
+            }
+        }
+        if (DBG) {
+            Log.d(TAG, "getPropertyList returns " + returnList.size() + " configs");
+        }
+        return returnList;
+    }
+
+    @Override
+    public CarPropertyValue getProperty(int prop, int zone) {
+        if (mConfigs.get(prop) == null) {
+            // Do not attempt to register an invalid propId
+            Log.e(TAG, "getProperty: propId is not in config list:0x" + toHexString(prop));
+            return null;
+        }
+        ICarImpl.assertPermission(mContext, mHal.getReadPermission(prop));
+        return mHal.getProperty(prop, zone);
+    }
+
+    @Override
+    public void setProperty(CarPropertyValue prop) {
+        int propId = prop.getPropertyId();
+        if (mConfigs.get(propId) == null) {
+            // Do not attempt to register an invalid propId
+            Log.e(TAG, "setProperty:  propId is not in config list:0x" + toHexString(propId));
+            return;
+        }
+        ICarImpl.assertPermission(mContext, mHal.getWritePermission(propId));
+        mHal.setProperty(prop);
+    }
+
+    // Implement PropertyHalListener interface
+    @Override
+    public void onPropertyChange(List<CarPropertyEvent> events) {
+        Map<IBinder, Pair<ICarPropertyEventListener, List<CarPropertyEvent>>> eventsToDispatch =
+                new HashMap<>();
+
+        for (CarPropertyEvent event : events) {
+            int propId = event.getCarPropertyValue().getPropertyId();
+            List<Client> clients = mPropIdClientMap.get(propId);
+            if (clients == null) {
+                Log.e(TAG, "onPropertyChange: no listener registered for propId=0x"
+                        + toHexString(propId));
+                continue;
+            }
+
+            for (Client c : clients) {
+                IBinder listenerBinder = c.getListenerBinder();
+                Pair<ICarPropertyEventListener, List<CarPropertyEvent>> p =
+                        eventsToDispatch.get(listenerBinder);
+                if (p == null) {
+                    // Initialize the linked list for the listener
+                    p = new Pair<>(c.getListener(), new LinkedList<CarPropertyEvent>());
+                    eventsToDispatch.put(listenerBinder, p);
+                }
+                p.second.add(event);
+            }
+        }
+        // Parse the dispatch list to send events
+        for (Pair<ICarPropertyEventListener, List<CarPropertyEvent>> p: eventsToDispatch.values()) {
+            try {
+                p.first.onEvent(p.second);
+            } catch (RemoteException ex) {
+                // If we cannot send a record, its likely the connection snapped. Let binder
+                // death handle the situation.
+                Log.e(TAG, "onEvent calling failed: " + ex);
+            }
+        }
+    }
+
+    @Override
+    public void onPropertySetError(int property, int area) {
+        List<Client> clients = mPropIdClientMap.get(property);
+        if (clients != null) {
+            List<CarPropertyEvent> eventList = new LinkedList<>();
+            eventList.add(createErrorEvent(property, area));
+            for (Client c : clients) {
+                try {
+                    c.getListener().onEvent(eventList);
+                } catch (RemoteException ex) {
+                    // If we cannot send a record, its likely the connection snapped. Let the binder
+                    // death handle the situation.
+                    Log.e(TAG, "onEvent calling failed: " + ex);
+                }
+            }
+        } else {
+            Log.e(TAG, "onPropertySetError called with no listener registered for propId=0x"
+                    + toHexString(property));
+        }
+    }
+
+    private static CarPropertyEvent createErrorEvent(int property, int area) {
+        return new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_ERROR,
+                new CarPropertyValue<>(property, area, null));
+    }
+}
diff --git a/service/src/com/android/car/CarPropertyServiceBase.java b/service/src/com/android/car/CarPropertyServiceBase.java
deleted file mode 100644
index 02ab7c9..0000000
--- a/service/src/com/android/car/CarPropertyServiceBase.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.car.Car;
-import android.car.hardware.CarPropertyConfig;
-import android.car.hardware.CarPropertyValue;
-import android.car.hardware.property.CarPropertyEvent;
-import android.car.hardware.property.ICarProperty;
-import android.car.hardware.property.ICarPropertyEventListener;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.car.hal.PropertyHalServiceBase;
-
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * This class implements the binder interface for ICarProperty.aidl to make it easier to create
- * multiple managers that deal with Vehicle Properties. To create a new service, simply extend
- * this class and call the super() constructor with the appropriate arguments for the new service.
- * {@link CarHvacService} shows the basic usage.
- */
-public class CarPropertyServiceBase extends ICarProperty.Stub
-        implements CarServiceBase, PropertyHalServiceBase.PropertyHalListener {
-    private final Context mContext;
-    private final boolean mDbg;
-    private final Map<IBinder, PropertyDeathRecipient> mDeathRecipientMap =
-            new ConcurrentHashMap<>();
-    private final PropertyHalServiceBase mHal;
-    private final Map<IBinder, ICarPropertyEventListener> mListenersMap = new ConcurrentHashMap<>();
-    private final String mPermission;
-    private final String mTag;
-
-    private final Object mLock = new Object();
-
-    public CarPropertyServiceBase(Context context, PropertyHalServiceBase hal, String permission,
-            boolean dbg, String tag) {
-        mContext = context;
-        mHal = hal;
-        mPermission = permission;
-        mDbg = dbg;
-        mTag = tag + ".service";
-    }
-
-    class PropertyDeathRecipient implements IBinder.DeathRecipient {
-        private IBinder mListenerBinder;
-
-        PropertyDeathRecipient(IBinder listenerBinder) {
-            mListenerBinder = listenerBinder;
-        }
-
-        /**
-         * Client died. Remove the listener from HAL service and unregister if this is the last
-         * client.
-         */
-        @Override
-        public void binderDied() {
-            if (mDbg) {
-                Log.d(mTag, "binderDied " + mListenerBinder);
-            }
-            CarPropertyServiceBase.this.unregisterListenerLocked(mListenerBinder);
-        }
-
-        void release() {
-            mListenerBinder.unlinkToDeath(this, 0);
-        }
-    }
-
-    @Override
-    public void init() {
-    }
-
-    @Override
-    public void release() {
-        for (PropertyDeathRecipient recipient : mDeathRecipientMap.values()) {
-            recipient.release();
-        }
-        mDeathRecipientMap.clear();
-        mListenersMap.clear();
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-    }
-
-    @Override
-    public void registerListener(ICarPropertyEventListener listener) {
-        if (mDbg) {
-            Log.d(mTag, "registerListener");
-        }
-        ICarImpl.assertPermission(mContext, mPermission);
-        if (listener == null) {
-            Log.e(mTag, "registerListener: Listener is null.");
-            throw new IllegalArgumentException("listener cannot be null.");
-        }
-
-        IBinder listenerBinder = listener.asBinder();
-
-        synchronized (mLock) {
-            if (mListenersMap.containsKey(listenerBinder)) {
-                // Already registered, nothing to do.
-                return;
-            }
-
-            PropertyDeathRecipient deathRecipient = new PropertyDeathRecipient(listenerBinder);
-            try {
-                listenerBinder.linkToDeath(deathRecipient, 0);
-            } catch (RemoteException e) {
-                Log.e(mTag, "Failed to link death for recipient. " + e);
-                throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
-            }
-            mDeathRecipientMap.put(listenerBinder, deathRecipient);
-
-            if (mListenersMap.isEmpty()) {
-                mHal.setListener(this);
-            }
-
-            mListenersMap.put(listenerBinder, listener);
-        }
-    }
-
-    @Override
-    public void unregisterListener(ICarPropertyEventListener listener) {
-        if (mDbg) {
-            Log.d(mTag, "unregisterListener");
-        }
-        ICarImpl.assertPermission(mContext, mPermission);
-        if (listener == null) {
-            Log.e(mTag, "unregisterListener: Listener is null.");
-            throw new IllegalArgumentException("Listener is null");
-        }
-
-        IBinder listenerBinder = listener.asBinder();
-        synchronized (mLock) {
-            if (!mListenersMap.containsKey(listenerBinder)) {
-                Log.e(mTag, "unregisterListener: Listener was not previously registered.");
-            }
-            unregisterListenerLocked(listenerBinder);
-        }
-    }
-
-    // Removes the listenerBinder from the current state.
-    // The function assumes that binder will exist both in listeners and death recipients list.
-    private void unregisterListenerLocked(IBinder listenerBinder) {
-        boolean found = mListenersMap.remove(listenerBinder) != null;
-
-        if (found) {
-            mDeathRecipientMap.get(listenerBinder).release();
-            mDeathRecipientMap.remove(listenerBinder);
-        }
-
-        if (mListenersMap.isEmpty()) {
-            mHal.setListener(null);
-        }
-    }
-
-    @Override
-    public List<CarPropertyConfig> getPropertyList() {
-        ICarImpl.assertPermission(mContext, mPermission);
-        return mHal.getPropertyList();
-    }
-
-    @Override
-    public CarPropertyValue getProperty(int prop, int zone) {
-        ICarImpl.assertPermission(mContext, mPermission);
-        return mHal.getProperty(prop, zone);
-    }
-
-    @Override
-    public void setProperty(CarPropertyValue prop) {
-        ICarImpl.assertPermission(mContext, mPermission);
-        mHal.setProperty(prop);
-    }
-
-    private ICarPropertyEventListener[] getListeners() {
-        synchronized (mLock) {
-            int size = mListenersMap.values().size();
-            return mListenersMap.values().toArray(new ICarPropertyEventListener[size]);
-        }
-    }
-
-    // Implement PropertyHalListener interface
-    @Override
-    public void onPropertyChange(CarPropertyEvent event) {
-        for (ICarPropertyEventListener listener : getListeners()) {
-            try {
-                listener.onEvent(event);
-            } catch (RemoteException ex) {
-                // If we could not send a record, its likely the connection snapped. Let the binder
-                // death handle the situation.
-                Log.e(mTag, "onEvent calling failed: " + ex);
-            }
-        }
-    }
-
-    @Override
-    public void onPropertySetError(int property, int area) {
-        for (ICarPropertyEventListener listener : getListeners()) {
-            try {
-                listener.onEvent(createErrorEvent(property, area));
-            } catch (RemoteException ex) {
-                // If we could not send a record, its likely the connection snapped. Let the binder
-                // death handle the situation.
-                Log.e(mTag, "onEvent calling failed: " + ex);
-            }
-        }
-    }
-
-    private static CarPropertyEvent createErrorEvent(int property, int area) {
-        return new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_ERROR,
-                new CarPropertyValue<>(property, area, null));
-    }
-}
diff --git a/service/src/com/android/car/CarSensorService.java b/service/src/com/android/car/CarSensorService.java
deleted file mode 100644
index 4b0227e..0000000
--- a/service/src/com/android/car/CarSensorService.java
+++ /dev/null
@@ -1,844 +0,0 @@
-/*
- * Copyright (C) 2015 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.android.car.Listeners.ClientWithRate;
-
-import android.car.Car;
-import android.car.hardware.CarSensorConfig;
-import android.car.hardware.CarSensorEvent;
-import android.car.hardware.CarSensorManager;
-import android.car.hardware.ICarSensor;
-import android.car.hardware.ICarSensorEventListener;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import com.android.car.hal.SensorHalService;
-import com.android.internal.annotations.GuardedBy;
-
-import com.google.android.collect.Lists;
-
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.ConcurrentModificationException;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.ReentrantLock;
-
-
-public class CarSensorService extends ICarSensor.Stub
-        implements CarServiceBase, SensorHalService.SensorListener {
-
-    /**
-     * When set, sensor service sets its own dispatching rate limit.
-     * VehicleNetworkService is already doing this, so not necessary to set it for now.
-     */
-    private static final boolean ENABLE_DISPATCHING_LIMIT = false;
-
-    /** {@link #mSensorLock} is not waited forever for handling disconnection */
-    private static final long MAX_SENSOR_LOCK_WAIT_MS = 1000;
-
-    /** lock to access sensor structures */
-    private final ReentrantLock mSensorLock = new ReentrantLock();
-    /** hold clients callback  */
-    @GuardedBy("mSensorLock")
-    private final LinkedList<SensorClient> mClients = new LinkedList<>();
-
-    /** key: sensor type. */
-    @GuardedBy("mSensorLock")
-    private final SparseArray<Listeners<SensorClient>> mSensorListeners = new SparseArray<>();
-    /** key: sensor type. */
-    @GuardedBy("mSensorLock")
-    private final SparseArray<SensorRecord> mSensorRecords = new SparseArray<>();
-
-    private final SensorHalService mSensorHal;
-    private int[] mCarProvidedSensors;
-    private int[] mSupportedSensors;
-    private final AtomicBoolean mSensorDiscovered = new AtomicBoolean(false);
-
-    private final Context mContext;
-
-    private final HandlerThread mHandlerThread;
-    private final SensorDispatchHandler mSensorDispatchHandler;
-
-    public CarSensorService(Context context, SensorHalService sensorHal) {
-        mContext = context;
-        if (ENABLE_DISPATCHING_LIMIT) {
-            mHandlerThread = new HandlerThread("SENSOR", Process.THREAD_PRIORITY_AUDIO);
-            mHandlerThread.start();
-            mSensorDispatchHandler = new SensorDispatchHandler(mHandlerThread.getLooper());
-        } else {
-            mHandlerThread = null;
-            mSensorDispatchHandler = null;
-        }
-        // This triggers sensor hal init as well.
-        mSensorHal = sensorHal;
-    }
-
-    @Override
-    public void init() {
-        mSensorLock.lock();
-        try {
-            mSensorHal.registerSensorListener(this);
-            mCarProvidedSensors = mSensorHal.getSupportedSensors();
-            mSupportedSensors = refreshSupportedSensorsLocked();
-
-            addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_NIGHT, getInitialNightMode());
-            addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_IGNITION_STATE,
-                getInitialIgnitionState());
-        } finally {
-            mSensorLock.unlock();
-        }
-    }
-
-    private CarSensorEvent getInitialIgnitionState() {
-        return mSensorHal.getCurrentSensorValue(CarSensorManager.SENSOR_TYPE_IGNITION_STATE);
-    }
-
-    private CarSensorEvent getInitialNightMode() {
-        CarSensorEvent event = mSensorHal.getCurrentSensorValue(CarSensorManager.SENSOR_TYPE_NIGHT);
-        if (event == null) {
-            Log.e(CarLog.TAG_SENSOR, "Daynight sensor not ready!");
-
-            // Create a place holder event that puts us in NIGHT mode at startup if we failed
-            // to get the actual VHAL value.
-            event = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_NIGHT, 0, 0, 1, 0);
-            event.intValues[0] = 1; // 1 means night mode!
-        }
-        Log.i(CarLog.TAG_SENSOR, "initial daynight: " +
-            (event.intValues[0] == 1 ? "Night" : "Day"));
-
-        return event;
-    }
-
-    @GuardedBy("mSensorLock")
-    private void addNewSensorRecordLocked(int type, CarSensorEvent event) {
-        SensorRecord record = new SensorRecord();
-        record.lastEvent = event;
-        mSensorRecords.put(type,record);
-    }
-
-    @Override
-    public void release() {
-        if (mHandlerThread != null) {
-            mHandlerThread.quit();
-        }
-        tryHoldSensorLock();
-        try {
-            for (int i = mSensorListeners.size() - 1; i >= 0; --i) {
-                Listeners listener = mSensorListeners.valueAt(i);
-                listener.release();
-            }
-            mSensorListeners.clear();
-            mSensorRecords.clear();
-            mClients.clear();
-        } finally {
-            releaseSensorLockSafely();
-        }
-    }
-
-    private void tryHoldSensorLock() {
-        try {
-            mSensorLock.tryLock(MAX_SENSOR_LOCK_WAIT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            //ignore
-        }
-    }
-
-    private void releaseSensorLockSafely() {
-        if (mSensorLock.isHeldByCurrentThread()) {
-            mSensorLock.unlock();
-        }
-    }
-
-    private void processSensorData(List<CarSensorEvent> events) {
-        ArrayMap<SensorClient, List<CarSensorEvent>> eventsByClient = new ArrayMap<>();
-
-        mSensorLock.lock();
-        for (CarSensorEvent event: events) {
-            SensorRecord record = mSensorRecords.get(event.sensorType);
-            if (record != null) {
-                if (record.lastEvent == null) {
-                    record.lastEvent = event;
-                } else if (record.lastEvent.timestamp < event.timestamp) {
-                    record.lastEvent = event;
-                    //TODO recycle event, bug: 32094595
-                } else { // wrong timestamp, throw away this.
-                    //TODO recycle new event, bug: 32094595
-                    continue;
-                }
-
-                Listeners<SensorClient> listeners = mSensorListeners.get(event.sensorType);
-                if (listeners == null) {
-                    continue;
-                }
-
-                for (ClientWithRate<SensorClient> clientWithRate : listeners.getClients()) {
-                    SensorClient client = clientWithRate.getClient();
-                    List<CarSensorEvent> clientEvents = eventsByClient.get(client);
-                    if (clientEvents == null) {
-                        clientEvents = new LinkedList<>();
-                        eventsByClient.put(client, clientEvents);
-                    }
-                    clientEvents.add(event);
-                }
-            }
-        }
-        mSensorLock.unlock();
-
-        for (ArrayMap.Entry<SensorClient, List<CarSensorEvent>> entry : eventsByClient.entrySet()) {
-            SensorClient client = entry.getKey();
-            List<CarSensorEvent> clientEvents = entry.getValue();
-
-            client.dispatchSensorUpdate(clientEvents);
-        }
-    }
-
-    /**
-     * Received sensor data from car.
-     */
-    @Override
-    public void onSensorEvents(List<CarSensorEvent> events) {
-        if (ENABLE_DISPATCHING_LIMIT) {
-            mSensorDispatchHandler.handleSensorEvents(events);
-        } else {
-            processSensorData(events);
-        }
-    }
-
-    @Override
-    public int[] getSupportedSensors() {
-        mSensorLock.lock();
-        int[] supportedSensors = mSupportedSensors;
-        mSensorLock.unlock();
-        return supportedSensors;
-    }
-
-    @Override
-    public boolean registerOrUpdateSensorListener(int sensorType, int rate,
-            ICarSensorEventListener listener) {
-        boolean shouldStartSensors = false;
-        SensorRecord sensorRecord = null;
-        SensorClient sensorClient = null;
-        Integer oldRate = null;
-        Listeners<SensorClient> sensorListeners = null;
-        mSensorLock.lock();
-        try {
-            sensorRecord = mSensorRecords.get(sensorType);
-            if (sensorRecord == null) {
-                if (Log.isLoggable(CarLog.TAG_SENSOR, Log.INFO)) {
-                    Log.i(CarLog.TAG_SENSOR, "Requested sensor " + sensorType + " not supported");
-                }
-                return false;
-            }
-            if (Binder.getCallingUid() != Process.myUid()) {
-                switch (getSensorPermission(sensorType)) {
-                    case PackageManager.PERMISSION_DENIED:
-                        throw new SecurityException("client does not have permission:"
-                                + getPermissionName(sensorType)
-                                + " pid:" + Binder.getCallingPid()
-                                + " uid:" + Binder.getCallingUid());
-                    case PackageManager.PERMISSION_GRANTED:
-                        break;
-                }
-            }
-            if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                Log.d(CarLog.TAG_SENSOR, "registerOrUpdateSensorListener " + sensorType + " " +
-                        listener);
-            }
-            sensorClient = findSensorClientLocked(listener);
-            ClientWithRate<SensorClient> sensorClientWithRate = null;
-            sensorListeners = mSensorListeners.get(sensorType);
-            if (sensorClient == null) {
-                sensorClient = new SensorClient(listener);
-                try {
-                    listener.asBinder().linkToDeath(sensorClient, 0);
-                } catch (RemoteException e) {
-                    if (Log.isLoggable(CarLog.TAG_SENSOR, Log.INFO)) {
-                        Log.i(CarLog.TAG_SENSOR, "Adding listener failed.");
-                    }
-                    return false;
-                }
-                mClients.add(sensorClient);
-            }
-            // If we have a cached event for this sensor, send the event.
-            SensorRecord record = mSensorRecords.get(sensorType);
-            if (record != null && record.lastEvent != null) {
-                sensorClient.dispatchSensorUpdate(Lists.newArrayList(record.lastEvent));
-            }
-            if (sensorListeners == null) {
-                sensorListeners = new Listeners<>(rate);
-                mSensorListeners.put(sensorType, sensorListeners);
-                shouldStartSensors = true;
-            } else {
-                oldRate = sensorListeners.getRate();
-                sensorClientWithRate = sensorListeners.findClientWithRate(sensorClient);
-            }
-            if (sensorClientWithRate == null) {
-                sensorClientWithRate = new ClientWithRate<>(sensorClient, rate);
-                sensorListeners.addClientWithRate(sensorClientWithRate);
-            } else {
-                sensorClientWithRate.setRate(rate);
-            }
-            if (sensorListeners.getRate() > rate) {
-                sensorListeners.setRate(rate);
-                shouldStartSensors = sensorSupportRate(sensorType);
-            }
-            sensorClient.addSensor(sensorType);
-        } finally {
-            mSensorLock.unlock();
-        }
-        // start sensor outside lock as it can take time.
-        if (shouldStartSensors) {
-            if (!startSensor(sensorRecord, sensorType, rate)) {
-                // failed. so remove from active sensor list.
-                mSensorLock.lock();
-                try {
-                    sensorClient.removeSensor(sensorType);
-                    if (oldRate != null) {
-                        sensorListeners.setRate(oldRate);
-                    } else {
-                        mSensorListeners.remove(sensorType);
-                    }
-                } finally {
-                    mSensorLock.unlock();
-                }
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private boolean sensorSupportRate(int sensorType) {
-        switch (sensorType) {
-            case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
-            case CarSensorManager.SENSOR_TYPE_RPM:
-            case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
-                return true;
-            case CarSensorManager.SENSOR_TYPE_ODOMETER:
-            case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
-            case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
-            case CarSensorManager.SENSOR_TYPE_GEAR:
-            case CarSensorManager.SENSOR_TYPE_NIGHT:
-            case CarSensorManager.SENSOR_TYPE_ENVIRONMENT:
-                return false;
-            default:
-                Log.w(CarLog.TAG_SENSOR, "sensorSupportRate not listed sensor:" + sensorType);
-                return false;
-        }
-    }
-
-    private int getSensorPermission(int sensorType) {
-        String permission = getPermissionName(sensorType);
-        int result = PackageManager.PERMISSION_GRANTED;
-        if (permission != null) {
-            return mContext.checkCallingOrSelfPermission(permission);
-        }
-        // If no permission is required, return granted.
-        return result;
-    }
-
-    //TODO handle per property OEM permission. bug: 32094983
-    private String getPermissionName(int sensorType) {
-        if ((sensorType >= CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_START) &&
-                (sensorType <= CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_END)) {
-            return Car.PERMISSION_VENDOR_EXTENSION;
-        }
-        String permission = null;
-        switch (sensorType) {
-            case CarSensorManager.SENSOR_TYPE_ENGINE_OIL_LEVEL:
-            case CarSensorManager.SENSOR_TYPE_RPM:
-                permission = Car.PERMISSION_CAR_ENGINE_DETAILED;
-                break;
-            case CarSensorManager.SENSOR_TYPE_FUEL_DOOR_OPEN:
-            case CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_OPEN:
-            case CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED:
-                permission = Car.PERMISSION_ENERGY_PORTS;
-                break;
-            case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
-            case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
-                permission = Car.PERMISSION_SPEED;
-                break;
-            case CarSensorManager.SENSOR_TYPE_ODOMETER:
-                permission = Car.PERMISSION_MILEAGE;
-                break;
-            case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
-            case CarSensorManager.SENSOR_TYPE_EV_BATTERY_LEVEL:
-            case CarSensorManager.SENSOR_TYPE_EV_BATTERY_CHARGE_RATE:
-                permission = Car.PERMISSION_ENERGY;
-                break;
-            case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE:
-            case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE:
-                permission = Car.PERMISSION_CAR_DYNAMICS_STATE;
-                break;
-            default:
-                break;
-        }
-        return permission;
-    }
-
-    private boolean startSensor(SensorRecord record, int sensorType, int rate) {
-        //TODO handle sensor rate properly. bug: 32095903
-        //Some sensors which report only when there is change should be always set with maximum
-        //rate. For now, set every sensor to the maximum.
-        if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
-            Log.v(CarLog.TAG_SENSOR, "startSensor " + sensorType + " with rate " + rate);
-        }
-        if (mSensorHal != null) {
-            if (!mSensorHal.isReady()) {
-                Log.w(CarLog.TAG_SENSOR, "Sensor channel not available.");
-                return false;
-            }
-            if (record.enabled) {
-                return true;
-            }
-            if (mSensorHal.requestSensorStart(sensorType, 0)) {
-                record.enabled = true;
-                return true;
-            }
-        }
-        Log.w(CarLog.TAG_SENSOR, "requestSensorStart failed, sensor type:" + sensorType);
-        return false;
-    }
-
-    @Override
-    public void unregisterSensorListener(int sensorType, ICarSensorEventListener listener) {
-        boolean shouldStopSensor = false;
-        boolean shouldRestartSensor = false;
-        SensorRecord record = null;
-        int newRate = 0;
-        mSensorLock.lock();
-        try {
-            record = mSensorRecords.get(sensorType);
-            if (record == null) {
-                // unregister not supported sensor. ignore.
-                if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                    Log.d(CarLog.TAG_SENSOR, "unregister for unsupported sensor");
-                }
-                return;
-            }
-            SensorClient sensorClient = findSensorClientLocked(listener);
-            if (sensorClient == null) {
-                // never registered or already unregistered.
-                if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                    Log.d(CarLog.TAG_SENSOR, "unregister for not existing client");
-                }
-                return;
-            }
-            sensorClient.removeSensor(sensorType);
-            if (sensorClient.getNumberOfActiveSensor() == 0) {
-                sensorClient.release();
-                mClients.remove(sensorClient);
-            }
-            Listeners<SensorClient> sensorListeners = mSensorListeners.get(sensorType);
-            if (sensorListeners == null) {
-                // sensor not active
-                if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                    Log.d(CarLog.TAG_SENSOR, "unregister for non-active sensor");
-                }
-                return;
-            }
-            ClientWithRate<SensorClient> clientWithRate =
-                    sensorListeners.findClientWithRate(sensorClient);
-            if (clientWithRate == null) {
-                if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                    Log.d(CarLog.TAG_SENSOR, "unregister for not registered sensor");
-                }
-                return;
-            }
-            sensorListeners.removeClientWithRate(clientWithRate);
-            if (sensorListeners.getNumberOfClients() == 0) {
-                shouldStopSensor = true;
-                mSensorListeners.remove(sensorType);
-            } else if (sensorListeners.updateRate()) { // rate changed
-                newRate = sensorListeners.getRate();
-                shouldRestartSensor = sensorSupportRate(sensorType);
-            }
-            if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                Log.d(CarLog.TAG_SENSOR, "unregister succeeded");
-            }
-        } finally {
-            mSensorLock.unlock();
-        }
-        if (shouldStopSensor) {
-            stopSensor(record, sensorType);
-        } else if (shouldRestartSensor) {
-            startSensor(record, sensorType, newRate);
-        }
-    }
-
-    @Override
-    public CarSensorConfig getSensorConfig(int sensorType) {
-        if (Binder.getCallingUid() != Process.myUid()) {
-            switch (getSensorPermission(sensorType)) {
-                case PackageManager.PERMISSION_DENIED:
-                    throw new SecurityException("client does not have permission:"
-                        + getPermissionName(sensorType)
-                        + " pid:" + Binder.getCallingPid()
-                        + " uid:" + Binder.getCallingUid());
-                case PackageManager.PERMISSION_GRANTED:
-                    break;
-            }
-        }
-        return(mSensorHal.getSensorConfig(sensorType));
-    }
-
-    private void stopSensor(SensorRecord record, int sensorType) {
-        if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-            Log.d(CarLog.TAG_SENSOR, "stopSensor " + sensorType);
-        }
-        if (mSensorHal == null || !mSensorHal.isReady()) {
-            Log.w(CarLog.TAG_SENSOR, "Sensor channel not available.");
-            return;
-        }
-        if (!record.enabled) {
-            return;
-        }
-        record.enabled = false;
-        // make lastEvent invalid as old data can be sent to client when subscription is restarted
-        // later.
-        record.lastEvent = null;
-        if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-            Log.d(CarLog.TAG_SENSOR, "stopSensor requestStop " + sensorType);
-        }
-        mSensorHal.requestSensorStop(sensorType);
-    }
-
-    @Override
-    public CarSensorEvent getLatestSensorEvent(int sensorType) {
-        SensorRecord record = null;
-        mSensorLock.lock();
-        try {
-            record = mSensorRecords.get(sensorType);
-        } finally {
-            mSensorLock.unlock();
-        }
-        if (record != null) {
-            return record.lastEvent;
-        }
-        return null;
-    }
-
-    @GuardedBy("mSensorLock")
-    private int[] refreshSupportedSensorsLocked() {
-        int numCarSensors = (mCarProvidedSensors == null) ? 0 : mCarProvidedSensors.length;
-
-        int[] supportedSensors = new int[numCarSensors];
-        int index = 0;
-
-        for (int i = 0; i < numCarSensors; i++) {
-            int sensor = mCarProvidedSensors[i];
-
-            if (mSensorRecords.get(sensor) == null) {
-                SensorRecord record = new SensorRecord();
-                mSensorRecords.put(sensor, record);
-            }
-            supportedSensors[index] = sensor;
-            index++;
-        }
-
-        return supportedSensors;
-    }
-
-    private boolean isSensorRealLocked(int sensorType) {
-        if (mCarProvidedSensors != null) {
-            for (int sensor : mCarProvidedSensors) {
-                if (sensor == sensorType ) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Find SensorClient from client list and return it.
-     * This should be called with mClients locked.
-     * @param listener
-     * @return null if not found.
-     */
-    @GuardedBy("mSensorLock")
-    private SensorClient findSensorClientLocked(ICarSensorEventListener listener) {
-        IBinder binder = listener.asBinder();
-        for (SensorClient sensorClient : mClients) {
-            if (sensorClient.isHoldingListenerBinder(binder)) {
-                return sensorClient;
-            }
-        }
-        return null;
-    }
-
-    private void removeClient(SensorClient sensorClient) {
-        mSensorLock.lock();
-        try {
-            for (int sensor: sensorClient.getSensorArray()) {
-                unregisterSensorListener(sensor,
-                        sensorClient.getICarSensorEventListener());
-            }
-            mClients.remove(sensorClient);
-        } finally {
-            mSensorLock.unlock();
-        }
-    }
-
-    private class SensorDispatchHandler extends Handler {
-        private static final long SENSOR_DISPATCH_MIN_INTERVAL_MS = 16; // over 60Hz
-
-        private static final int MSG_SENSOR_DATA = 0;
-
-        private long mLastSensorDispatchTime = -1;
-        private int mFreeListIndex = 0;
-        private final LinkedList<CarSensorEvent>[] mSensorDataList = new LinkedList[2];
-
-        private SensorDispatchHandler(Looper looper) {
-            super(looper);
-            for (int i = 0; i < mSensorDataList.length; i++) {
-                mSensorDataList[i] = new LinkedList<CarSensorEvent>();
-            }
-        }
-
-        private synchronized void handleSensorEvents(List<CarSensorEvent> data) {
-            LinkedList<CarSensorEvent> list = mSensorDataList[mFreeListIndex];
-            list.addAll(data);
-            requestDispatchLocked();
-        }
-
-        private synchronized void handleSensorEvent(CarSensorEvent event) {
-            LinkedList<CarSensorEvent> list = mSensorDataList[mFreeListIndex];
-            list.add(event);
-            requestDispatchLocked();
-        }
-
-        private void requestDispatchLocked() {
-            Message msg = obtainMessage(MSG_SENSOR_DATA);
-            long now = SystemClock.uptimeMillis();
-            long delta = now - mLastSensorDispatchTime;
-            if (delta > SENSOR_DISPATCH_MIN_INTERVAL_MS) {
-                sendMessage(msg);
-            } else {
-                sendMessageDelayed(msg, SENSOR_DISPATCH_MIN_INTERVAL_MS - delta);
-            }
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SENSOR_DATA:
-                    doHandleSensorData();
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        private void doHandleSensorData() {
-            List<CarSensorEvent> listToDispatch = null;
-            synchronized (this) {
-                mLastSensorDispatchTime = SystemClock.uptimeMillis();
-                int nonFreeListIndex = mFreeListIndex ^ 0x1;
-                List<CarSensorEvent> nonFreeList = mSensorDataList[nonFreeListIndex];
-                List<CarSensorEvent> freeList = mSensorDataList[mFreeListIndex];
-                if (nonFreeList.size() > 0) {
-                    Log.w(CarLog.TAG_SENSOR, "non free list not empty");
-                    // copy again, but this should not be normal case
-                    nonFreeList.addAll(freeList);
-                    listToDispatch = nonFreeList;
-                    freeList.clear();
-                } else if (freeList.size() > 0) {
-                    listToDispatch = freeList;
-                    mFreeListIndex = nonFreeListIndex;
-                }
-            }
-            // leave this part outside lock so that time-taking dispatching can be done without
-            // blocking sensor event notification.
-            if (listToDispatch != null) {
-                processSensorData(listToDispatch);
-                listToDispatch.clear();
-            }
-        }
-
-    }
-
-    /** internal instance for pending client request */
-    private class SensorClient implements Listeners.IListener {
-        /** callback for sensor events */
-        private final ICarSensorEventListener mListener;
-        private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
-
-        /** when false, it is already released */
-        private volatile boolean mActive = true;
-
-        SensorClient(ICarSensorEventListener listener) {
-            this.mListener = listener;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof SensorClient &&
-                    mListener.asBinder() == ((SensorClient) o).mListener.asBinder()) {
-                return true;
-            }
-            return false;
-        }
-
-        boolean isHoldingListenerBinder(IBinder listenerBinder) {
-            return mListener.asBinder() == listenerBinder;
-        }
-
-        void addSensor(int sensor) {
-            mActiveSensors.put(sensor, true);
-        }
-
-        void removeSensor(int sensor) {
-            mActiveSensors.delete(sensor);
-        }
-
-        int getNumberOfActiveSensor() {
-            return mActiveSensors.size();
-        }
-
-        int[] getSensorArray() {
-            int[] sensors = new int[mActiveSensors.size()];
-            for (int i = sensors.length - 1; i >= 0; --i) {
-                sensors[i] = mActiveSensors.keyAt(i);
-            }
-            return sensors;
-        }
-
-        ICarSensorEventListener getICarSensorEventListener() {
-            return mListener;
-        }
-
-        /**
-         * Client dead. should remove all sensor requests from client
-         */
-        @Override
-        public void binderDied() {
-            mListener.asBinder().unlinkToDeath(this, 0);
-            removeClient(this);
-        }
-
-        void dispatchSensorUpdate(List<CarSensorEvent> events) {
-            if (events.size() == 0) {
-                return;
-            }
-            if (mActive) {
-                try {
-                    mListener.onSensorChanged(events);
-                } catch (RemoteException e) {
-                    //ignore. crash will be handled by death handler
-                }
-            } else {
-                if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                    Log.d(CarLog.TAG_SENSOR, "sensor update while client is already released");
-                }
-            }
-        }
-
-        @Override
-        public void release() {
-            if (mActive) {
-                mListener.asBinder().unlinkToDeath(this, 0);
-                mActiveSensors.clear();
-                mActive = false;
-            }
-        }
-    }
-
-    private static class SensorRecord {
-        /** Record the lastly received sensor event */
-        CarSensorEvent lastEvent = null;
-        /** sensor was enabled by at least one client */
-        boolean enabled = false;
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-        writer.println("*CarSensorService*");
-        writer.println("supported sensors:" + Arrays.toString(mSupportedSensors));
-        writer.println("**last events for sensors**");
-        if (mSensorRecords != null) {
-            try {
-                int sensorRecordSize = mSensorRecords.size();
-                for (int i = 0; i < sensorRecordSize; i++) {
-                    int sensor = mSensorRecords.keyAt(i);
-                    SensorRecord record = mSensorRecords.get(sensor);
-                    if (record != null && record.lastEvent != null) {
-                        writer.println("sensor: " + sensor
-                                + " active: " + record.enabled);
-                        writer.println(" " + record.lastEvent.toString());
-                    }
-                    Listeners listeners = mSensorListeners.get(sensor);
-                    if (listeners != null) {
-                        writer.println(" rate: " + listeners.getRate());
-                    }
-                }
-            } catch (ConcurrentModificationException e) {
-                writer.println("concurrent modification happened");
-            }
-        } else {
-            writer.println("null records");
-        }
-        writer.println("**clients**");
-        try {
-            for (SensorClient client: mClients) {
-                if (client != null) {
-                    try {
-                        writer.println("binder:" + client.mListener
-                                + " active sensors:" + Arrays.toString(client.getSensorArray()));
-                    } catch (ConcurrentModificationException e) {
-                        writer.println("concurrent modification happened");
-                    }
-                } else {
-                    writer.println("null client");
-                }
-            }
-        } catch  (ConcurrentModificationException e) {
-            writer.println("concurrent modification happened");
-        }
-        writer.println("**sensor listeners**");
-        try {
-            int sensorListenerSize = mSensorListeners.size();
-            for (int i = 0; i < sensorListenerSize; i++) {
-                int sensor = mSensorListeners.keyAt(i);
-                Listeners sensorListeners = mSensorListeners.get(sensor);
-                if (sensorListeners != null) {
-                    writer.println(" Sensor:" + sensor
-                            + " num client:" + sensorListeners.getNumberOfClients()
-                            + " rate:" + sensorListeners.getRate());
-                }
-            }
-        }  catch  (ConcurrentModificationException e) {
-            writer.println("concurrent modification happened");
-        }
-    }
-}
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index 289706d..23c4d32 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -23,10 +23,11 @@
 import android.car.drivingstate.ICarDrivingStateChangeListener;
 import android.car.drivingstate.ICarUxRestrictionsChangeListener;
 import android.car.drivingstate.ICarUxRestrictionsManager;
-import android.car.hardware.CarSensorEvent;
-import android.car.hardware.CarSensorManager;
-import android.car.hardware.ICarSensorEventListener;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarPropertyEventListener;
 import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -48,9 +49,11 @@
     private static final String TAG = "CarUxR";
     private static final boolean DBG = false;
     private static final int MAX_TRANSITION_LOG_SIZE = 20;
+    private static final int PROPERTY_UPDATE_RATE = 5; // Update rate in Hz
+    private static final float SPEED_NOT_AVAILABLE = -1.0F;
     private final Context mContext;
     private final CarDrivingStateService mDrivingStateService;
-    private final CarSensorService mCarSensorService;
+    private final CarPropertyService mCarPropertyService;
     private final CarUxRestrictionsServiceHelper mHelper;
     // List of clients listening to UX restriction events.
     private final List<UxRestrictionsClient> mUxRClients = new ArrayList<>();
@@ -62,20 +65,20 @@
 
 
     public CarUxRestrictionsManagerService(Context context, CarDrivingStateService drvService,
-            CarSensorService sensorService) {
+            CarPropertyService propertyService) {
         mContext = context;
         mDrivingStateService = drvService;
-        mCarSensorService = sensorService;
+        mCarPropertyService = propertyService;
         mHelper = new CarUxRestrictionsServiceHelper(mContext, R.xml.car_ux_restrictions_map);
         // Unrestricted until driving state information is received. During boot up, we don't want
-        // everything to be blocked until data is available from CarSensorManager.  If we start
+        // everything to be blocked until data is available from CarPropertyManager.  If we start
         // driving and we don't get speed or gear information, we have bigger problems.
         mCurrentUxRestrictions = mHelper.createUxRestrictionsEvent(false,
                 CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
     }
 
     @Override
-    public void init() {
+    public synchronized void init() {
         try {
             if (!mHelper.loadUxRestrictionsFromXml()) {
                 Log.e(TAG, "Error reading Ux Restrictions Mapping. Falling back to defaults");
@@ -88,9 +91,41 @@
         // subscribe to driving State
         mDrivingStateService.registerDrivingStateChangeListener(
                 mICarDrivingStateChangeEventListener);
-        // subscribe to Sensor service for speed
-        mCarSensorService.registerOrUpdateSensorListener(CarSensorManager.SENSOR_TYPE_CAR_SPEED,
-                CarSensorManager.SENSOR_RATE_UI, mICarSensorEventListener);
+        // subscribe to property service for speed
+        mCarPropertyService.registerListener(VehicleProperty.PERF_VEHICLE_SPEED,
+                PROPERTY_UPDATE_RATE, mICarPropertyEventListener);
+        initializeUxRestrictions();
+    }
+
+    // Update current restrictions by getting the current driving state and speed.
+    private void initializeUxRestrictions() {
+        CarDrivingStateEvent currentDrivingStateEvent =
+                mDrivingStateService.getCurrentDrivingState();
+        // if we don't have enough information from the CarPropertyService to compute the UX
+        // restrictions, then leave the UX restrictions unchanged from what it was initialized to
+        // in the constructor.
+        if (currentDrivingStateEvent == null || currentDrivingStateEvent.eventValue
+                == CarDrivingStateEvent.DRIVING_STATE_UNKNOWN) {
+            return;
+        }
+        int currentDrivingState = currentDrivingStateEvent.eventValue;
+        Float currentSpeed = getCurrentSpeed();
+        if (currentSpeed == SPEED_NOT_AVAILABLE) {
+            return;
+        }
+        // At this point the underlying CarPropertyService has provided us enough information to
+        // compute the UX restrictions that could be potentially different from the initial UX
+        // restrictions.
+        handleDispatchUxRestrictions(currentDrivingState, currentSpeed);
+    }
+
+    private Float getCurrentSpeed() {
+        CarPropertyValue value = mCarPropertyService.getProperty(VehicleProperty.PERF_VEHICLE_SPEED,
+                0);
+        if (value != null) {
+            return (Float) value.getValue();
+        }
+        return SPEED_NOT_AVAILABLE;
     }
 
     @Override
@@ -154,7 +189,6 @@
         return null;
     }
 
-
     /**
      * Unregister the given UX Restrictions listener
      *
@@ -280,10 +314,10 @@
             return;
         }
         int drivingState = event.eventValue;
-        CarSensorEvent speed = mCarSensorService.getLatestSensorEvent(
-                CarSensorManager.SENSOR_TYPE_CAR_SPEED);
-        if (speed != null) {
-            mCurrentMovingSpeed = speed.floatValues[0];
+        Float speed = getCurrentSpeed();
+
+        if (speed != SPEED_NOT_AVAILABLE) {
+            mCurrentMovingSpeed = speed;
         } else if (drivingState == CarDrivingStateEvent.DRIVING_STATE_PARKED
                 || drivingState == CarDrivingStateEvent.DRIVING_STATE_UNKNOWN) {
             // If speed is unavailable, but the driving state is parked or unknown, it can still be
@@ -303,17 +337,19 @@
     }
 
     /**
-     * {@link CarSensorEvent} listener registered with the {@link CarSensorService} for getting
+     * {@link CarPropertyEvent} listener registered with the {@link CarPropertyService} for getting
      * speed change notifications.
      */
-    private final ICarSensorEventListener mICarSensorEventListener =
-            new ICarSensorEventListener.Stub() {
+    private final ICarPropertyEventListener mICarPropertyEventListener =
+            new ICarPropertyEventListener.Stub() {
                 @Override
-                public void onSensorChanged(List<CarSensorEvent> events) {
-                    for (CarSensorEvent event : events) {
-                        if (event != null
-                                && event.sensorType == CarSensorManager.SENSOR_TYPE_CAR_SPEED) {
-                            handleSpeedChange(event.floatValues[0]);
+                public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
+                    for (CarPropertyEvent event : events) {
+                        if ((event.getEventType()
+                                == CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE)
+                                && (event.getCarPropertyValue().getPropertyId()
+                                == VehicleProperty.PERF_VEHICLE_SPEED)) {
+                            handleSpeedChange((Float) event.getCarPropertyValue().getValue());
                         }
                     }
                 }
@@ -348,7 +384,6 @@
             uxRestrictions = getDefaultRestrictions(currentDrivingState);
         } else {
             uxRestrictions = mHelper.getUxRestrictions(currentDrivingState, speed);
-
         }
 
         if (DBG) {
diff --git a/service/src/com/android/car/CarVendorExtensionService.java b/service/src/com/android/car/CarVendorExtensionService.java
deleted file mode 100644
index 2e2d074..0000000
--- a/service/src/com/android/car/CarVendorExtensionService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.car.Car;
-import android.content.Context;
-
-import com.android.car.hal.VendorExtensionHalService;
-
-/**
- * Service responsible for handling custom properties that were defined in vehicle HAL by OEMs.
- */
-public class CarVendorExtensionService extends CarPropertyServiceBase {
-
-    private final static boolean DEBUG = false;
-
-    public CarVendorExtensionService(Context context, VendorExtensionHalService vendorHal) {
-        super(context, vendorHal, Car.PERMISSION_VENDOR_EXTENSION, DEBUG, CarLog.TAG_VENDOR_EXT);
-    }
-}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index c192202..ce5ea75 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -65,21 +65,17 @@
     private final CarPowerManagementService mCarPowerManagementService;
     private final CarPackageManagerService mCarPackageManagerService;
     private final CarInputService mCarInputService;
-    private final CarSensorService mCarSensorService;
     private final CarDrivingStateService mCarDrivingStateService;
     private final CarUxRestrictionsManagerService mCarUXRestrictionsService;
-    private final CarInfoService mCarInfoService;
     private final CarAudioService mCarAudioService;
     private final CarProjectionService mCarProjectionService;
-    private final CarCabinService mCarCabinService;
-    private final CarHvacService mCarHvacService;
+    private final CarPropertyService mCarPropertyService;
     private final CarNightService mCarNightService;
     private final AppFocusService mAppFocusService;
     private final GarageModeService mGarageModeService;
     private final InstrumentClusterService mInstrumentClusterService;
     private final CarLocationService mCarLocationService;
     private final SystemStateControllerService mSystemStateControllerService;
-    private final CarVendorExtensionService mCarVendorExtensionService;
     private final CarBluetoothService mCarBluetoothService;
     private final PerUserCarServiceHelper mPerUserCarServiceHelper;
     private final CarDiagnosticService mCarDiagnosticService;
@@ -116,10 +112,10 @@
         mSystemActivityMonitoringService = new SystemActivityMonitoringService(serviceContext);
         mCarPowerManagementService = new CarPowerManagementService(mContext, mHal.getPowerHal(),
                 systemInterface);
-        mCarSensorService = new CarSensorService(serviceContext, mHal.getSensorHal());
-        mCarDrivingStateService = new CarDrivingStateService(serviceContext, mCarSensorService);
+        mCarPropertyService = new CarPropertyService(serviceContext, mHal.getPropertyHal());
+        mCarDrivingStateService = new CarDrivingStateService(serviceContext, mCarPropertyService);
         mCarUXRestrictionsService = new CarUxRestrictionsManagerService(serviceContext,
-                mCarDrivingStateService, mCarSensorService);
+                mCarDrivingStateService, mCarPropertyService);
         mCarPackageManagerService = new CarPackageManagerService(serviceContext,
                 mCarUXRestrictionsService,
                 mSystemActivityMonitoringService);
@@ -127,22 +123,17 @@
         mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService);
         mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService);
         mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService,
-                mCarSensorService);
-        mCarInfoService = new CarInfoService(serviceContext, mHal.getInfoHal());
+                mCarPropertyService);
         mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService);
         mCarAudioService = new CarAudioService(serviceContext);
-        mCarCabinService = new CarCabinService(serviceContext, mHal.getCabinHal());
-        mCarHvacService = new CarHvacService(serviceContext, mHal.getHvacHal());
-        mCarNightService = new CarNightService(serviceContext, mCarSensorService);
+        mCarNightService = new CarNightService(serviceContext, mCarPropertyService);
         mInstrumentClusterService = new InstrumentClusterService(serviceContext,
                 mAppFocusService, mCarInputService);
         mSystemStateControllerService = new SystemStateControllerService(serviceContext,
                 mCarPowerManagementService, mCarAudioService, this);
-        mCarVendorExtensionService = new CarVendorExtensionService(serviceContext,
-                mHal.getVendorExtensionHal());
         mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext);
-        mCarBluetoothService = new CarBluetoothService(serviceContext, mCarCabinService,
-                mCarSensorService, mPerUserCarServiceHelper, mCarUXRestrictionsService);
+        mCarBluetoothService = new CarBluetoothService(serviceContext, mCarPropertyService,
+                mPerUserCarServiceHelper, mCarUXRestrictionsService);
         mVmsSubscriberService = new VmsSubscriberService(serviceContext, mHal.getVmsHal());
         mVmsPublisherService = new VmsPublisherService(serviceContext, mHal.getVmsHal());
         mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal());
@@ -156,23 +147,19 @@
         List<CarServiceBase> allServices = new ArrayList<>();
         allServices.add(mSystemActivityMonitoringService);
         allServices.add(mCarPowerManagementService);
-        allServices.add(mCarSensorService);
+        allServices.add(mCarPropertyService);
         allServices.add(mCarDrivingStateService);
         allServices.add(mCarUXRestrictionsService);
         allServices.add(mCarPackageManagerService);
         allServices.add(mCarInputService);
         allServices.add(mCarLocationService);
         allServices.add(mGarageModeService);
-        allServices.add(mCarInfoService);
         allServices.add(mAppFocusService);
         allServices.add(mCarAudioService);
-        allServices.add(mCarCabinService);
-        allServices.add(mCarHvacService);
         allServices.add(mCarNightService);
         allServices.add(mInstrumentClusterService);
         allServices.add(mCarProjectionService);
         allServices.add(mSystemStateControllerService);
-        allServices.add(mCarVendorExtensionService);
         allServices.add(mCarBluetoothService);
         allServices.add(mCarDiagnosticService);
         allServices.add(mPerUserCarServiceHelper);
@@ -233,26 +220,23 @@
         switch (serviceName) {
             case Car.AUDIO_SERVICE:
                 return mCarAudioService;
-            case Car.SENSOR_SERVICE:
-                return mCarSensorService;
-            case Car.INFO_SERVICE:
-                return mCarInfoService;
             case Car.APP_FOCUS_SERVICE:
                 return mAppFocusService;
             case Car.PACKAGE_SERVICE:
                 return mCarPackageManagerService;
-            case Car.CABIN_SERVICE:
-                assertCabinPermission(mContext);
-                return mCarCabinService;
             case Car.DIAGNOSTIC_SERVICE:
                 assertAnyDiagnosticPermission(mContext);
                 return mCarDiagnosticService;
-            case Car.HVAC_SERVICE:
-                assertHvacPermission(mContext);
-                return mCarHvacService;
             case Car.POWER_SERVICE:
                 assertPowerPermission(mContext);
                 return mCarPowerManagementService;
+            case Car.CABIN_SERVICE:
+            case Car.HVAC_SERVICE:
+            case Car.INFO_SERVICE:
+            case Car.PROPERTY_SERVICE:
+            case Car.SENSOR_SERVICE:
+            case Car.VENDOR_EXTENSION_SERVICE:
+                return mCarPropertyService;
             case Car.CAR_NAVIGATION_SERVICE:
                 assertNavigationManagerPermission(mContext);
                 IInstrumentClusterNavigation navService =
@@ -264,9 +248,6 @@
             case Car.PROJECTION_SERVICE:
                 assertProjectionPermission(mContext);
                 return mCarProjectionService;
-            case Car.VENDOR_EXTENSION_SERVICE:
-                assertVendorExtensionPermission(mContext);
-                return mCarVendorExtensionService;
             case Car.VMS_SUBSCRIBER_SERVICE:
                 assertVmsSubscriberPermission(mContext);
                 return mVmsSubscriberService;
@@ -319,10 +300,6 @@
         assertPermission(context, Car.PERMISSION_MOCK_VEHICLE_HAL);
     }
 
-    public static void assertCabinPermission(Context context) {
-        assertPermission(context, Car.PERMISSION_ADJUST_CAR_CABIN);
-    }
-
     public static void assertNavigationManagerPermission(Context context) {
         assertPermission(context, Car.PERMISSION_CAR_NAVIGATION_MANAGER);
     }
@@ -331,10 +308,6 @@
         assertPermission(context, Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL);
     }
 
-    public static void assertHvacPermission(Context context) {
-        assertPermission(context, Car.PERMISSION_CONTROL_CAR_CLIMATE);
-    }
-
     public static void assertPowerPermission(Context context) {
         assertPermission(context, Car.PERMISSION_CAR_POWER);
     }
@@ -343,10 +316,6 @@
         assertPermission(context, Car.PERMISSION_CAR_PROJECTION);
     }
 
-    public static void assertVendorExtensionPermission(Context context) {
-        assertPermission(context, Car.PERMISSION_VENDOR_EXTENSION);
-    }
-
     public static void assertAnyDiagnosticPermission(Context context) {
         assertAnyPermission(context,
                 Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL,
@@ -371,6 +340,18 @@
         }
     }
 
+    /**
+     * Checks to see if the caller has a permission.
+     * @param context
+     * @param permission
+     *
+     * @return boolean TRUE if caller has the permission.
+     */
+    public static boolean hasPermission(Context context, String permission) {
+        return context.checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     public static void assertAnyPermission(Context context, String... permissions) {
         for (String permission : permissions) {
             if (context.checkCallingOrSelfPermission(permission) ==
diff --git a/service/src/com/android/car/hal/CabinHalService.java b/service/src/com/android/car/hal/CabinHalService.java
deleted file mode 100644
index 2cdf5d5..0000000
--- a/service/src/com/android/car/hal/CabinHalService.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2016 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.hal;
-
-import android.car.hardware.cabin.CarCabinManager;
-import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
-
-public class CabinHalService extends PropertyHalServiceBase {
-    private static final boolean DBG = false;
-    private static final String TAG = "CAR.CABIN.HAL";
-
-    private final ManagerToHalPropIdMap mMgrHalPropIdMap = ManagerToHalPropIdMap.create(
-            CarCabinManager.ID_DOOR_POS,
-            VehicleProperty.DOOR_POS,
-
-            CarCabinManager.ID_DOOR_MOVE,
-            VehicleProperty.DOOR_MOVE,
-
-            CarCabinManager.ID_DOOR_LOCK,
-            VehicleProperty.DOOR_LOCK,
-
-            CarCabinManager.ID_MIRROR_Z_POS,
-            VehicleProperty.MIRROR_Z_POS,
-
-            CarCabinManager.ID_MIRROR_Z_MOVE,
-            VehicleProperty.MIRROR_Z_MOVE,
-
-            CarCabinManager.ID_MIRROR_Y_POS,
-            VehicleProperty.MIRROR_Y_POS,
-
-            CarCabinManager.ID_MIRROR_Y_MOVE,
-            VehicleProperty.MIRROR_Y_MOVE,
-
-            CarCabinManager.ID_MIRROR_LOCK,
-            VehicleProperty.MIRROR_LOCK,
-
-            CarCabinManager.ID_MIRROR_FOLD,
-            VehicleProperty.MIRROR_FOLD,
-
-            CarCabinManager.ID_SEAT_MEMORY_SELECT,
-            VehicleProperty.SEAT_MEMORY_SELECT,
-
-            CarCabinManager.ID_SEAT_MEMORY_SET,
-            VehicleProperty.SEAT_MEMORY_SET,
-
-            CarCabinManager.ID_SEAT_BELT_BUCKLED,
-            VehicleProperty.SEAT_BELT_BUCKLED,
-
-            CarCabinManager.ID_SEAT_BELT_HEIGHT_POS,
-            VehicleProperty.SEAT_BELT_HEIGHT_POS,
-
-            CarCabinManager.ID_SEAT_BELT_HEIGHT_MOVE,
-            VehicleProperty.SEAT_BELT_HEIGHT_MOVE,
-
-            CarCabinManager.ID_SEAT_FORE_AFT_POS,
-            VehicleProperty.SEAT_FORE_AFT_POS,
-
-            CarCabinManager.ID_SEAT_FORE_AFT_MOVE,
-            VehicleProperty.SEAT_FORE_AFT_MOVE,
-
-            CarCabinManager.ID_SEAT_BACKREST_ANGLE_1_POS,
-            VehicleProperty.SEAT_BACKREST_ANGLE_1_POS,
-
-            CarCabinManager.ID_SEAT_BACKREST_ANGLE_1_MOVE,
-            VehicleProperty.SEAT_BACKREST_ANGLE_1_MOVE,
-
-            CarCabinManager.ID_SEAT_BACKREST_ANGLE_2_POS,
-            VehicleProperty.SEAT_BACKREST_ANGLE_2_POS,
-
-            CarCabinManager.ID_SEAT_BACKREST_ANGLE_2_MOVE,
-            VehicleProperty.SEAT_BACKREST_ANGLE_2_MOVE,
-
-            CarCabinManager.ID_SEAT_HEIGHT_POS,
-            VehicleProperty.SEAT_HEIGHT_POS,
-
-            CarCabinManager.ID_SEAT_HEIGHT_MOVE,
-            VehicleProperty.SEAT_HEIGHT_MOVE,
-
-            CarCabinManager.ID_SEAT_DEPTH_POS,
-            VehicleProperty.SEAT_DEPTH_POS,
-
-            CarCabinManager.ID_SEAT_DEPTH_MOVE,
-            VehicleProperty.SEAT_DEPTH_MOVE,
-
-            CarCabinManager.ID_SEAT_TILT_POS,
-            VehicleProperty.SEAT_TILT_POS,
-
-            CarCabinManager.ID_SEAT_TILT_MOVE,
-            VehicleProperty.SEAT_TILT_MOVE,
-
-            CarCabinManager.ID_SEAT_LUMBAR_FORE_AFT_POS,
-            VehicleProperty.SEAT_LUMBAR_FORE_AFT_POS,
-
-            CarCabinManager.ID_SEAT_LUMBAR_FORE_AFT_MOVE,
-            VehicleProperty.SEAT_LUMBAR_FORE_AFT_MOVE,
-
-            CarCabinManager.ID_SEAT_LUMBAR_SIDE_SUPPORT_POS,
-            VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_POS,
-
-            CarCabinManager.ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE,
-            VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_MOVE,
-
-            CarCabinManager.ID_SEAT_HEADREST_HEIGHT_POS,
-            VehicleProperty.SEAT_HEADREST_HEIGHT_POS,
-
-            CarCabinManager.ID_SEAT_HEADREST_HEIGHT_MOVE,
-            VehicleProperty.SEAT_HEADREST_HEIGHT_MOVE,
-
-            CarCabinManager.ID_SEAT_HEADREST_ANGLE_POS,
-            VehicleProperty.SEAT_HEADREST_ANGLE_POS,
-
-            CarCabinManager.ID_SEAT_HEADREST_ANGLE_MOVE,
-            VehicleProperty.SEAT_HEADREST_ANGLE_MOVE,
-
-            CarCabinManager.ID_SEAT_HEADREST_FORE_AFT_POS,
-            VehicleProperty.SEAT_HEADREST_FORE_AFT_POS,
-
-            CarCabinManager.ID_SEAT_HEADREST_FORE_AFT_MOVE,
-            VehicleProperty.SEAT_HEADREST_FORE_AFT_MOVE,
-
-            CarCabinManager.ID_WINDOW_POS,
-            VehicleProperty.WINDOW_POS,
-
-            CarCabinManager.ID_WINDOW_MOVE,
-            VehicleProperty.WINDOW_MOVE,
-
-            CarCabinManager.ID_WINDOW_LOCK,
-            VehicleProperty.WINDOW_LOCK
-    );
-
-    public CabinHalService(VehicleHal vehicleHal) {
-        super(vehicleHal, TAG, DBG);
-    }
-
-    // Convert the Cabin public API property ID to HAL property ID
-    @Override
-    protected int managerToHalPropId(int propId) {
-        return mMgrHalPropIdMap.getHalPropId(propId);
-    }
-
-    // Convert he HAL specific property ID to Cabin public API
-    @Override
-    protected int halToManagerPropId(int halPropId) {
-        return mMgrHalPropIdMap.getManagerPropId(halPropId);
-    }
-}
diff --git a/service/src/com/android/car/hal/CarPropertyUtils.java b/service/src/com/android/car/hal/CarPropertyUtils.java
index 8b5c446..d9151e2 100644
--- a/service/src/com/android/car/hal/CarPropertyUtils.java
+++ b/service/src/com/android/car/hal/CarPropertyUtils.java
@@ -51,14 +51,23 @@
         if (Boolean.class == clazz) {
             return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
                                           v.int32Values.get(0) == 1);
+        } else if (Boolean[].class == clazz) {
+            Boolean[] values = new Boolean[v.int32Values.size()];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = v.int32Values.get(i) == 1;
+            }
+            return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
         } else if (String.class == clazz) {
             return new CarPropertyValue<>(propertyId, areaId, status, timestamp, v.stringValue);
-        } else if (Long.class == clazz) {
-            return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
-                                          v.int64Values.get(0));
         } else if (byte[].class == clazz) {
             byte[] halData = toByteArray(v.bytes);
             return new CarPropertyValue<>(propertyId, areaId, status, timestamp, halData);
+        } else if (Long[].class == clazz) {
+            Long[] values = new Long[v.int64Values.size()];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = v.int64Values.get(i);
+            }
+            return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
         } else /* All list properties */ {
             Object[] values = getRawValueList(clazz, v).toArray();
             return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
@@ -76,15 +85,23 @@
         Object o = carProp.getValue();
 
         if (o instanceof Boolean) {
-            v.int32Values.add(((Boolean )o) ? 1 : 0);
+            v.int32Values.add(((Boolean) o) ? 1 : 0);
+        } else if (o instanceof Boolean[]) {
+            for (Boolean b : (Boolean[]) o) {
+                v.int32Values.add(((Boolean) o) ? 1 : 0);
+            }
         } else if (o instanceof Integer) {
             v.int32Values.add((Integer) o);
-        } else if (o instanceof Float) {
-            v.floatValues.add((Float) o);
         } else if (o instanceof Integer[]) {
             Collections.addAll(v.int32Values, (Integer[]) o);
+        } else if (o instanceof Float) {
+            v.floatValues.add((Float) o);
         } else if (o instanceof Float[]) {
             Collections.addAll(v.floatValues, (Float[]) o);
+        } else if (o instanceof Long) {
+            v.int64Values.add((Long) o);
+        } else if (o instanceof Long[]) {
+            Collections.addAll(v.int64Values, (Long[]) o);
         } else if (o instanceof String) {
             v.stringValue = (String) o;
         } else if (o instanceof byte[]) {
@@ -159,7 +176,7 @@
     private static @VehicleAreaType.VehicleAreaTypeValue int getVehicleAreaType(int halArea) {
         switch (halArea) {
             case VehicleArea.GLOBAL:
-                return VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
+                return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
             case VehicleArea.SEAT:
                 return VehicleAreaType.VEHICLE_AREA_TYPE_SEAT;
             case VehicleArea.DOOR:
@@ -203,10 +220,12 @@
     }
 
     private static List getRawValueList(Class<?> clazz, VehiclePropValue.RawValue value) {
-        if (classMatched(Float.class, clazz)) {
+        if (classMatched(Float.class, clazz) || classMatched(Float[].class, clazz)) {
             return value.floatValues;
-        } else if (classMatched(Integer.class, clazz)) {
+        } else if (classMatched(Integer.class, clazz) || classMatched(Integer[].class, clazz)) {
             return value.int32Values;
+        } else if (classMatched(Long.class, clazz) || classMatched(Long[].class, clazz)) {
+            return value.int64Values;
         } else {
             throw new IllegalArgumentException("Unexpected type: " + clazz);
         }
diff --git a/service/src/com/android/car/hal/HvacHalService.java b/service/src/com/android/car/hal/HvacHalService.java
deleted file mode 100644
index 0a7bd6b..0000000
--- a/service/src/com/android/car/hal/HvacHalService.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2015 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.hal;
-
-import android.car.hardware.hvac.CarHvacManager;
-import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
-
-public class HvacHalService extends PropertyHalServiceBase {
-    private static final boolean DBG = false;
-    private static final String TAG = "HvacHalService";
-
-    private final ManagerToHalPropIdMap mMgrHalPropIdMap = ManagerToHalPropIdMap.create(
-            CarHvacManager.ID_MIRROR_DEFROSTER_ON, VehicleProperty.HVAC_SIDE_MIRROR_HEAT,
-
-            CarHvacManager.ID_STEERING_WHEEL_HEAT, VehicleProperty.HVAC_STEERING_WHEEL_HEAT,
-
-            CarHvacManager.ID_OUTSIDE_AIR_TEMP, VehicleProperty.ENV_OUTSIDE_TEMPERATURE,
-
-            CarHvacManager.ID_TEMPERATURE_DISPLAY_UNITS,
-            VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS,
-
-            CarHvacManager.ID_ZONED_TEMP_SETPOINT, VehicleProperty.HVAC_TEMPERATURE_SET,
-
-            CarHvacManager.ID_ZONED_TEMP_ACTUAL, VehicleProperty.HVAC_TEMPERATURE_CURRENT,
-
-            CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, VehicleProperty.HVAC_FAN_SPEED,
-
-            CarHvacManager.ID_ZONED_FAN_SPEED_RPM, VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM,
-
-            CarHvacManager.ID_ZONED_FAN_DIRECTION_AVAILABLE,
-            VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE,
-
-            CarHvacManager.ID_ZONED_FAN_DIRECTION, VehicleProperty.HVAC_FAN_DIRECTION,
-
-            CarHvacManager.ID_ZONED_SEAT_TEMP, VehicleProperty.HVAC_SEAT_TEMPERATURE,
-
-            CarHvacManager.ID_ZONED_AC_ON, VehicleProperty.HVAC_AC_ON,
-
-            CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON, VehicleProperty.HVAC_AUTO_ON,
-
-            CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,VehicleProperty.HVAC_RECIRC_ON,
-
-            CarHvacManager.ID_ZONED_MAX_AC_ON, VehicleProperty.HVAC_MAX_AC_ON,
-
-            CarHvacManager.ID_ZONED_DUAL_ZONE_ON, VehicleProperty.HVAC_DUAL_ON,
-
-            CarHvacManager.ID_ZONED_MAX_DEFROST_ON, VehicleProperty.HVAC_MAX_DEFROST_ON,
-
-            CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleProperty.HVAC_POWER_ON,
-
-            CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON, VehicleProperty.HVAC_AUTO_RECIRC_ON,
-
-            CarHvacManager.ID_WINDOW_DEFROSTER_ON, VehicleProperty.HVAC_DEFROSTER
-    );
-
-    public HvacHalService(VehicleHal vehicleHal) {
-        super(vehicleHal, TAG, DBG);
-    }
-
-    // Convert the HVAC public API property ID to HAL property ID
-    @Override
-    protected int managerToHalPropId(int hvacPropId) {
-        return mMgrHalPropIdMap.getHalPropId(hvacPropId);
-    }
-
-    // Convert he HAL specific property ID to HVAC public API
-    @Override
-    protected int halToManagerPropId(int halPropId) {
-        return mMgrHalPropIdMap.getManagerPropId(halPropId);
-    }
-}
diff --git a/service/src/com/android/car/hal/InfoHalService.java b/service/src/com/android/car/hal/InfoHalService.java
deleted file mode 100644
index 779757f..0000000
--- a/service/src/com/android/car/hal/InfoHalService.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2015 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.hal;
-
-import android.car.CarInfoManager;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.car.CarLog;
-
-import java.io.PrintWriter;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-public class InfoHalService extends HalServiceBase {
-
-    private final VehicleHal mHal;
-    private Bundle mBasicInfo = new Bundle();
-
-    public InfoHalService(VehicleHal hal) {
-        mHal = hal;
-    }
-
-    @Override
-    public void init() {
-        //nothing to do
-    }
-
-    @Override
-    public synchronized void release() {
-        mBasicInfo = new Bundle();
-    }
-
-    @Override
-    public synchronized Collection<VehiclePropConfig> takeSupportedProperties(
-            Collection<VehiclePropConfig> allProperties) {
-        List<VehiclePropConfig> supported = new LinkedList<>();
-        for (VehiclePropConfig p: allProperties) {
-            switch (p.prop) {
-                case VehicleProperty.INFO_MAKE:
-                    readPropertyToBundle(p.prop, CarInfoManager.BASIC_INFO_KEY_MANUFACTURER);
-                    break;
-                case VehicleProperty.INFO_MODEL:
-                    readPropertyToBundle(p.prop, CarInfoManager.BASIC_INFO_KEY_MODEL);
-                    break;
-                case VehicleProperty.INFO_MODEL_YEAR:
-                    readPropertyToBundle(p.prop, CarInfoManager.BASIC_INFO_KEY_MODEL_YEAR);
-                    break;
-                case VehicleProperty.INFO_FUEL_CAPACITY:
-                    readPropertyToBundle(p.prop, CarInfoManager.BASIC_INFO_FUEL_CAPACITY);
-                    break;
-                case VehicleProperty.INFO_FUEL_TYPE:
-                    readPropertyToBundle(p.prop, CarInfoManager.BASIC_INFO_FUEL_TYPES);
-                    break;
-                case VehicleProperty.INFO_EV_BATTERY_CAPACITY:
-                    readPropertyToBundle(p.prop, CarInfoManager.BASIC_INFO_EV_BATTERY_CAPACITY);
-                    break;
-                case VehicleProperty.INFO_EV_CONNECTOR_TYPE:
-                    readPropertyToBundle(p.prop, CarInfoManager.BASIC_INFO_EV_CONNECTOR_TYPES);
-                    break;
-                default: // not supported
-                    break;
-            }
-        }
-        return supported;
-    }
-
-    private void readPropertyToBundle(int prop, String key) {
-        try {
-            int propType =  prop & VehiclePropertyType.MASK;
-
-            switch(propType) {
-                case VehiclePropertyType.STRING:
-                    mBasicInfo.putString(key, mHal.get(String.class, prop));
-                    break;
-                case VehiclePropertyType.FLOAT:
-                    mBasicInfo.putFloat(key, mHal.get(float.class, prop));
-                    break;
-                case VehiclePropertyType.INT32:
-                    mBasicInfo.putInt(key, mHal.get(int.class, prop));
-                    break;
-                case VehiclePropertyType.INT32_VEC:
-                    mBasicInfo.putIntArray(key, mHal.get(int[].class, prop));
-                    break;
-                default: // not supported
-                    throw(new IllegalArgumentException("Property type " + propType + " is not" +
-                        "supported"));
-            }
-        } catch (PropertyTimeoutException e) {
-            Log.e(CarLog.TAG_INFO, "Unable to read property", e);
-        }
-    }
-
-    @Override
-    public void handleHalEvents(List<VehiclePropValue> values) {
-        for (VehiclePropValue v : values) {
-            logUnexpectedEvent(v.prop);
-        }
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-        writer.println("*InfoHal*");
-        writer.println("**BasicInfo:" + mBasicInfo);
-    }
-
-    public synchronized Bundle getBasicInfo() {
-        return mBasicInfo;
-    }
-
-    private void logUnexpectedEvent(int property) {
-       Log.w(CarLog.TAG_INFO, "unexpected HAL event for property 0x" +
-               Integer.toHexString(property));
-    }
-}
diff --git a/service/src/com/android/car/hal/PropertyHalService.java b/service/src/com/android/car/hal/PropertyHalService.java
new file mode 100644
index 0000000..ad81507
--- /dev/null
+++ b/service/src/com/android/car/hal/PropertyHalService.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2018 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.hal;
+
+import static com.android.car.hal.CarPropertyUtils.toCarPropertyValue;
+import static com.android.car.hal.CarPropertyUtils.toVehiclePropValue;
+
+import static java.lang.Integer.toHexString;
+
+import android.annotation.Nullable;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.car.CarLog;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Common interface for HAL services that send Vehicle Properties back and forth via ICarProperty.
+ * Services that communicate by passing vehicle properties back and forth via ICarProperty should
+ * extend this class.
+ */
+public class PropertyHalService extends HalServiceBase {
+    private final boolean mDbg = true;
+    private final LinkedList<CarPropertyEvent> mEventsToDispatch = new LinkedList<>();
+    private final Map<Integer, CarPropertyConfig<?>> mProps =
+            new ConcurrentHashMap<>();
+    private final SparseArray<Float> mRates = new SparseArray<Float>();
+    private static final String TAG = "PropertyHalService";
+    private final VehicleHal mVehicleHal;
+    private final PropertyHalServiceIds mPropIds;
+
+    @GuardedBy("mLock")
+    private PropertyHalListener mListener;
+
+    private Set<Integer> mSubscribedPropIds;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Converts manager property ID to Vehicle HAL property ID.
+     * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}.
+     */
+    private int managerToHalPropId(int propId) {
+        if (mProps.containsKey(propId)) {
+            return propId;
+        } else {
+            return NOT_SUPPORTED_PROPERTY;
+        }
+    }
+
+    /**
+     * Converts Vehicle HAL property ID to manager property ID.
+     * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}.
+     */
+    private int halToManagerPropId(int halPropId) {
+        if (mProps.containsKey(halPropId)) {
+            return halPropId;
+        } else {
+            return NOT_SUPPORTED_PROPERTY;
+        }
+    }
+
+    /**
+     * PropertyHalListener used to send events to CarPropertyService
+     */
+    public interface PropertyHalListener {
+        /**
+         * This event is sent whenever the property value is updated
+         * @param event
+         */
+        void onPropertyChange(List<CarPropertyEvent> events);
+        /**
+         * This event is sent when the set property call fails
+         * @param property
+         * @param area
+         */
+        void onPropertySetError(int property, int area);
+    }
+
+    public PropertyHalService(VehicleHal vehicleHal) {
+        mPropIds = new PropertyHalServiceIds();
+        mSubscribedPropIds = new HashSet<Integer>();
+        mVehicleHal = vehicleHal;
+        if (mDbg) {
+            Log.d(TAG, "started PropertyHalService");
+        }
+    }
+
+    /**
+     * Set the listener for the HAL service
+     * @param listener
+     */
+    public void setListener(PropertyHalListener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+        }
+    }
+
+    /**
+     *
+     * @return List<CarPropertyConfig> List of configs available.
+     */
+    public Map<Integer, CarPropertyConfig<?>> getPropertyList() {
+        if (mDbg) {
+            Log.d(TAG, "getPropertyList");
+        }
+        return mProps;
+    }
+
+    /**
+     * Returns property or null if property is not ready yet.
+     * @param mgrPropId
+     * @param areaId
+     */
+    @Nullable
+    public CarPropertyValue getProperty(int mgrPropId, int areaId) {
+        int halPropId = managerToHalPropId(mgrPropId);
+        if (halPropId == NOT_SUPPORTED_PROPERTY) {
+            throw new IllegalArgumentException("Invalid property Id : 0x" + toHexString(mgrPropId));
+        }
+
+        VehiclePropValue value = null;
+        try {
+            value = mVehicleHal.get(halPropId, areaId);
+        } catch (PropertyTimeoutException e) {
+            Log.e(CarLog.TAG_PROPERTY, "get, property not ready 0x" + toHexString(halPropId), e);
+        }
+
+        return value == null ? null : toCarPropertyValue(value, mgrPropId);
+    }
+
+    /**
+     * Returns sample rate for the property
+     * @param propId
+     */
+    public float getSampleRate(int propId) {
+        return mVehicleHal.getSampleRate(propId);
+    }
+
+    /**
+     * Get the read permission string for the property.
+     * @param propId
+     */
+    @Nullable
+    public String getReadPermission(int propId) {
+        return mPropIds.getReadPermission(propId);
+    }
+
+    /**
+     * Get the write permission string for the property.
+     * @param propId
+     */
+    @Nullable
+    public String getWritePermission(int propId) {
+        return mPropIds.getWritePermission(propId);
+    }
+
+    /**
+     * Set the property value.
+     * @param prop
+     */
+    public void setProperty(CarPropertyValue prop) {
+        int halPropId = managerToHalPropId(prop.getPropertyId());
+        if (halPropId == NOT_SUPPORTED_PROPERTY) {
+            throw new IllegalArgumentException("Invalid property Id : 0x"
+                    + toHexString(prop.getPropertyId()));
+        }
+        VehiclePropValue halProp = toVehiclePropValue(prop, halPropId);
+        try {
+            mVehicleHal.set(halProp);
+        } catch (PropertyTimeoutException e) {
+            Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Subscribe to this property at the specified update rate.
+     * @param propId
+     * @param rate
+     */
+    public void subscribeProperty(int propId, float rate) {
+        if (mDbg) {
+            Log.d(TAG, "subscribeProperty propId=0x" + toHexString(propId) + ", rate=" + rate);
+        }
+        int halPropId = managerToHalPropId(propId);
+        if (halPropId == NOT_SUPPORTED_PROPERTY) {
+            throw new IllegalArgumentException("Invalid property Id : 0x"
+                    + toHexString(propId));
+        }
+        // Validate the min/max rate
+        CarPropertyConfig cfg = mProps.get(propId);
+        if (rate > cfg.getMaxSampleRate()) {
+            rate = cfg.getMaxSampleRate();
+        } else if (rate < cfg.getMinSampleRate()) {
+            rate = cfg.getMinSampleRate();
+        }
+        synchronized (mSubscribedPropIds) {
+            mSubscribedPropIds.add(halPropId);
+        }
+        mVehicleHal.subscribeProperty(this, halPropId, rate);
+    }
+
+    /**
+     * Unsubscribe the property and turn off update events for it.
+     * @param propId
+     */
+    public void unsubscribeProperty(int propId) {
+        if (mDbg) {
+            Log.d(TAG, "unsubscribeProperty propId=0x" + toHexString(propId));
+        }
+        int halPropId = managerToHalPropId(propId);
+        if (halPropId == NOT_SUPPORTED_PROPERTY) {
+            throw new IllegalArgumentException("Invalid property Id : 0x"
+                    + toHexString(propId));
+        }
+        synchronized (mSubscribedPropIds) {
+            if (mSubscribedPropIds.contains(halPropId)) {
+                mSubscribedPropIds.remove(halPropId);
+                mVehicleHal.unsubscribeProperty(this, halPropId);
+            }
+        }
+    }
+
+    @Override
+    public void init() {
+        if (mDbg) {
+            Log.d(TAG, "init()");
+        }
+    }
+
+    @Override
+    public void release() {
+        if (mDbg) {
+            Log.d(TAG, "release()");
+        }
+        synchronized (mSubscribedPropIds) {
+            for (Integer prop : mSubscribedPropIds) {
+                mVehicleHal.unsubscribeProperty(this, prop);
+            }
+            mSubscribedPropIds.clear();
+        }
+        mProps.clear();
+
+        synchronized (mLock) {
+            mListener = null;
+        }
+    }
+
+    @Override
+    public Collection<VehiclePropConfig> takeSupportedProperties(
+            Collection<VehiclePropConfig> allProperties) {
+        List<VehiclePropConfig> taken = new LinkedList<>();
+
+        for (VehiclePropConfig p : allProperties) {
+            if (mPropIds.isSupportedProperty(p.prop)) {
+                CarPropertyConfig config = CarPropertyUtils.toCarPropertyConfig(p, p.prop);
+                taken.add(p);
+                mProps.put(p.prop, config);
+                if (mDbg) {
+                    Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop));
+                }
+            }
+        }
+        if (mDbg) {
+            Log.d(TAG, "takeSupportedProperties() took " + taken.size() + " properties");
+        }
+        return taken;
+    }
+
+    @Override
+    public void handleHalEvents(List<VehiclePropValue> values) {
+        PropertyHalListener listener;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+        if (listener != null) {
+            for (VehiclePropValue v : values) {
+                int mgrPropId = halToManagerPropId(v.prop);
+                if (mgrPropId == NOT_SUPPORTED_PROPERTY) {
+                    Log.e(TAG, "Property is not supported: 0x" + toHexString(v.prop));
+                    continue;
+                }
+                CarPropertyValue<?> propVal = toCarPropertyValue(v, mgrPropId);
+                CarPropertyEvent event = new CarPropertyEvent(
+                        CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, propVal);
+                if (event != null) {
+                    mEventsToDispatch.add(event);
+                }
+            }
+            listener.onPropertyChange(mEventsToDispatch);
+            mEventsToDispatch.clear();
+        }
+    }
+
+    @Override
+    public void handlePropertySetError(int property, int area) {
+        PropertyHalListener listener;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+        if (listener != null) {
+            listener.onPropertySetError(property, area);
+        }
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        writer.println(TAG);
+        writer.println("  Properties available:");
+        for (CarPropertyConfig prop : mProps.values()) {
+            writer.println("    " + prop.toString());
+        }
+    }
+}
diff --git a/service/src/com/android/car/hal/PropertyHalServiceBase.java b/service/src/com/android/car/hal/PropertyHalServiceBase.java
deleted file mode 100644
index 07b722c..0000000
--- a/service/src/com/android/car/hal/PropertyHalServiceBase.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2016 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.hal;
-
-import static com.android.car.hal.CarPropertyUtils.toCarPropertyValue;
-import static com.android.car.hal.CarPropertyUtils.toVehiclePropValue;
-import static java.lang.Integer.toHexString;
-
-import android.annotation.Nullable;
-import android.car.hardware.CarPropertyConfig;
-import android.car.hardware.CarPropertyValue;
-import android.car.hardware.property.CarPropertyEvent;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.util.Log;
-
-import com.android.car.CarLog;
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Common interface for HAL services that send Vehicle Properties back and forth via ICarProperty.
- * Services that communicate by passing vehicle properties back and forth via ICarProperty should
- * extend this class.
- */
-public abstract class PropertyHalServiceBase extends HalServiceBase {
-    private final boolean mDbg;
-    private final ConcurrentHashMap<Integer, CarPropertyConfig<?>> mProps =
-            new ConcurrentHashMap<>();
-    private final String mTag;
-    private final VehicleHal mVehicleHal;
-
-    @GuardedBy("mLock")
-    private PropertyHalListener mListener;
-    private final Object mLock = new Object();
-
-    public interface PropertyHalListener {
-        void onPropertyChange(CarPropertyEvent event);
-        void onPropertySetError(int property, int area);
-    }
-
-    protected PropertyHalServiceBase(VehicleHal vehicleHal, String tag, boolean dbg) {
-        mVehicleHal = vehicleHal;
-        mTag = "PropertyHalServiceBase." + tag;
-        mDbg = dbg;
-
-        if (mDbg) {
-            Log.d(mTag, "started PropertyHalServiceBase!");
-        }
-    }
-
-    public void setListener(PropertyHalListener listener) {
-        synchronized (mLock) {
-            mListener = listener;
-        }
-    }
-
-    public List<CarPropertyConfig> getPropertyList() {
-        return new ArrayList<>(mProps.values());
-    }
-
-    /**
-     * Returns property or null if property is not ready yet.
-     */
-    @Nullable
-    public CarPropertyValue getProperty(int mgrPropId, int areaId) {
-        int halPropId = managerToHalPropId(mgrPropId);
-        if (halPropId == NOT_SUPPORTED_PROPERTY) {
-            throw new IllegalArgumentException("Invalid property Id : 0x" + toHexString(mgrPropId));
-        }
-
-        VehiclePropValue value = null;
-        try {
-            value = mVehicleHal.get(halPropId, areaId);
-        } catch (PropertyTimeoutException e) {
-            Log.e(CarLog.TAG_PROPERTY, "get, property not ready 0x" + toHexString(halPropId), e);
-        }
-
-        return value == null ? null : toCarPropertyValue(value, mgrPropId);
-    }
-
-    public void setProperty(CarPropertyValue prop) {
-        int halPropId = managerToHalPropId(prop.getPropertyId());
-        if (halPropId == NOT_SUPPORTED_PROPERTY) {
-            throw new IllegalArgumentException("Invalid property Id : 0x"
-                    + toHexString(prop.getPropertyId()));
-        }
-        VehiclePropValue halProp = toVehiclePropValue(prop, halPropId);
-        try {
-            mVehicleHal.set(halProp);
-        } catch (PropertyTimeoutException e) {
-            Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e);
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public void init() {
-        if (mDbg) {
-            Log.d(mTag, "init()");
-        }
-        // Subscribe to each of the properties
-        for (Integer prop : mProps.keySet()) {
-            mVehicleHal.subscribeProperty(this, prop);
-        }
-    }
-
-    @Override
-    public void release() {
-        if (mDbg) {
-            Log.d(mTag, "release()");
-        }
-
-        for (Integer prop : mProps.keySet()) {
-            mVehicleHal.unsubscribeProperty(this, prop);
-        }
-
-        // Clear the property list
-        mProps.clear();
-
-        synchronized (mLock) {
-            mListener = null;
-        }
-    }
-
-    @Override
-    public Collection<VehiclePropConfig> takeSupportedProperties(
-            Collection<VehiclePropConfig> allProperties) {
-        List<VehiclePropConfig> taken = new LinkedList<>();
-
-        for (VehiclePropConfig p : allProperties) {
-            int mgrPropId = halToManagerPropId(p.prop);
-
-            if (mgrPropId == NOT_SUPPORTED_PROPERTY) {
-                continue;  // The property is not handled by this HAL.
-            }
-
-            CarPropertyConfig config = CarPropertyUtils.toCarPropertyConfig(p, mgrPropId);
-
-            taken.add(p);
-            mProps.put(p.prop, config);
-
-            if (mDbg) {
-                Log.d(mTag, "takeSupportedProperties: " + toHexString(p.prop));
-            }
-        }
-        return taken;
-    }
-
-    @Override
-    public void handleHalEvents(List<VehiclePropValue> values) {
-        PropertyHalListener listener;
-        synchronized (mLock) {
-            listener = mListener;
-        }
-        if (listener != null) {
-            for (VehiclePropValue v : values) {
-                int prop = v.prop;
-                int mgrPropId = halToManagerPropId(prop);
-
-                if (mgrPropId == NOT_SUPPORTED_PROPERTY) {
-                    Log.e(mTag, "Property is not supported: 0x" + toHexString(prop));
-                    continue;
-                }
-
-                CarPropertyEvent event;
-                CarPropertyValue<?> propVal = toCarPropertyValue(v, mgrPropId);
-                event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
-                        propVal);
-
-                listener.onPropertyChange(event);
-                if (mDbg) {
-                    Log.d(mTag, "handleHalEvents event: " + event);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void handlePropertySetError(int property, int area) {
-        PropertyHalListener listener;
-        synchronized (mLock) {
-            listener = mListener;
-        }
-        if (listener != null) {
-            listener.onPropertySetError(property, area);
-        }
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-        writer.println(mTag);
-        writer.println("  Properties available:");
-        for (CarPropertyConfig prop : mProps.values()) {
-            writer.println("    " + prop.toString());
-        }
-    }
-
-    /**
-     * Converts manager property ID to Vehicle HAL property ID.
-     * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}.
-     */
-    abstract protected int managerToHalPropId(int managerPropId);
-
-    /**
-     * Converts Vehicle HAL property ID to manager property ID.
-     * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}.
-     */
-    abstract protected int halToManagerPropId(int halPropId);
-}
diff --git a/service/src/com/android/car/hal/PropertyHalServiceIds.java b/service/src/com/android/car/hal/PropertyHalServiceIds.java
new file mode 100644
index 0000000..651afea
--- /dev/null
+++ b/service/src/com/android/car/hal/PropertyHalServiceIds.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2018 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.hal;
+
+import android.annotation.Nullable;
+import android.car.Car;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
+import android.util.Pair;
+import android.util.SparseArray;
+
+/**
+ * Helper class to define which property IDs are used by PropertyHalService.  This class binds the
+ * read and write permissions to the property ID.
+ */
+public class PropertyHalServiceIds {
+    // Index (key is propertyId, and the value is readPermission, writePermission
+    private final SparseArray<Pair<String, String>> mProps;
+
+    public PropertyHalServiceIds() {
+        mProps = new SparseArray<>();
+
+        // Add propertyId and read/write permissions
+        // Cabin Properties
+        mProps.put(VehicleProperty.DOOR_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_DOORS,
+                Car.PERMISSION_CONTROL_CAR_DOORS));
+        mProps.put(VehicleProperty.DOOR_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_DOORS,
+                Car.PERMISSION_CONTROL_CAR_DOORS));
+        mProps.put(VehicleProperty.DOOR_LOCK, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_DOORS,
+                Car.PERMISSION_CONTROL_CAR_DOORS));
+        mProps.put(VehicleProperty.MIRROR_Z_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_MIRRORS,
+                Car.PERMISSION_CONTROL_CAR_MIRRORS));
+        mProps.put(VehicleProperty.MIRROR_Z_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_MIRRORS,
+                Car.PERMISSION_CONTROL_CAR_MIRRORS));
+        mProps.put(VehicleProperty.MIRROR_Y_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_MIRRORS,
+                Car.PERMISSION_CONTROL_CAR_MIRRORS));
+        mProps.put(VehicleProperty.MIRROR_Y_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_MIRRORS,
+                Car.PERMISSION_CONTROL_CAR_MIRRORS));
+        mProps.put(VehicleProperty.MIRROR_LOCK, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_MIRRORS,
+                Car.PERMISSION_CONTROL_CAR_MIRRORS));
+        mProps.put(VehicleProperty.MIRROR_FOLD, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_MIRRORS,
+                Car.PERMISSION_CONTROL_CAR_MIRRORS));
+        mProps.put(VehicleProperty.SEAT_MEMORY_SELECT, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_MEMORY_SET, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_BELT_BUCKLED, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_BELT_HEIGHT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_BELT_HEIGHT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_FORE_AFT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_FORE_AFT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_BACKREST_ANGLE_1_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_BACKREST_ANGLE_1_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_BACKREST_ANGLE_2_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_BACKREST_ANGLE_2_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEIGHT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEIGHT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_DEPTH_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_DEPTH_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_TILT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_TILT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_LUMBAR_FORE_AFT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_LUMBAR_FORE_AFT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEADREST_HEIGHT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEADREST_HEIGHT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEADREST_ANGLE_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEADREST_ANGLE_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEADREST_FORE_AFT_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.SEAT_HEADREST_FORE_AFT_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_SEATS,
+                Car.PERMISSION_CONTROL_CAR_SEATS));
+        mProps.put(VehicleProperty.WINDOW_POS, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_WINDOWS,
+                Car.PERMISSION_CONTROL_CAR_WINDOWS));
+        mProps.put(VehicleProperty.WINDOW_MOVE, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_WINDOWS,
+                Car.PERMISSION_CONTROL_CAR_WINDOWS));
+        mProps.put(VehicleProperty.WINDOW_LOCK, new Pair<>(
+                Car.PERMISSION_CONTROL_CAR_WINDOWS,
+                Car.PERMISSION_CONTROL_CAR_WINDOWS));
+
+        // HVAC properties
+        mProps.put(VehicleProperty.HVAC_FAN_SPEED, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_FAN_DIRECTION, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_TEMPERATURE_CURRENT, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_TEMPERATURE_SET, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_DEFROSTER, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_AC_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_MAX_AC_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_MAX_DEFROST_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_RECIRC_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_DUAL_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_AUTO_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_SEAT_TEMPERATURE, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_SIDE_MIRROR_HEAT, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_STEERING_WHEEL_HEAT, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_POWER_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_AUTO_RECIRC_ON, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.HVAC_SEAT_VENTILATION, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+        mProps.put(VehicleProperty.ENV_OUTSIDE_TEMPERATURE, new Pair<>(
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE,
+                    Car.PERMISSION_CONTROL_CAR_CLIMATE));
+
+        // Info properties
+        mProps.put(VehicleProperty.INFO_VIN, new Pair<>(
+                    Car.PERMISSION_IDENTIFICATION,
+                    Car.PERMISSION_IDENTIFICATION));
+        mProps.put(VehicleProperty.INFO_MAKE, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_MODEL, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_MODEL_YEAR, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_FUEL_CAPACITY, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_FUEL_TYPE, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_EV_BATTERY_CAPACITY, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_EV_CONNECTOR_TYPE, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_FUEL_DOOR_LOCATION, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_EV_PORT_LOCATION, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+        mProps.put(VehicleProperty.INFO_DRIVER_SEAT, new Pair<>(
+                    Car.PERMISSION_CAR_INFO,
+                    Car.PERMISSION_CAR_INFO));
+
+        // Sensor properties
+        mProps.put(VehicleProperty.PERF_ODOMETER, new Pair<>(
+                Car.PERMISSION_MILEAGE,
+                Car.PERMISSION_MILEAGE));
+        mProps.put(VehicleProperty.PERF_VEHICLE_SPEED, new Pair<>(
+                Car.PERMISSION_SPEED,
+                Car.PERMISSION_SPEED));
+        mProps.put(VehicleProperty.ENGINE_COOLANT_TEMP, new Pair<>(
+                Car.PERMISSION_CAR_ENGINE_DETAILED,
+                Car.PERMISSION_CAR_ENGINE_DETAILED));
+        mProps.put(VehicleProperty.ENGINE_OIL_LEVEL, new Pair<>(
+                Car.PERMISSION_CAR_ENGINE_DETAILED,
+                Car.PERMISSION_CAR_ENGINE_DETAILED));
+        mProps.put(VehicleProperty.ENGINE_OIL_TEMP, new Pair<>(
+                Car.PERMISSION_CAR_ENGINE_DETAILED,
+                Car.PERMISSION_CAR_ENGINE_DETAILED));
+        mProps.put(VehicleProperty.ENGINE_RPM, new Pair<>(
+                Car.PERMISSION_CAR_ENGINE_DETAILED,
+                Car.PERMISSION_CAR_ENGINE_DETAILED));
+        mProps.put(VehicleProperty.WHEEL_TICK, new Pair<>(
+                Car.PERMISSION_SPEED,
+                Car.PERMISSION_SPEED));
+        mProps.put(VehicleProperty.FUEL_LEVEL, new Pair<>(
+                Car.PERMISSION_ENERGY,
+                Car.PERMISSION_ENERGY));
+        mProps.put(VehicleProperty.FUEL_DOOR_OPEN, new Pair<>(
+                Car.PERMISSION_ENERGY_PORTS,
+                Car.PERMISSION_ENERGY_PORTS));
+        mProps.put(VehicleProperty.EV_BATTERY_LEVEL, new Pair<>(
+                Car.PERMISSION_ENERGY,
+                Car.PERMISSION_ENERGY));
+        mProps.put(VehicleProperty.EV_CHARGE_PORT_OPEN, new Pair<>(
+                Car.PERMISSION_ENERGY_PORTS,
+                Car.PERMISSION_ENERGY_PORTS));
+        mProps.put(VehicleProperty.EV_CHARGE_PORT_CONNECTED, new Pair<>(
+                Car.PERMISSION_ENERGY_PORTS,
+                Car.PERMISSION_ENERGY_PORTS));
+        mProps.put(VehicleProperty.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, new Pair<>(
+                Car.PERMISSION_ENERGY,
+                Car.PERMISSION_ENERGY));
+        mProps.put(VehicleProperty.RANGE_REMAINING, new Pair<>(
+                Car.PERMISSION_ENERGY,
+                Car.PERMISSION_ENERGY));
+        mProps.put(VehicleProperty.TIRE_PRESSURE, new Pair<>(
+                Car.PERMISSION_TIRES,
+                Car.PERMISSION_TIRES));
+        mProps.put(VehicleProperty.GEAR_SELECTION, new Pair<>(
+                Car.PERMISSION_POWERTRAIN,
+                Car.PERMISSION_POWERTRAIN));
+        mProps.put(VehicleProperty.CURRENT_GEAR, new Pair<>(
+                Car.PERMISSION_POWERTRAIN,
+                Car.PERMISSION_POWERTRAIN));
+        mProps.put(VehicleProperty.PARKING_BRAKE_ON, new Pair<>(
+                Car.PERMISSION_POWERTRAIN,
+                Car.PERMISSION_POWERTRAIN));
+        mProps.put(VehicleProperty.PARKING_BRAKE_AUTO_APPLY, new Pair<>(
+                Car.PERMISSION_POWERTRAIN,
+                Car.PERMISSION_POWERTRAIN));
+        mProps.put(VehicleProperty.FUEL_LEVEL_LOW, new Pair<>(
+                Car.PERMISSION_ENERGY,
+                Car.PERMISSION_ENERGY));
+        mProps.put(VehicleProperty.NIGHT_MODE, new Pair<>(
+                Car.PERMISSION_EXTERIOR_ENVIRONMENT,
+                Car.PERMISSION_EXTERIOR_ENVIRONMENT));
+        mProps.put(VehicleProperty.TURN_SIGNAL_STATE, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.IGNITION_STATE, new Pair<>(
+                Car.PERMISSION_CAR_POWER,
+                Car.PERMISSION_CAR_POWER));
+        mProps.put(VehicleProperty.ABS_ACTIVE, new Pair<>(
+                Car.PERMISSION_CAR_DYNAMICS_STATE,
+                Car.PERMISSION_CAR_DYNAMICS_STATE));
+        mProps.put(VehicleProperty.TRACTION_CONTROL_ACTIVE, new Pair<>(
+                Car.PERMISSION_CAR_DYNAMICS_STATE,
+                Car.PERMISSION_CAR_DYNAMICS_STATE));
+        mProps.put(VehicleProperty.ENV_OUTSIDE_TEMPERATURE, new Pair<>(
+                Car.PERMISSION_EXTERIOR_ENVIRONMENT,
+                Car.PERMISSION_EXTERIOR_ENVIRONMENT));
+        mProps.put(VehicleProperty.HEADLIGHTS_STATE, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.HIGH_BEAM_LIGHTS_STATE, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.FOG_LIGHTS_STATE, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.HAZARD_LIGHTS_STATE, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.HEADLIGHTS_SWITCH, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.HIGH_BEAM_LIGHTS_SWITCH, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.FOG_LIGHTS_SWITCH, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS));
+        mProps.put(VehicleProperty.HAZARD_LIGHTS_SWITCH, new Pair<>(
+                Car.PERMISSION_EXTERIOR_LIGHTS,
+                Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS));
+    }
+
+    /**
+     * Returns read permission string for given property ID.
+     */
+    @Nullable
+    public String getReadPermission(int propId) {
+        Pair<String, String> p = mProps.get(propId);
+        if (p != null) {
+            // Property ID exists.  Return read permission.
+            return p.first;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns write permission string for given property ID.
+     */
+    @Nullable
+    public String getWritePermission(int propId) {
+        Pair<String, String> p = mProps.get(propId);
+        if (p != null) {
+            // Property ID exists.  Return write permission.
+            return p.second;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Return true if property is a vendor property and was added
+     */
+    public boolean insertVendorProperty(int propId) {
+        if ((propId & VehiclePropertyGroup.MASK) == VehiclePropertyGroup.VENDOR) {
+            mProps.put(propId, new Pair<>(
+                    Car.PERMISSION_VENDOR_EXTENSION, Car.PERMISSION_VENDOR_EXTENSION));
+            return true;
+        } else {
+            // This is not a vendor extension property, it is not added
+            return false;
+        }
+    }
+
+    /**
+     * Check if property ID is in the list of known IDs that PropertyHalService is interested it.
+     */
+    public boolean isSupportedProperty(int propId) {
+        if (mProps.get(propId) != null) {
+            // Property is in the list of supported properties
+            return true;
+        } else {
+            // If it's a vendor property, insert it into the propId list and handle it
+            return insertVendorProperty(propId);
+        }
+    }
+}
diff --git a/service/src/com/android/car/hal/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
deleted file mode 100644
index f368955..0000000
--- a/service/src/com/android/car/hal/SensorHalService.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2015 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.hal;
-
-import static java.lang.Integer.toHexString;
-
-import android.annotation.Nullable;
-import android.car.hardware.CarSensorConfig;
-import android.car.hardware.CarSensorEvent;
-import android.car.hardware.CarSensorManager;
-import android.hardware.automotive.vehicle.V2_0.VehicleGear;
-import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.SparseIntArray;
-
-import com.android.car.CarLog;
-import com.android.car.CarSensorEventFactory;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Sensor HAL implementation for physical sensors in car.
- */
-public class SensorHalService extends SensorHalServiceBase {
-    private static final String TAG = CarLog.concatTag(CarLog.TAG_SENSOR, SensorHalService.class);
-    private static final boolean DBG_EVENTS = false;
-
-    /**
-     * Listener for monitoring sensor event. Only sensor service will implement this.
-     */
-    public interface SensorListener {
-        /**
-         * Sensor events are available.
-         *
-         * @param events
-         */
-        void onSensorEvents(List<CarSensorEvent> events);
-    }
-
-    // Manager property Id to HAL property Id mapping.
-    private final static ManagerToHalPropIdMap mManagerToHalPropIdMap =
-        ManagerToHalPropIdMap.create(
-            CarSensorManager.SENSOR_TYPE_CAR_SPEED, VehicleProperty.PERF_VEHICLE_SPEED,
-            CarSensorManager.SENSOR_TYPE_RPM, VehicleProperty.ENGINE_RPM,
-            CarSensorManager.SENSOR_TYPE_ODOMETER, VehicleProperty.PERF_ODOMETER,
-            CarSensorManager.SENSOR_TYPE_GEAR, VehicleProperty.GEAR_SELECTION,
-            CarSensorManager.SENSOR_TYPE_NIGHT, VehicleProperty.NIGHT_MODE,
-            CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, VehicleProperty.PARKING_BRAKE_ON,
-            CarSensorManager.SENSOR_TYPE_FUEL_LEVEL, VehicleProperty.FUEL_LEVEL,
-            CarSensorManager.SENSOR_TYPE_IGNITION_STATE, VehicleProperty.IGNITION_STATE,
-            CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE, VehicleProperty.WHEEL_TICK,
-            CarSensorManager.SENSOR_TYPE_ABS_ACTIVE, VehicleProperty.ABS_ACTIVE,
-            CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
-            VehicleProperty.TRACTION_CONTROL_ACTIVE,
-            CarSensorManager.SENSOR_TYPE_FUEL_DOOR_OPEN, VehicleProperty.FUEL_DOOR_OPEN,
-            CarSensorManager.SENSOR_TYPE_EV_BATTERY_LEVEL, VehicleProperty.EV_BATTERY_LEVEL,
-            CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_OPEN, VehicleProperty.EV_CHARGE_PORT_OPEN,
-            CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED,
-            VehicleProperty.EV_CHARGE_PORT_CONNECTED,
-            CarSensorManager.SENSOR_TYPE_EV_BATTERY_CHARGE_RATE,
-            VehicleProperty.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE,
-            CarSensorManager.SENSOR_TYPE_ENGINE_OIL_LEVEL, VehicleProperty.ENGINE_OIL_LEVEL
-        );
-
-    private final static SparseIntArray mMgrGearToHalMap = initSparseIntArray(
-            VehicleGear.GEAR_NEUTRAL, CarSensorEvent.GEAR_NEUTRAL,
-            VehicleGear.GEAR_REVERSE, CarSensorEvent.GEAR_REVERSE,
-            VehicleGear.GEAR_PARK, CarSensorEvent.GEAR_PARK,
-            VehicleGear.GEAR_DRIVE, CarSensorEvent.GEAR_DRIVE,
-            VehicleGear.GEAR_1, CarSensorEvent.GEAR_FIRST,
-            VehicleGear.GEAR_2, CarSensorEvent.GEAR_SECOND,
-            VehicleGear.GEAR_3, CarSensorEvent.GEAR_THIRD,
-            VehicleGear.GEAR_4, CarSensorEvent.GEAR_FOURTH,
-            VehicleGear.GEAR_5, CarSensorEvent.GEAR_FIFTH,
-            VehicleGear.GEAR_6, CarSensorEvent.GEAR_SIXTH,
-            VehicleGear.GEAR_7, CarSensorEvent.GEAR_SEVENTH,
-            VehicleGear.GEAR_8, CarSensorEvent.GEAR_EIGHTH,
-            VehicleGear.GEAR_9, CarSensorEvent.GEAR_NINTH);
-
-    private final static SparseIntArray mMgrIgnitionStateToHalMap = initSparseIntArray(
-        VehicleIgnitionState.UNDEFINED, CarSensorEvent.IGNITION_STATE_UNDEFINED,
-        VehicleIgnitionState.LOCK, CarSensorEvent.IGNITION_STATE_LOCK,
-        VehicleIgnitionState.OFF, CarSensorEvent.IGNITION_STATE_OFF,
-        VehicleIgnitionState.ACC, CarSensorEvent.IGNITION_STATE_ACC,
-        VehicleIgnitionState.ON, CarSensorEvent.IGNITION_STATE_ON,
-        VehicleIgnitionState.START, CarSensorEvent.IGNITION_STATE_START);
-
-    private SensorListener mSensorListener;
-
-    private int[] mMicrometersPerWheelTick = {0, 0, 0, 0};
-
-    @Override
-    public void init() {
-        VehiclePropConfig config;
-        // Populate internal values if available
-        synchronized (this) {
-            config = mSensorToPropConfig.get(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
-        }
-        if (config == null) {
-            Log.e(TAG, "init:  unable to get property config for SENSOR_TYPE_WHEEL_TICK_DISTANCE");
-        } else {
-            for (int i = 0; i < 4; i++) {
-                mMicrometersPerWheelTick[i] = config.configArray.get(i +
-                    INDEX_WHEEL_DISTANCE_FRONT_LEFT);
-            }
-        }
-        super.init();
-    }
-
-    public SensorHalService(VehicleHal hal) {
-        super(hal);
-    }
-
-    public synchronized void registerSensorListener(SensorListener listener) {
-        mSensorListener = listener;
-    }
-
-    @Override
-    protected int getTokenForProperty(VehiclePropConfig halProperty) {
-        int sensor = mManagerToHalPropIdMap.getManagerPropId(halProperty.prop);
-        if (sensor != SENSOR_TYPE_INVALID
-            && halProperty.changeMode != VehiclePropertyChangeMode.STATIC
-            && ((halProperty.access & VehiclePropertyAccess.READ) != 0)) {
-            return sensor;
-        }
-        return SENSOR_TYPE_INVALID;
-    }
-
-    // Should be used only inside handleHalEvents method.
-    private final LinkedList<CarSensorEvent> mEventsToDispatch = new LinkedList<>();
-
-    @Override
-    public void handleHalEvents(List<VehiclePropValue> values) {
-        for (VehiclePropValue v : values) {
-            CarSensorEvent event = createCarSensorEvent(v);
-            if (event != null) {
-                mEventsToDispatch.add(event);
-            }
-        }
-        SensorListener sensorListener;
-        synchronized (this) {
-            sensorListener = mSensorListener;
-        }
-        if (DBG_EVENTS) Log.d(TAG, "handleHalEvents, listener: " + sensorListener);
-        if (sensorListener != null) {
-            sensorListener.onSensorEvents(mEventsToDispatch);
-        }
-        mEventsToDispatch.clear();
-    }
-
-    @Nullable
-    private Integer mapHalEnumValueToMgr(int propId, int halValue) {
-        int mgrValue = halValue;
-
-        switch (propId) {
-            case VehicleProperty.GEAR_SELECTION:
-                mgrValue = mMgrGearToHalMap.get(halValue, -1);
-                break;
-            case VehicleProperty.IGNITION_STATE:
-                mgrValue = mMgrIgnitionStateToHalMap.get(halValue, -1);
-            default:
-                break; // Do nothing
-        }
-        return mgrValue == -1 ? null : mgrValue;
-    }
-
-    @Nullable
-    private CarSensorEvent createCarSensorEvent(VehiclePropValue v) {
-        int property = v.prop;
-        int sensorType = mManagerToHalPropIdMap.getManagerPropId(property);
-        if (sensorType == SENSOR_TYPE_INVALID) {
-            throw new RuntimeException("no sensor defined for property 0x" + toHexString(property));
-        }
-        // Handle the valid sensor
-        int dataType = property & VehiclePropertyType.MASK;
-        CarSensorEvent event = null;
-        switch (dataType) {
-            case VehiclePropertyType.BOOLEAN:
-                event = CarSensorEventFactory.createBooleanEvent(sensorType, v.timestamp,
-                    v.value.int32Values.get(0) == 1);
-                break;
-            case VehiclePropertyType.MIXED:
-                event = CarSensorEventFactory.createMixedEvent(sensorType, v.timestamp, v);
-                break;
-            case VehiclePropertyType.INT32:
-                Integer mgrVal = mapHalEnumValueToMgr(property, v.value.int32Values.get(0));
-                event = mgrVal == null ? null
-                    : CarSensorEventFactory.createIntEvent(sensorType, v.timestamp, mgrVal);
-                break;
-            case VehiclePropertyType.FLOAT:
-                event = CarSensorEventFactory.createFloatEvent(sensorType, v.timestamp,
-                    v.value.floatValues.get(0));
-                break;
-            case VehiclePropertyType.INT64_VEC:
-                event = CarSensorEventFactory.createInt64VecEvent(sensorType, v.timestamp,
-                                                                  v.value.int64Values);
-                break;
-            default:
-                Log.w(TAG, "createCarSensorEvent: unsupported type: 0x" + toHexString(dataType));
-                break;
-        }
-        // Perform property specific actions
-        switch (property) {
-            case VehicleProperty.WHEEL_TICK:
-                // Apply the um/tick scaling factor, then divide by 1000 to generate mm
-                for (int i = 0; i < 4; i++) {
-                    // ResetCounts is at longValues[0]
-                    if (event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] !=
-                        Long.MAX_VALUE) {
-                        event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] *=
-                            mMicrometersPerWheelTick[i];
-                        event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] /=
-                            1000;
-                    }
-                }
-                break;
-        }
-        if (DBG_EVENTS) Log.i(TAG, "Sensor event created: " + event);
-        return event;
-    }
-
-    @Nullable
-    public CarSensorEvent getCurrentSensorValue(int sensorType) {
-        VehiclePropValue propValue = getCurrentSensorVehiclePropValue(sensorType);
-        return (null != propValue) ? createCarSensorEvent(propValue) : null;
-    }
-
-    @Override
-    protected float fixSamplingRateForProperty(VehiclePropConfig prop, int carSensorManagerRate) {
-        switch (prop.changeMode) {
-            case VehiclePropertyChangeMode.ON_CHANGE:
-                return 0;
-        }
-        float rate = 1.0f;
-        switch (carSensorManagerRate) {
-            case CarSensorManager.SENSOR_RATE_FASTEST:
-                rate = prop.maxSampleRate;
-                break;
-            case CarSensorManager.SENSOR_RATE_FAST:
-                rate = 10f;  // every 100ms
-                break;
-            case CarSensorManager.SENSOR_RATE_UI:
-                rate = 5f;   // every 200ms
-                break;
-            default: // fall back to default.
-                break;
-        }
-        if (rate > prop.maxSampleRate) {
-            rate = prop.maxSampleRate;
-        }
-        if (rate < prop.minSampleRate) {
-            rate = prop.minSampleRate;
-        }
-        return rate;
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-        writer.println("*Sensor HAL*");
-        writer.println("**Supported properties**");
-        for (int i = 0; i < mSensorToPropConfig.size(); i++) {
-            writer.println(mSensorToPropConfig.valueAt(i).toString());
-        }
-        for (int i = 0; i < mMicrometersPerWheelTick.length; i++) {
-            writer.println("mMicrometersPerWheelTick[" + i + "] = " + mMicrometersPerWheelTick[i]);
-        }
-    }
-
-    private static SparseIntArray initSparseIntArray(int... keyValuePairs) {
-        int inputLength = keyValuePairs.length;
-        if (inputLength % 2 != 0) {
-            throw new IllegalArgumentException("Odd number of key-value elements");
-        }
-
-        SparseIntArray map = new SparseIntArray(inputLength / 2);
-        for (int i = 0; i < keyValuePairs.length; i += 2) {
-            map.put(keyValuePairs[i], keyValuePairs[i + 1]);
-        }
-        return map;
-    }
-
-    private static final int INDEX_WHEEL_DISTANCE_ENABLE_FLAG = 0;
-    private static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
-    private static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
-    private static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
-    private static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
-    private static final int WHEEL_TICK_DISTANCE_BUNDLE_SIZE = 6;
-
-    private Bundle createWheelDistanceTickBundle(ArrayList<Integer> configArray) {
-        Bundle b = new Bundle(WHEEL_TICK_DISTANCE_BUNDLE_SIZE);
-        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS,
-            configArray.get(INDEX_WHEEL_DISTANCE_ENABLE_FLAG));
-        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK,
-            configArray.get(INDEX_WHEEL_DISTANCE_FRONT_LEFT));
-        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK,
-            configArray.get(INDEX_WHEEL_DISTANCE_FRONT_RIGHT));
-        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK,
-            configArray.get(INDEX_WHEEL_DISTANCE_REAR_RIGHT));
-        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK,
-            configArray.get(INDEX_WHEEL_DISTANCE_REAR_LEFT));
-        return b;
-    }
-
-
-    public CarSensorConfig getSensorConfig(int sensorType) {
-        VehiclePropConfig cfg;
-        synchronized (this) {
-            cfg = mSensorToPropConfig.get(sensorType);
-        }
-        if (cfg == null) {
-            /* Invalid sensor type. */
-            throw new IllegalArgumentException("Unknown sensorType = " + sensorType);
-        } else {
-            Bundle b;
-            switch(sensorType) {
-                case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
-                    b = createWheelDistanceTickBundle(cfg.configArray);
-                    break;
-                default:
-                    /* Unhandled config.  Create empty bundle */
-                    b = Bundle.EMPTY;
-                    break;
-            }
-            return new CarSensorConfig(sensorType, b);
-        }
-    }
-}
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index aedf137..f73d07c 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -69,13 +69,9 @@
     private static final int NO_AREA = -1;
 
     private final HandlerThread mHandlerThread;
-    private final SensorHalService mSensorHal;
-    private final InfoHalService mInfoHal;
-    private final CabinHalService mCabinHal;
     private final PowerHalService mPowerHal;
-    private final HvacHalService mHvacHal;
+    private final PropertyHalService mPropertyHal;
     private final InputHalService mInputHal;
-    private final VendorExtensionHalService mVendorExtensionHal;
     private final VmsHalService mVmsHal;
     private DiagnosticHalService mDiagnosticHal = null;
 
@@ -98,21 +94,13 @@
         mHandlerThread.start();
         // passing this should be safe as long as it is just kept and not used in constructor
         mPowerHal = new PowerHalService(this);
-        mSensorHal = new SensorHalService(this);
-        mInfoHal = new InfoHalService(this);
-        mCabinHal = new CabinHalService(this);
-        mHvacHal = new HvacHalService(this);
+        mPropertyHal = new PropertyHalService(this);
         mInputHal = new InputHalService(this);
-        mVendorExtensionHal = new VendorExtensionHalService(this);
         mVmsHal = new VmsHalService(this);
         mDiagnosticHal = new DiagnosticHalService(this);
         mAllServices.addAll(Arrays.asList(mPowerHal,
-                mSensorHal,
-                mInfoHal,
-                mCabinHal,
-                mHvacHal,
                 mInputHal,
-                mVendorExtensionHal,
+                mPropertyHal,
                 mDiagnosticHal,
                 mVmsHal));
 
@@ -121,18 +109,13 @@
 
     /** Dummy version only for testing */
     @VisibleForTesting
-    public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal,
-            CabinHalService cabinHal, DiagnosticHalService diagnosticHal,
-            HvacHalService hvacHal, HalClient halClient) {
+    public VehicleHal(PowerHalService powerHal, DiagnosticHalService diagnosticHal,
+            HalClient halClient, PropertyHalService propertyHal) {
         mHandlerThread = null;
         mPowerHal = powerHal;
-        mSensorHal = sensorHal;
-        mInfoHal = infoHal;
-        mCabinHal = cabinHal;
+        mPropertyHal = propertyHal;
         mDiagnosticHal = diagnosticHal;
-        mHvacHal = hvacHal;
         mInputHal = null;
-        mVendorExtensionHal = null;
         mVmsHal = null;
         mHalClient = halClient;
         mDiagnosticHal = diagnosticHal;
@@ -207,36 +190,20 @@
         // keep the looper thread as should be kept for the whole life cycle.
     }
 
-    public SensorHalService getSensorHal() {
-        return mSensorHal;
-    }
-
-    public InfoHalService getInfoHal() {
-        return mInfoHal;
-    }
-
-    public CabinHalService getCabinHal() {
-        return mCabinHal;
-    }
-
     public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; }
 
     public PowerHalService getPowerHal() {
         return mPowerHal;
     }
 
-    public HvacHalService getHvacHal() {
-        return mHvacHal;
+    public PropertyHalService getPropertyHal() {
+        return mPropertyHal;
     }
 
     public InputHalService getInputHal() {
         return mInputHal;
     }
 
-    public VendorExtensionHalService getVendorExtensionHal() {
-        return mVendorExtensionHal;
-    }
-
     public VmsHalService getVmsHal() { return mVmsHal; }
 
     private void assertServiceOwnerLocked(HalServiceBase service, int property) {
@@ -403,6 +370,23 @@
         return mHalClient.getValue(requestedPropValue);
     }
 
+    /**
+     *
+     * @param propId Property ID to return the current sample rate for.
+     *
+     * @return float Returns the current sample rate of the specified propId, or -1 if the
+     *                  property is not currently subscribed.
+     */
+    public float getSampleRate(int propId) {
+        SubscribeOptions opts = mSubscribedProperties.get(propId);
+        if (opts == null) {
+            // No sample rate for this property
+            return -1;
+        } else {
+            return opts.sampleRate;
+        }
+    }
+
     void set(VehiclePropValue propValue) throws PropertyTimeoutException {
         mHalClient.setValue(propValue);
     }
diff --git a/service/src/com/android/car/hal/VendorExtensionHalService.java b/service/src/com/android/car/hal/VendorExtensionHalService.java
deleted file mode 100644
index ab351be..0000000
--- a/service/src/com/android/car/hal/VendorExtensionHalService.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 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.hal;
-
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
-
-/**
- * Implementation of {@link HalServiceBase} that responsible for custom properties that were defined
- * by OEMs.
- */
-public class VendorExtensionHalService extends PropertyHalServiceBase {
-
-    private final static String TAG = VendorExtensionHalService.class.getSimpleName();
-    private final static boolean DEBUG = false;
-
-    VendorExtensionHalService(VehicleHal vehicleHal) {
-        super(vehicleHal, TAG, DEBUG);
-    }
-
-    @Override
-    protected int managerToHalPropId(int managerPropId) {
-        return isVendorProperty(managerPropId) ? managerPropId : NOT_SUPPORTED_PROPERTY;
-    }
-
-    @Override
-    protected int halToManagerPropId(int halPropId) {
-        return isVendorProperty(halPropId) ? halPropId : NOT_SUPPORTED_PROPERTY;
-    }
-
-    private static boolean isVendorProperty(int property) {
-        return (property & VehiclePropertyGroup.MASK) == VehiclePropertyGroup.VENDOR;
-    }
-}
\ No newline at end of file
diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java
index bea3e2c..fd84e2b 100644
--- a/service/src/com/android/car/hal/VmsHalService.java
+++ b/service/src/com/android/car/hal/VmsHalService.java
@@ -802,7 +802,7 @@
     private static VehiclePropValue toTypedVmsVehiclePropValue(int messageType) {
         VehiclePropValue vehicleProp = new VehiclePropValue();
         vehicleProp.prop = HAL_PROPERTY_ID;
-        vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
+        vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
         VehiclePropValue.RawValue v = vehicleProp.value;
 
         v.int32Values.add(messageType);
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml b/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml
index 759a73e..89d67be 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml
@@ -17,8 +17,13 @@
     android:orientation="vertical" android:layout_width="match_parent"
     android:layout_height="match_parent">
     <ImageView
-        android:id="@+id/voice_button"
+        android:id="@+id/voice_button_intent"
         android:layout_gravity="center"
         android:src="@drawable/ic_voice_assistant_mic"
         style="@style/OverviewButton"/>
-</LinearLayout>
\ No newline at end of file
+    <ImageView
+        android:id="@+id/voice_button_service"
+        android:layout_gravity="center"
+        android:src="@drawable/ic_voice_assistant_mic"
+        style="@style/OverviewButton"/>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/property.xml b/tests/EmbeddedKitchenSinkApp/res/layout/property.xml
new file mode 100644
index 0000000..f8f6f79
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/property.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal" >
+    <ListView
+        android:id="@+id/lvPropertyList"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:layout_width="0dp"
+        android:scrollbars="vertical">
+    </ListView>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:layout_width="0dp"
+        android:orientation="vertical" >
+
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_height="0dp"
+            android:layout_weight="2"
+            android:layout_width="match_parent"
+            android:orientation="horizontal" >
+            <Spinner
+                android:id="@+id/sPropertyId"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_width="0dp" />
+            <Button
+                android:id="@+id/bGetProperty"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_width="0dp"
+                android:text="@string/property_get"
+                android:textSize="@dimen/propertyTextSize"/>
+            <TextView
+                android:id="@+id/tvGetPropertyValue"
+                android:gravity="center"
+                android:layout_height="wrap_content"
+                android:layout_weight="2"
+                android:layout_width="0dp"
+                android:textSize="@dimen/propertyValueTextSize"/>
+        </LinearLayout>
+
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:orientation="horizontal" >
+            <Spinner
+                android:id="@+id/sAreaId"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_width="0dp" />
+            <EditText
+                android:id="@+id/etSetPropertyValue"
+                android:layout_height="wrap_content"
+                android:layout_weight="2"
+                android:layout_width="0dp"
+                android:inputType="phone" />
+            <Button
+                android:id="@+id/bSetProperty"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_width="0dp"
+                android:text="@string/property_set"
+                android:textSize="@dimen/propertyTextSize"/>
+        </LinearLayout>
+
+        <!-- Event Log -->
+        <ScrollView
+            android:id="@+id/svEventLog"
+            android:layout_height="0dp"
+            android:layout_weight="6"
+            android:layout_width="match_parent"
+            android:scrollbars="vertical">
+            <TextView
+                android:id="@+id/tvEventLog"
+                android:gravity="left"
+                android:layout_height="match_parent"
+                android:layout_width="match_parent"
+                android:textSize="@dimen/propertyValueTextSize"/>
+        </ScrollView>
+        <Button
+            android:id="@+id/bClearLog"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:text="@string/property_clear"
+            android:textSize="@dimen/propertyTextSize"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/property_list_item.xml b/tests/EmbeddedKitchenSinkApp/res/layout/property_list_item.xml
new file mode 100644
index 0000000..f8051ee
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/property_list_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <TextView
+        android:id="@+id/tvPropertyName"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_alignParentLeft="true"
+        android:paddingLeft="8dp"
+        android:textSize="@dimen/propertyTextSize" />
+
+    <ToggleButton
+        android:id="@+id/tbRegisterPropertyBtn"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:text="@string/property_register" />
+
+</RelativeLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
index a9e3457..c8806bc 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
@@ -20,6 +20,8 @@
     <dimen name="powerBtnHeight">80dp</dimen>
     <dimen name="powerBtnWidth">300dp</dimen>
     <dimen name="powerTextSize">24sp</dimen>
+    <dimen name="propertyTextSize">24sp</dimen>
+    <dimen name="propertyValueTextSize">14sp</dimen>
     <dimen name="rvcBtnHeight">40dp</dimen>
     <dimen name="rvcBtnWidth">150dp</dimen>
     <dimen name="rvcTextSize">10dp</dimen>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 36854c1..74c7c26 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -180,6 +180,13 @@
     <string name="power_shutdown">Shutdown</string>
     <string name="power_sleep">Sleep</string>
 
+    <!-- property test -->
+    <string name="property_clear">Clear Log</string>
+    <string name="property_get">Get</string>
+    <string name="property_register">Register</string>
+    <string name="property_set">Set</string>
+    <string name="property_unregister">Unregister</string>
+
     <!-- radio test -->
     <string name="radio_open">Open</string>
     <string name="radio_close">Close</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index aa6202a..7837f2e 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -17,8 +17,10 @@
 package com.google.android.car.kitchensink;
 
 
+import android.car.hardware.CarSensorManager;
 import android.car.hardware.hvac.CarHvacManager;
 import android.car.hardware.power.CarPowerManager;
+import android.car.hardware.property.CarPropertyManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
@@ -26,7 +28,6 @@
 import android.support.car.CarAppFocusManager;
 import android.support.car.CarConnectionCallback;
 import android.support.car.CarNotConnectedException;
-import android.support.car.hardware.CarSensorManager;
 import android.support.v4.app.Fragment;
 import android.util.Log;
 
@@ -50,6 +51,7 @@
 import com.google.android.car.kitchensink.notification.NotificationFragment;
 import com.google.android.car.kitchensink.orientation.OrientationTestFragment;
 import com.google.android.car.kitchensink.power.PowerTestFragment;
+import com.google.android.car.kitchensink.property.PropertyTestFragment;
 import com.google.android.car.kitchensink.sensor.SensorsTestFragment;
 import com.google.android.car.kitchensink.setting.CarServiceSettingsActivity;
 import com.google.android.car.kitchensink.storagelifetime.StorageLifetimeFragment;
@@ -153,6 +155,7 @@
             add("notification", NotificationFragment.class);
             add("orientation test", OrientationTestFragment.class);
             add("power test", PowerTestFragment.class);
+            add("property test", PropertyTestFragment.class);
             add("sensors", SensorsTestFragment.class);
             add("storage lifetime", StorageLifetimeFragment.class);
             add("touch test", TouchTestFragment.class);
@@ -177,17 +180,10 @@
     private Car mCarApi;
     private CarHvacManager mHvacManager;
     private CarPowerManager mPowerManager;
-    private CarSensorManager mCarSensorManager;
+    private CarPropertyManager mPropertyManager;
+    private CarSensorManager mSensorManager;
     private CarAppFocusManager mCarAppFocusManager;
 
-    private final CarSensorManager.OnSensorChangedListener mListener = (manager, event) -> {
-        switch (event.sensorType) {
-            case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
-                Log.d(TAG, "driving status:" + event.intValues[0]);
-                break;
-        }
-    };
-
     public CarHvacManager getHvacManager() {
         return mHvacManager;
     }
@@ -196,11 +192,19 @@
         return mPowerManager;
     }
 
+    public CarPropertyManager getPropertyManager() {
+        return mPropertyManager;
+    }
+
     @Override
     protected CarDrawerAdapter getRootAdapter() {
         return new DrawerAdapter();
     }
 
+    public CarSensorManager getSensorManager() {
+        return mSensorManager;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -247,9 +251,6 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        if (mCarSensorManager != null) {
-            mCarSensorManager.removeListener(mListener);
-        }
         if (mCarApi != null) {
             mCarApi.disconnect();
         }
@@ -271,10 +272,10 @@
                 mHvacManager = (CarHvacManager) mCarApi.getCarManager(android.car.Car.HVAC_SERVICE);
                 mPowerManager = (CarPowerManager) mCarApi.getCarManager(
                     android.car.Car.POWER_SERVICE);
-                mCarSensorManager = (CarSensorManager) mCarApi.getCarManager(Car.SENSOR_SERVICE);
-                mCarSensorManager.addListener(mListener,
-                        CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
-                        CarSensorManager.SENSOR_RATE_NORMAL);
+                mPropertyManager = (CarPropertyManager) mCarApi.getCarManager(
+                    android.car.Car.PROPERTY_SERVICE);
+                mSensorManager = (CarSensorManager) mCarApi.getCarManager(
+                    android.car.Car.SENSOR_SERVICE);
                 mCarAppFocusManager =
                         (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
             } catch (CarNotConnectedException e) {
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java
index 147b017..4bee453 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java
@@ -31,15 +31,18 @@
 
 public class CarAssistantFragment extends Fragment {
 
-    private ImageView mMic;
+    private ImageView mMicIntent;
+    private ImageView mMicService;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                              @Nullable Bundle savedInstanceState) {
         View v = inflater.inflate(R.layout.car_assistant, container, false);
-        mMic = (ImageView) v.findViewById(R.id.voice_button);
+        mMicIntent = (ImageView) v.findViewById(R.id.voice_button_intent);
+        mMicService = (ImageView) v.findViewById(R.id.voice_button_service);
         Context context = getContext();
-        mMic.setOnClickListener(new View.OnClickListener() {
+
+        mMicIntent.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
@@ -54,6 +57,14 @@
                 }
             }
         });
+        mMicService.setOnClickListener(v1 -> {
+            v1.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+            boolean success = getActivity().showAssist(null);
+            if (!success) {
+                Toast.makeText(context,
+                        "Assistant app is not available.", Toast.LENGTH_SHORT).show();
+            }
+        });
         return v;
     }
 }
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyInfo.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyInfo.java
new file mode 100644
index 0000000..0f0b5d4
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyInfo.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.google.android.car.kitchensink.property;
+
+import android.car.hardware.CarPropertyConfig;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+
+class PropertyInfo implements Comparable<PropertyInfo> {
+    public final CarPropertyConfig mConfig;
+    public final String mName;
+    public final int mPropId;
+
+    PropertyInfo(CarPropertyConfig config) {
+        mConfig = config;
+        mPropId = config.getPropertyId();
+        mName = VehicleProperty.toString(mPropId);
+    }
+
+    @Override
+    public String toString() {
+        return mName;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof PropertyInfo) {
+            return ((PropertyInfo) other).mPropId == mPropId;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mPropId;
+    }
+
+    @Override
+    public int compareTo(PropertyInfo propertyInfo) {
+        return mName.compareTo(propertyInfo.mName);
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyListAdapter.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyListAdapter.java
new file mode 100644
index 0000000..06ea4ee
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyListAdapter.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 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.google.android.car.kitchensink.property;
+
+import static java.lang.Integer.toHexString;
+
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.List;
+
+class PropertyListAdapter extends BaseAdapter implements ListAdapter {
+    private static final int DEFAULT_RATE = 1;
+    private static final String TAG = "PropertyListAdapter";
+    private final Context mContext;
+    private final PropertyListEventListener mListener;
+    private final CarPropertyManager mMgr;
+    private final List<PropertyInfo> mPropInfo;
+    private final TextView mTvEventLog;
+
+    PropertyListAdapter(List<PropertyInfo> propInfo, CarPropertyManager mgr, TextView eventLog,
+                        ScrollView svEventLog, Context context) {
+        mContext = context;
+        mListener = new PropertyListEventListener(eventLog, svEventLog);
+        mMgr = mgr;
+        mPropInfo = propInfo;
+        mTvEventLog = eventLog;
+    }
+
+    @Override
+    public int getCount() {
+        return mPropInfo.size();
+    }
+
+    @Override
+    public Object getItem(int pos) {
+        return mPropInfo.get(pos);
+    }
+
+    @Override
+    public long getItemId(int pos) {
+        return mPropInfo.get(pos).mPropId;
+    }
+
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        View view = convertView;
+        if (view == null) {
+            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            view = inflater.inflate(R.layout.property_list_item, null);
+        }
+
+        //Handle TextView and display string from your list
+        TextView listItemText = (TextView) view.findViewById(R.id.tvPropertyName);
+        listItemText.setText(mPropInfo.get(position).toString());
+
+        //Handle buttons and add onClickListeners
+        ToggleButton btn = (ToggleButton) view.findViewById(R.id.tbRegisterPropertyBtn);
+
+        btn.setOnClickListener(new View.OnClickListener(){
+            @Override
+            public void onClick(View v) {
+                CarPropertyConfig c = mPropInfo.get(position).mConfig;
+                int propId = c.getPropertyId();
+                try {
+                    if (btn.isChecked()) {
+                        mMgr.registerListener(mListener, propId, DEFAULT_RATE);
+                    } else {
+                        mMgr.unregisterListener(mListener, propId);
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "Unhandled exception: ", e);
+                }
+            }
+        });
+        return view;
+    }
+
+
+    private static class PropertyListEventListener implements
+            CarPropertyManager.CarPropertyEventListener {
+        private int mNumEvents;
+        private ScrollView mScrollView;
+        private TextView mTvLogEvent;
+
+        PropertyListEventListener(TextView logEvent, ScrollView scrollView) {
+            mScrollView = scrollView;
+            mTvLogEvent = logEvent;
+        }
+
+        @Override
+        public void onChangeEvent(CarPropertyValue value) {
+            mNumEvents++;
+            mTvLogEvent.append("Event " + mNumEvents + ":"
+                               + " time=" + value.getTimestamp()
+                               + " propId=0x" + toHexString(value.getPropertyId())
+                               + " areaId=0x" + toHexString(value.getAreaId())
+                               + " status=" + value.getStatus()
+                               + " value=" + value.getValue()
+                               + "\n");
+            scrollToBottom();
+        }
+
+        @Override
+        public void onErrorEvent(int propId, int areaId) {
+            mTvLogEvent.append("Received error event propId=0x" + toHexString(propId)
+                               + ", areaId=0x" + toHexString(areaId));
+            scrollToBottom();
+        }
+
+        private void scrollToBottom() {
+            mScrollView.post(new Runnable() {
+                public void run() {
+                    mScrollView.fullScroll(View.FOCUS_DOWN);
+                    //mScrollView.smoothScrollTo(0, mTextStatus.getBottom());
+                }
+            });
+        }
+
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java
new file mode 100644
index 0000000..fc6621a
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2018 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.google.android.car.kitchensink.property;
+
+import static java.lang.Integer.toHexString;
+
+import android.annotation.Nullable;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.ScrollView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.android.car.kitchensink.KitchenSinkActivity;
+import com.google.android.car.kitchensink.R;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class PropertyTestFragment extends Fragment implements OnItemSelectedListener{
+    private static final String TAG = "PropertyTestFragment";
+
+    private KitchenSinkActivity mActivity;
+    private CarPropertyManager mMgr;
+    private List<PropertyInfo> mPropInfo = null;
+    private Spinner mAreaId;
+    private TextView mEventLog;
+    private TextView mGetValue;
+    private ListView mListView;
+    private Spinner mPropertyId;
+    private ScrollView mScrollView;
+    private EditText mSetValue;
+
+    private final OnClickListener mNopOnClickListener = new OnClickListener() {
+        @Override
+        public void onClick(DialogInterface dialog, int which) { }
+    };
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater,
+            @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.property, container, false);
+        mActivity = (KitchenSinkActivity) getHost();
+        mMgr = mActivity.getPropertyManager();
+
+        // Get resource IDs
+        mAreaId = view.findViewById(R.id.sAreaId);
+        mEventLog = view.findViewById(R.id.tvEventLog);
+        mGetValue = view.findViewById(R.id.tvGetPropertyValue);
+        mListView = view.findViewById(R.id.lvPropertyList);
+        mPropertyId = view.findViewById(R.id.sPropertyId);
+        mScrollView = view.findViewById(R.id.svEventLog);
+        mSetValue = view.findViewById(R.id.etSetPropertyValue);
+
+        populateConfigList();
+        mListView.setAdapter(new PropertyListAdapter(mPropInfo, mMgr, mEventLog, mScrollView,
+                                                     mActivity));
+
+        // Configure dropdown menu for propertyId spinner
+        ArrayAdapter<PropertyInfo> adapter =
+                new ArrayAdapter<PropertyInfo>(mActivity, android.R.layout.simple_spinner_item,
+                                               mPropInfo);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mPropertyId.setAdapter(adapter);
+        mPropertyId.setOnItemSelectedListener(this);
+
+
+
+        // Configure listeners for buttons
+        Button b = view.findViewById(R.id.bGetProperty);
+        b.setOnClickListener(v -> {
+            try {
+                PropertyInfo info = (PropertyInfo) mPropertyId.getSelectedItem();
+                int propId = info.mConfig.getPropertyId();
+                int areaId = Integer.decode(mAreaId.getSelectedItem().toString());
+                CarPropertyValue value = mMgr.getProperty(propId, areaId);
+                if (propId == VehicleProperty.WHEEL_TICK) {
+                    Object[] ticks = (Object[]) value.getValue();
+                    mGetValue.setText("Timestamp=" + value.getTimestamp()
+                                      + "\nstatus=" + value.getStatus()
+                                      + "\n[0]=" + (Long) ticks[0]
+                                      + "\n[1]=" + (Long) ticks[1] + " [2]=" + (Long) ticks[2]
+                                      + "\n[3]=" + (Long) ticks[3] + " [4]=" + (Long) ticks[4]);
+                } else {
+                    mGetValue.setText("Timestamp=" + value.getTimestamp()
+                                      + "\nstatus=" + value.getStatus()
+                                      + "\nvalue=" + value.getValue());
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to get property", e);
+            }
+        });
+
+        b = view.findViewById(R.id.bSetProperty);
+        b.setOnClickListener(v -> {
+            try {
+                PropertyInfo info = (PropertyInfo) mPropertyId.getSelectedItem();
+                int propId = info.mConfig.getPropertyId();
+                int areaId = Integer.decode(mAreaId.getSelectedItem().toString());
+                String valueString = mSetValue.getText().toString();
+
+                switch (propId & VehiclePropertyType.MASK) {
+                    case VehiclePropertyType.BOOLEAN:
+                        Boolean boolVal = Boolean.parseBoolean(valueString);
+                        mMgr.setBooleanProperty(propId, areaId, boolVal);
+                        break;
+                    case VehiclePropertyType.FLOAT:
+                        Float floatVal = Float.parseFloat(valueString);
+                        mMgr.setFloatProperty(propId, areaId, floatVal);
+                        break;
+                    case VehiclePropertyType.INT32:
+                        Integer intVal = Integer.parseInt(valueString);
+                        mMgr.setIntProperty(propId, areaId, intVal);
+                        break;
+                    default:
+                        Toast.makeText(mActivity, "PropertyType=0x" + toHexString(propId
+                                & VehiclePropertyType.MASK) + " is not handled!",
+                                Toast.LENGTH_LONG).show();
+                        break;
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to set HVAC boolean property", e);
+            }
+        });
+
+        b = view.findViewById(R.id.bClearLog);
+        b.setOnClickListener(v -> {
+            mEventLog.setText("");
+        });
+
+        return view;
+    }
+
+    private void populateConfigList() {
+        try {
+            mPropInfo = mMgr.getPropertyList()
+                    .stream()
+                    .map(PropertyInfo::new)
+                    .sorted()
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            Log.e(TAG, "Unhandled exception in populateConfigList: ", e);
+        }
+    }
+
+    // Spinner callbacks
+    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+        // An item was selected. You can retrieve the selected item using
+        PropertyInfo info = (PropertyInfo) parent.getItemAtPosition(pos);
+        int[] areaIds = info.mConfig.getAreaIds();
+        List<String> areaString = new LinkedList<String>();
+        if (areaIds.length == 0) {
+            areaString.add("0x0");
+        } else {
+            for (int areaId : areaIds) {
+                areaString.add("0x" + toHexString(areaId));
+            }
+        }
+
+        // Configure dropdown menu for propertyId spinner
+        ArrayAdapter<String> adapter =
+                new ArrayAdapter<String>(mActivity, android.R.layout.simple_spinner_item,
+                                         areaString);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mAreaId.setAdapter(adapter);
+    }
+
+    public void onNothingSelected(AdapterView<?> parent) {
+        // Another interface callback
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
index 9c84f6e..abc2c10 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
@@ -16,17 +16,18 @@
 
 package com.google.android.car.kitchensink.sensor;
 
+import static java.lang.Integer.toHexString;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.hardware.CarSensorConfig;
+import android.car.hardware.CarSensorEvent;
+import android.car.hardware.CarSensorManager;
 import android.content.pm.PackageManager;
-import android.location.Location;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.car.CarNotConnectedException;
-import android.support.car.hardware.CarSensorConfig;
-import android.support.car.hardware.CarSensorEvent;
-import android.support.car.hardware.CarSensorManager;
 import android.support.v4.app.Fragment;
 import android.text.TextUtils;
 import android.util.Log;
@@ -51,7 +52,7 @@
 public class SensorsTestFragment extends Fragment {
     private static final String TAG = "CAR.SENSOR.KS";
     private static final boolean DBG = true;
-    private static final boolean DBG_VERBOSE = false;
+    private static final boolean DBG_VERBOSE = true;
     private static final int KS_PERMISSIONS_REQUEST = 1;
 
     private final static String[] REQUIRED_PERMISSIONS = new String[]{
@@ -66,7 +67,7 @@
     private final CarSensorManager.OnSensorChangedListener mOnSensorChangedListener =
             new CarSensorManager.OnSensorChangedListener() {
                 @Override
-                public void onSensorChanged(CarSensorManager manager, CarSensorEvent event) {
+                public void onSensorChanged(CarSensorEvent event) {
                     if (DBG_VERBOSE) {
                         Log.v(TAG, "New car sensor event: " + event);
                     }
@@ -115,26 +116,23 @@
     public void onPause() {
         super.onPause();
         if (mSensorManager != null) {
-            mSensorManager.removeListener(mOnSensorChangedListener);
+            mSensorManager.unregisterListener(mOnSensorChangedListener);
         }
     }
 
     private void initSensors() {
         try {
-            mSensorManager = (CarSensorManager)
-                    mActivity.getCar().getCarManager(Car.SENSOR_SERVICE);
+            mSensorManager =
+                (CarSensorManager) ((KitchenSinkActivity) getActivity()).getSensorManager();
             supportedSensors = mSensorManager.getSupportedSensors();
             for (Integer sensor : supportedSensors) {
-                if ((sensor == CarSensorManager.SENSOR_TYPE_LOCATION
-                     || sensor == CarSensorManager.SENSOR_TYPE_GPS_SATELLITE)
-                    && !mActivePermissions.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
-                    continue;
-                }
-                mSensorManager.addListener(mOnSensorChangedListener, sensor,
+                mSensorManager.registerListener(mOnSensorChangedListener, sensor,
                         CarSensorManager.SENSOR_RATE_NORMAL);
             }
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Car not connected or not supported", e);
+        } catch (Exception e) {
+            Log.e(TAG, "initSensors() exception caught: ", e);
         }
     }
 
@@ -188,23 +186,20 @@
             for (Integer i : supportedSensors) {
                 CarSensorEvent event = mEventMap.get(i);
                 switch (i) {
-                    case CarSensorManager.SENSOR_TYPE_COMPASS:
-                        summary.add(getCompassString(event));
-                        break;
                     case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
                         summary.add(getContext().getString(R.string.sensor_speed,
                                 getTimestamp(event),
-                                event == null ? mNaString : event.getCarSpeedData().carSpeed));
+                                event == null ? mNaString : event.getCarSpeedData(null).carSpeed));
                         break;
                     case CarSensorManager.SENSOR_TYPE_RPM:
                         summary.add(getContext().getString(R.string.sensor_rpm,
                                 getTimestamp(event),
-                                event == null ? mNaString : event.getRpmData().rpm));
+                                event == null ? mNaString : event.getRpmData(null).rpm));
                         break;
                     case CarSensorManager.SENSOR_TYPE_ODOMETER:
                         summary.add(getContext().getString(R.string.sensor_odometer,
                                 getTimestamp(event),
-                                event == null ? mNaString : event.getOdometerData().kms));
+                                event == null ? mNaString : event.getOdometerData(null).kms));
                         break;
                     case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
                         summary.add(getFuelLevel(event));
@@ -216,38 +211,23 @@
                         summary.add(getContext().getString(R.string.sensor_parking_brake,
                                 getTimestamp(event),
                                 event == null ? mNaString :
-                                event.getParkingBrakeData().isEngaged));
+                                event.getParkingBrakeData(null).isEngaged));
                         break;
                     case CarSensorManager.SENSOR_TYPE_GEAR:
                         summary.add(getContext().getString(R.string.sensor_gear,
                                 getTimestamp(event),
-                                event == null ? mNaString : event.getGearData().gear));
+                                event == null ? mNaString : event.getGearData(null).gear));
                         break;
                     case CarSensorManager.SENSOR_TYPE_NIGHT:
                         summary.add(getContext().getString(R.string.sensor_night,
                                 getTimestamp(event),
-                                event == null ? mNaString : event.getNightData().isNightMode));
-                        break;
-                    case CarSensorManager.SENSOR_TYPE_LOCATION:
-                        summary.add(getLocationString(event));
-                        break;
-                    case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
-                        String drivingStatus = mNaString;
-                        String binDrivingStatus = mNaString;
-                        if (event != null) {
-                            CarSensorEvent.DrivingStatusData drivingStatusData =
-                                    event.getDrivingStatusData();
-                            drivingStatus = String.valueOf(drivingStatusData.status);
-                            binDrivingStatus = Integer.toBinaryString(drivingStatusData.status);
-                        }
-                        summary.add(getContext().getString(R.string.sensor_driving_status,
-                                getTimestamp(event), drivingStatus, binDrivingStatus));
+                                event == null ? mNaString : event.getNightData(null).isNightMode));
                         break;
                     case CarSensorManager.SENSOR_TYPE_ENVIRONMENT:
                         String temperature = mNaString;
                         String pressure = mNaString;
                         if (event != null) {
-                            CarSensorEvent.EnvironmentData env = event.getEnvironmentData();
+                            CarSensorEvent.EnvironmentData env = event.getEnvironmentData(null);
                             temperature = Float.isNaN(env.temperature) ? temperature :
                                     String.valueOf(env.temperature);
                             pressure = Float.isNaN(env.pressure) ? pressure :
@@ -256,19 +236,10 @@
                         summary.add(getContext().getString(R.string.sensor_environment,
                                 getTimestamp(event), temperature, pressure));
                         break;
-                    case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
-                        summary.add(getAccelerometerString(event));
-                        break;
-                    case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE:
-                        summary.add(getGpsSatelliteString(event));
-                        break;
-                    case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
-                        summary.add(getGyroscopeString(event));
-                        break;
                     case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
                         if(event != null) {
                             CarSensorEvent.CarWheelTickDistanceData d =
-                                event.getCarWheelTickDistanceData();
+                                    event.getCarWheelTickDistanceData(null);
                             summary.add(getContext().getString(R.string.sensor_wheel_ticks,
                                 getTimestamp(event), d.sensorResetCount, d.frontLeftWheelDistanceMm,
                                 d.frontRightWheelDistanceMm, d.rearLeftWheelDistanceMm,
@@ -295,14 +266,15 @@
                     case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE:
                         summary.add(getContext().getString(R.string.sensor_abs_is_active,
                             getTimestamp(event), event == null ? mNaString :
-                            event.getCarAbsActiveData().absIsActive));
+                                    event.getCarAbsActiveData(null).absIsActive));
                         break;
 
                     case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE:
                         summary.add(
                             getContext().getString(R.string.sensor_traction_control_is_active,
                             getTimestamp(event), event == null ? mNaString :
-                            event.getCarTractionControlActiveData().tractionControlIsActive));
+                                    event.getCarTractionControlActiveData(null)
+                                    .tractionControlIsActive));
                         break;
                     case CarSensorManager.SENSOR_TYPE_EV_BATTERY_LEVEL:
                         summary.add(getEvBatteryLevel(event));
@@ -318,7 +290,7 @@
                         break;
                     default:
                         // Should never happen.
-                        Log.w(TAG, "Unrecognized event type: " + i);
+                        Log.w(TAG, "Unrecognized event type: " + toHexString(i));
                 }
             }
             summaryString = TextUtils.join("\n", summary);
@@ -338,93 +310,10 @@
         return mDateFormat.format(new Date(event.timestamp / (1000L * 1000L)));
     }
 
-    private String getCompassString(CarSensorEvent event) {
-        String bear = mNaString;
-        String pitch = mNaString;
-        String roll = mNaString;
-        if (event != null) {
-            CarSensorEvent.CompassData compass = event.getCompassData();
-            bear = Float.isNaN(compass.bearing) ? bear : String.valueOf(compass.bearing);
-            pitch = Float.isNaN(compass.pitch) ? pitch : String.valueOf(compass.pitch);
-            roll = Float.isNaN(compass.roll) ? roll : String.valueOf(compass.roll);
-        }
-        return getContext().getString(R.string.sensor_compass,
-                getTimestamp(event), bear, pitch, roll);
-    }
-
-    private String getGyroscopeString(CarSensorEvent event) {
-        String x = mNaString;
-        String y = mNaString;
-        String z = mNaString;
-        if (event != null) {
-            CarSensorEvent.GyroscopeData gyro = event.getGyroscopeData();
-            x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x);
-            y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y);
-            z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z);
-        }
-        return getContext().getString(R.string.sensor_gyroscope,
-                getTimestamp(event), x, y, z);
-    }
-
-    private String getAccelerometerString(CarSensorEvent event) {
-        String x = mNaString;
-        String y = mNaString;
-        String z = mNaString;
-        if (event != null) {
-            CarSensorEvent.AccelerometerData gyro = event.getAccelerometerData();
-            x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x);
-            y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y);
-            z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z);
-        }
-        return getContext().getString(R.string.sensor_accelerometer,
-                getTimestamp(event), x, y, z);
-    }
-
-    private String getLocationString(CarSensorEvent event) {
-        String lat = mNaString;
-        String lon = mNaString;
-        String accuracy = mNaString;
-        String alt = mNaString;
-        String speed = mNaString;
-        String bearing = mNaString;
-        if (event != null) {
-            Location location = event.getLocation(null);
-            lat = String.valueOf(location.getLatitude());
-            lon = String.valueOf(location.getLongitude());
-            accuracy = location.hasAccuracy() ? String.valueOf(location.getAccuracy()) : accuracy;
-            alt = location.hasAltitude() ? String.valueOf(location.getAltitude()) : alt;
-            speed = location.hasSpeed() ? String.valueOf(location.getSpeed()) : speed;
-            bearing = location.hasBearing() ? String.valueOf(location.getBearing()) : bearing;
-        }
-        return getContext().getString(R.string.sensor_location,
-                getTimestamp(event), lat, lon, accuracy, alt, speed, bearing);
-    }
-
-    private String getGpsSatelliteString(CarSensorEvent event) {
-        String inUse = mNaString;
-        String inView = mNaString;
-        String perSattelite = "";
-        if (event != null) {
-            CarSensorEvent.GpsSatelliteData gpsData = event.getGpsSatelliteData(true);
-            inUse = gpsData.numberInUse != -1 ? String.valueOf(gpsData.numberInUse) : inUse;
-            inView = gpsData.numberInView != -1 ? String.valueOf(gpsData.numberInView) : inView;
-            List<String> perSatteliteList = new ArrayList<>();
-            int num = gpsData.usedInFix.length;
-            for (int i=0; i<num; i++) {
-                perSatteliteList.add(getContext().getString(R.string.sensor_single_gps_satellite,
-                        i+1, gpsData.usedInFix[i], gpsData.prn[i], gpsData.snr[i],
-                        gpsData.azimuth[i], gpsData.elevation[i]));
-            }
-            perSattelite = TextUtils.join(", ", perSatteliteList);
-        }
-        return getContext().getString(R.string.sensor_gps,
-                getTimestamp(event), inView, inUse, perSattelite);
-    }
-
     private String getFuelLevel(CarSensorEvent event) {
         String fuelLevel = mNaString;
         if(event != null) {
-            fuelLevel = String.valueOf(event.getFuelLevelData().level);
+            fuelLevel = String.valueOf(event.getFuelLevelData(null).level);
         }
         return getContext().getString(R.string.sensor_fuel_level, getTimestamp(event), fuelLevel);
     }
@@ -432,7 +321,7 @@
     private String getFuelDoorOpen(CarSensorEvent event) {
         String fuelDoorOpen = mNaString;
         if(event != null) {
-            fuelDoorOpen = String.valueOf(event.getCarFuelDoorOpenData().fuelDoorIsOpen);
+            fuelDoorOpen = String.valueOf(event.getCarFuelDoorOpenData(null).fuelDoorIsOpen);
         }
         return getContext().getString(R.string.sensor_fuel_door_open, getTimestamp(event),
             fuelDoorOpen);
@@ -441,7 +330,7 @@
     private String getEvBatteryLevel(CarSensorEvent event) {
         String evBatteryLevel = mNaString;
         if(event != null) {
-            evBatteryLevel = String.valueOf(event.getCarEvBatteryLevelData().evBatteryLevel);
+            evBatteryLevel = String.valueOf(event.getCarEvBatteryLevelData(null).evBatteryLevel);
         }
         return getContext().getString(R.string.sensor_ev_battery_level, getTimestamp(event),
             evBatteryLevel);
@@ -451,7 +340,7 @@
         String evChargePortOpen = mNaString;
         if(event != null) {
             evChargePortOpen = String.valueOf(
-                event.getCarEvChargePortOpenData().evChargePortIsOpen);
+                event.getCarEvChargePortOpenData(null).evChargePortIsOpen);
         }
         return getContext().getString(R.string.sensor_ev_charge_port_is_open, getTimestamp(event),
             evChargePortOpen);
@@ -461,7 +350,7 @@
         String evChargePortConnected = mNaString;
         if(event != null) {
             evChargePortConnected = String.valueOf(
-                event.getCarEvChargePortConnectedData().evChargePortIsConnected);
+                event.getCarEvChargePortConnectedData(null).evChargePortIsConnected);
         }
         return getContext().getString(R.string.sensor_ev_charge_port_is_connected,
             getTimestamp(event), evChargePortConnected);
@@ -470,7 +359,7 @@
     private String getEvChargeRate(CarSensorEvent event) {
         String evChargeRate = mNaString;
         if(event != null) {
-            evChargeRate = String.valueOf(event.getCarEvBatteryChargeRateData().evChargeRate);
+            evChargeRate = String.valueOf(event.getCarEvBatteryChargeRateData(null).evChargeRate);
         }
         return getContext().getString(R.string.sensor_ev_charge_rate, getTimestamp(event),
             evChargeRate);
diff --git a/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java b/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
index e91abb7..ba82e6f 100644
--- a/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
+++ b/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
@@ -147,7 +147,7 @@
         mDrvStatus.requestLayout();
     }
 
-    private CarUxRestrictionsManager.onUxRestrictionsChangedListener mUxRChangeListener =
+    private CarUxRestrictionsManager.OnUxRestrictionsChangedListener mUxRChangeListener =
             this::updateUxRText;
 
 
diff --git a/tests/carservice_test/src/com/android/car/CarCabinManagerTest.java b/tests/carservice_test/src/com/android/car/CarCabinManagerTest.java
index 8e86b60..67725b9 100644
--- a/tests/carservice_test/src/com/android/car/CarCabinManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarCabinManagerTest.java
@@ -142,7 +142,9 @@
     @Test
     public void testEvent() throws Exception {
         mCarCabinManager.registerCallback(new EventListener());
-
+        // Wait for two events generated on registration
+        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
+        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
         // Inject a boolean event and wait for its callback in onPropertySet.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.DOOR_LOCK)
                 .setAreaId(VehicleAreaDoor.ROW_1_LEFT)
@@ -163,7 +165,6 @@
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .addIntValue(75)
                 .build();
-
         assertEquals(0, mAvailable.availablePermits());
         getMockedVehicleHal().injectEvent(v);
 
@@ -191,6 +192,15 @@
         @Override
         public synchronized void onPropertySubscribe(int property, float sampleRate) {
             Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
+            if (mMap.get(property) == null) {
+                Log.d(TAG, "onPropertySubscribe add dummy property: " + property);
+                VehiclePropValue dummyValue = VehiclePropValueBuilder.newBuilder(property)
+                        .setAreaId(VehicleAreaDoor.ROW_1_LEFT)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .addIntValue(1)
+                        .build();
+                mMap.put(property, dummyValue);
+            }
         }
 
         @Override
diff --git a/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java b/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
index 38fd4b1..31ed4b2 100644
--- a/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
+++ b/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
@@ -19,6 +19,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.car.Car;
@@ -82,13 +83,8 @@
         mCarUxRManager.registerListener(listener);
         // With no gear value available, driving state should be unknown
         listener.reset();
-        getMockedVehicleHal().injectEvent(
-                VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
-                        .addFloatValue(0.0f)
-                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
-                        .build());
-
         // Test Parked state and corresponding restrictions based on car_ux_restrictions_map.xml
+        Log.d(TAG, "Injecting gear park");
         getMockedVehicleHal().injectEvent(
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
                         .addIntValue(VehicleGear.GEAR_PARK)
@@ -98,9 +94,17 @@
         assertNotNull(drivingEvent);
         assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED);
 
+        Log.d(TAG, "Injecting speed 0");
+        getMockedVehicleHal().injectEvent(
+                VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
+                        .addFloatValue(0.0f)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .build());
+
         // Switch gear to drive.  Driving state changes to Idling but the UX restrictions don't
         // change between parked and idling.
         listener.reset();
+        Log.d(TAG, "Injecting gear drive");
         getMockedVehicleHal().injectEvent(
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
                         .addIntValue(VehicleGear.GEAR_DRIVE)
@@ -112,6 +116,7 @@
 
         // Test Moving state and corresponding restrictions based on car_ux_restrictions_map.xml
         listener.reset();
+        Log.d(TAG, "Injecting speed 30");
         getMockedVehicleHal().injectEvent(
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
                         .addFloatValue(30.0f)
@@ -128,6 +133,7 @@
 
         // Test Idling state and corresponding restrictions based on car_ux_restrictions_map.xml
         listener.reset();
+        Log.d(TAG, "Injecting speed 0");
         getMockedVehicleHal().injectEvent(
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
                         .addFloatValue(0.0f)
@@ -142,6 +148,83 @@
         assertThat(restrictions.getActiveRestrictions())
                 .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
 
+        // Apply Parking brake.  Supported gears is not provided in this test and hence
+        // Automatic transmission should be assumed and hence parking brake state should not
+        // make a difference to the driving state.
+        listener.reset();
+        Log.d(TAG, "Injecting parking brake on");
+        getMockedVehicleHal().injectEvent(
+                VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON)
+                        .setBooleanValue(true)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .build());
+        drivingEvent = listener.waitForDrivingStateChange();
+        assertNull(drivingEvent);
+
+        mCarDrivingStateManager.unregisterListener();
+        mCarUxRManager.unregisterListener();
+    }
+
+    @Test
+    public void testDrivingStateChangeForMalformedInputs()
+            throws CarNotConnectedException, InterruptedException {
+        CarDrivingStateEvent drivingEvent;
+        CarUxRestrictions restrictions;
+        DrivingStateListener listener = new DrivingStateListener();
+        mCarDrivingStateManager.registerListener(listener);
+        mCarUxRManager.registerListener(listener);
+
+        // Start with gear = park and speed = 0 to begin with a known state.
+        listener.reset();
+        Log.d(TAG, "Injecting gear park");
+        getMockedVehicleHal().injectEvent(
+                VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+                        .addIntValue(VehicleGear.GEAR_PARK)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .build());
+        drivingEvent = listener.waitForDrivingStateChange();
+        assertNotNull(drivingEvent);
+        assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED);
+
+        Log.d(TAG, "Injecting speed 0");
+        getMockedVehicleHal().injectEvent(
+                VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
+                        .addFloatValue(0.0f)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .build());
+
+        // Inject an invalid gear.  Since speed is still valid, idling will be the expected
+        // driving state
+        listener.reset();
+        Log.d(TAG, "Injecting gear -1");
+        getMockedVehicleHal().injectEvent(
+                VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+                        .addIntValue(-1)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .build());
+        drivingEvent = listener.waitForDrivingStateChange();
+        assertNotNull(drivingEvent);
+        assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING);
+
+        // Now, send in an invalid speed value as well, now the driving state will be unknown and
+        // the UX restrictions will change to fully restricted.
+        listener.reset();
+        Log.d(TAG, "Injecting speed -1");
+        getMockedVehicleHal().injectEvent(
+                VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
+                        .addFloatValue(-1.0f)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .build());
+        drivingEvent = listener.waitForDrivingStateChange();
+        assertNotNull(drivingEvent);
+        assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
+        restrictions = listener.waitForUxRestrictionsChange();
+        assertNotNull(restrictions);
+        assertTrue(restrictions.isRequiresDistractionOptimization());
+        assertThat(restrictions.getActiveRestrictions())
+                .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
+        mCarDrivingStateManager.unregisterListener();
+        mCarUxRManager.unregisterListener();
     }
 
     /**
@@ -149,7 +232,7 @@
      */
     private class DrivingStateListener implements
             CarDrivingStateManager.CarDrivingStateEventListener,
-            CarUxRestrictionsManager.onUxRestrictionsChangedListener {
+            CarUxRestrictionsManager.OnUxRestrictionsChangedListener {
         private final Object mDrivingStateLock = new Object();
         @GuardedBy("mDrivingStateLock")
         private CarDrivingStateEvent mLastEvent = null;
diff --git a/tests/carservice_test/src/com/android/car/CarHvacManagerTest.java b/tests/carservice_test/src/com/android/car/CarHvacManagerTest.java
index 6029637..1493fe9 100644
--- a/tests/carservice_test/src/com/android/car/CarHvacManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarHvacManagerTest.java
@@ -142,7 +142,7 @@
         MutableInt propertyIdReceived = new MutableInt(0);
         MutableInt areaIdReceived = new MutableInt(0);
 
-        mCarHvacManager.registerCallback(new CarHvacEventCallback() {
+        mCarHvacManager.registerCallback(new CarHvacEventCallback()  {
             @Override
             public void onChangeEvent(CarPropertyValue value) {
 
@@ -166,6 +166,11 @@
     @Test
     public void testEvent() throws Exception {
         mCarHvacManager.registerCallback(new EventListener());
+        // Wait for events generated on registration
+        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
+        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
+        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
+        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
 
         // Inject a boolean event and wait for its callback in onPropertySet.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER)
@@ -225,6 +230,16 @@
         @Override
         public synchronized void onPropertySubscribe(int property, float sampleRate) {
             Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
+            if (mMap.get(property) == null) {
+                Log.d(TAG, "onPropertySubscribe add dummy property: " + property);
+                VehiclePropValue dummyValue = VehiclePropValueBuilder.newBuilder(property)
+                        .setAreaId(0)
+                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                        .addIntValue(1)
+                        .addFloatValue(1)
+                        .build();
+                mMap.put(property, dummyValue);
+            }
         }
 
         @Override
diff --git a/tests/carservice_test/src/com/android/car/CarSensorManagerTest.java b/tests/carservice_test/src/com/android/car/CarSensorManagerTest.java
index 7f58063..e5b3566 100644
--- a/tests/carservice_test/src/com/android/car/CarSensorManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarSensorManagerTest.java
@@ -154,6 +154,8 @@
         CarSensorEvent event;
         CarSensorEvent.NightData data = null;
 
+        // Clear event generated by registerListener()
+        listener.waitForSensorChange();
         listener.reset();
 
         // Set the value TRUE and wait for the event to arrive
@@ -161,7 +163,7 @@
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.NIGHT_MODE)
                         .setBooleanValue(true)
                         .setTimestamp(1L)
-                        .build());
+                        .build(), true);
         assertTrue(listener.waitForSensorChange(1L));
 
         // Ensure we got the expected event
@@ -185,7 +187,7 @@
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.NIGHT_MODE)
                         .setTimestamp(1001)
                         .setBooleanValue(false)
-                        .build());
+                        .build(), true);
         assertTrue(listener.waitForSensorChange(1001));
 
         // Ensure we got the expected event
@@ -211,7 +213,7 @@
                 .setTimestamp(2001)
                 .setBooleanValue(true)
                 .build();
-        getMockedVehicleHal().injectEvent(value);
+        getMockedVehicleHal().injectEvent(value, true);
 
         // Ensure we did not get a callback (should timeout)
         Log.i(TAG, "waiting for unexpected callback -- should timeout.");
@@ -238,7 +240,8 @@
         SensorListener listener = new SensorListener();
         mCarSensorManager.registerListener(listener, CarSensorManager.SENSOR_TYPE_IGNITION_STATE,
                 CarSensorManager.SENSOR_RATE_NORMAL);
-
+        // Clear event generated by registerListener()
+        listener.waitForSensorChange();
 
         // Mapping of HAL -> Manager ignition states.
         int[] ignitionStates = new int[] {
@@ -265,7 +268,7 @@
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.IGNITION_STATE)
                         .addIntValue(halIgnitionState)
                         .setTimestamp(time)
-                        .build());
+                        .build(), true);
         assertTrue(listener.waitForSensorChange(time));
 
         CarSensorEvent eventReceived = listener.getLastEvent();
@@ -274,29 +277,15 @@
     }
 
     @Test
-    public void testIgnitionEvents_Bad() throws Exception {
-        SensorListener listener = new SensorListener();
-        mCarSensorManager.registerListener(listener, CarSensorManager.SENSOR_TYPE_IGNITION_STATE,
-                CarSensorManager.SENSOR_RATE_NORMAL);
-
-        listener.reset();
-        long time = SystemClock.elapsedRealtimeNanos();
-        getMockedVehicleHal().injectEvent(
-                VehiclePropValueBuilder.newBuilder(VehicleProperty.IGNITION_STATE)
-                        .addIntValue(0xdeadbeef)
-                        .setTimestamp(time)
-                        .build());
-
-        // Make sure invalid events are never propagated to clients.
-        assertFalse(listener.waitForSensorChange(time));
-    }
-
-    @Test
     public void testGear() throws Exception {
         SensorListener listener = new SensorListener();
+
         mCarSensorManager.registerListener(listener, CarSensorManager.SENSOR_TYPE_GEAR,
                 CarSensorManager.SENSOR_RATE_NORMAL);
 
+        // Clear event generated by registerListener()
+        listener.waitForSensorChange();
+
         // Mapping of HAL -> Manager gear selection states.
         int[] gears = new int[] {
                 VehicleGear.GEAR_PARK, CarSensorEvent.GEAR_PARK,
@@ -317,19 +306,6 @@
         for (int i = 0; i < gears.length; i += 2) {
             injectGearEventAndAssert(listener, gears[i], gears[i + 1]);
         }
-
-        // invalid input should not be forwarded
-        long time = SystemClock.elapsedRealtimeNanos();
-        getMockedVehicleHal().injectEvent(
-                VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
-                        .addIntValue(0xdeadbeef)
-                        .setTimestamp(time)
-                        .build());
-        assertFalse(listener.waitForSensorChange(time));
-        CarSensorEvent event = mCarSensorManager.getLatestSensorEvent(
-                CarSensorManager.SENSOR_TYPE_GEAR);
-        assertNotNull(event);  // Still holds an old event.
-        assertEquals(CarSensorEvent.GEAR_NINTH, event.intValues[0]);
     }
 
     private void injectGearEventAndAssert(SensorListener listener, int halValue,
@@ -340,7 +316,7 @@
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
                         .addIntValue(halValue)
                         .setTimestamp(time)
-                        .build());
+                        .build(), true);
         assertTrue(listener.waitForSensorChange(time));
         CarSensorEvent event = mCarSensorManager.getLatestSensorEvent(
                 CarSensorManager.SENSOR_TYPE_GEAR);
@@ -375,17 +351,20 @@
         VehiclePropValue value;
         CarSensorEvent event;
 
+        // Clear event generated by registerListener()
+        listener1.waitForSensorChange();
+        listener2.waitForSensorChange();
+        listener3.waitForSensorChange();
         listener1.reset();
         listener2.reset();
         listener3.reset();
 
         // Set the value TRUE and wait for the event to arrive
         value = VehiclePropValueBuilder.newBuilder(VehicleProperty.NIGHT_MODE)
-                    .setTimestamp(42L)
-                    .setBooleanValue(true)
-                    .build();
-
-        getMockedVehicleHal().injectEvent(value);
+                .setTimestamp(42L)
+                .setBooleanValue(true)
+                .build();
+        getMockedVehicleHal().injectEvent(value, true);
 
         assertTrue(listener1.waitForSensorChange(42L));
         assertTrue(listener2.waitForSensorChange(42L));
@@ -423,7 +402,7 @@
                 .setTimestamp(1001)
                 .setBooleanValue(false)
                 .build();
-        getMockedVehicleHal().injectEvent(value);
+        getMockedVehicleHal().injectEvent(value, true);
         assertTrue(listener1.waitForSensorChange(1001));
         assertTrue(listener2.waitForSensorChange(1001));
         assertTrue(listener3.waitForSensorChange(1001));
@@ -443,7 +422,6 @@
         assertFalse("Unexpected value", data.isNightMode);
 
         data = listener3.getLastEvent().getNightData(data);
-        listener3.reset();
         assertEquals("Unexpected event timestamp", 1001, data.timestamp);
         assertFalse("Unexpected value", data.isNightMode);
 
@@ -453,13 +431,19 @@
         assertFalse(data.isNightMode);
 
         Log.d(TAG, "Unregistering listener3");
+        listener1.reset();
+        listener2.reset();
+        listener3.reset();
         mCarSensorManager.unregisterListener(listener3);
-
         Log.d(TAG, "Rate changed - expect sensor restart and change event sent.");
+        value = VehiclePropValueBuilder.newBuilder(VehicleProperty.NIGHT_MODE)
+                .setTimestamp(1002)
+                .setBooleanValue(false)
+                .build();
+        getMockedVehicleHal().injectEvent(value, true);
         assertTrue(listener1.waitForSensorChange());
         assertTrue(listener2.waitForSensorChange());
         assertFalse(listener3.waitForSensorChange());
-
         listener1.reset();
         listener2.reset();
         listener3.reset();
@@ -468,11 +452,10 @@
                 .setTimestamp()
                 .setBooleanValue(true)
                 .build();
-        getMockedVehicleHal().injectEvent(value);
+        getMockedVehicleHal().injectEvent(value, true);
 
         assertTrue(listener1.waitForSensorChange());
         assertTrue(listener2.waitForSensorChange());
-
         listener1.reset();
         listener2.reset();
 
@@ -481,7 +464,7 @@
         assertFalse(listener3.waitForSensorChange());
 
         Log.d(TAG, "Unregistering listener2");
-        mCarSensorManager.unregisterListener(listener3);
+        mCarSensorManager.unregisterListener(listener2);
 
         Log.d(TAG, "Rate did nor change - dont expect sensor restart and change event sent.");
         assertFalse(listener1.waitForSensorChange());
diff --git a/tests/carservice_test/src/com/android/car/VmsHalServiceSubscriptionEventTest.java b/tests/carservice_test/src/com/android/car/VmsHalServiceSubscriptionEventTest.java
index 9f46ffe..82c9361 100644
--- a/tests/carservice_test/src/com/android/car/VmsHalServiceSubscriptionEventTest.java
+++ b/tests/carservice_test/src/com/android/car/VmsHalServiceSubscriptionEventTest.java
@@ -61,7 +61,7 @@
         addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
                 .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
                 .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_NONE, 0, 0);
+                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 0, 0);
     }
 
     @Override
diff --git a/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
index a9f9bef..d8cbe7b 100644
--- a/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
@@ -68,7 +68,7 @@
         addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
                 .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
                 .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_NONE, 0, 0);
+                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 0, 0);
     }
 
     @Override
diff --git a/tests/carservice_test/src/com/android/car/VmsPublisherPermissionsTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherPermissionsTest.java
index 1650f48..a4d094a 100644
--- a/tests/carservice_test/src/com/android/car/VmsPublisherPermissionsTest.java
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherPermissionsTest.java
@@ -16,7 +16,6 @@
 
 package com.android.car;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.car.VehicleAreaType;
@@ -59,7 +58,7 @@
         addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
                 .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
                 .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_NONE, 0, 0);
+                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 0, 0);
     }
 
     @Override
diff --git a/tests/carservice_test/src/com/android/car/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherSubscriberTest.java
index 30035b1..1ef35f7 100644
--- a/tests/carservice_test/src/com/android/car/VmsPublisherSubscriberTest.java
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherSubscriberTest.java
@@ -100,7 +100,7 @@
         addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
                 .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
                 .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_NONE, 0, 0);
+                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 0, 0);
     }
 
     @Override
diff --git a/tests/carservice_test/src/com/android/car/VmsSubscriberManagerTest.java b/tests/carservice_test/src/com/android/car/VmsSubscriberManagerTest.java
index 5fe153b..643ffff 100644
--- a/tests/carservice_test/src/com/android/car/VmsSubscriberManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/VmsSubscriberManagerTest.java
@@ -37,8 +37,13 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
+
 import com.android.car.vehiclehal.VehiclePropValueBuilder;
 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -46,8 +51,6 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -110,7 +113,7 @@
         addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
                 .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
                 .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_NONE, 0, 0);
+                .addAreaConfig(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 0, 0);
     }
 
     @Override
@@ -132,7 +135,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         v.value.int32Values.add(VmsMessageType.DATA); // MessageType
@@ -162,7 +165,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         v.value.int32Values.add(VmsMessageType.DATA); // MessageType
@@ -190,7 +193,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         v.value.int32Values.add(VmsMessageType.DATA); // MessageType
@@ -221,7 +224,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         v.value.int32Values.add(VmsMessageType.DATA); // MessageType
@@ -248,7 +251,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         v.value.int32Values.add(VmsMessageType.DATA); // MessageType
@@ -276,7 +279,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         v.value.int32Values.add(VmsMessageType.DATA); // MessageType
@@ -303,7 +306,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         v.value.int32Values.add(VmsMessageType.DATA); // MessageType
@@ -332,7 +335,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         //
@@ -378,7 +381,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         //
@@ -425,7 +428,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         //
@@ -465,7 +468,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         //
@@ -559,7 +562,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         //
@@ -637,7 +640,7 @@
 
         // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
         VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
                 .setTimestamp(SystemClock.elapsedRealtimeNanos())
                 .build();
         //
diff --git a/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java b/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java
index 591b610..a180886 100644
--- a/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java
@@ -50,6 +50,7 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -72,8 +73,7 @@
     private Handler mMainHandler;
     private Context mMockContext;
     // Mock of Services that the policy interacts with
-    private CarCabinService mMockCarCabinService;
-    private CarSensorService mMockCarSensorService;
+    private CarPropertyService mMockCarPropertyService;
     private CarUxRestrictionsManagerService mMockCarUxRService;
     private CarBluetoothUserService mMockBluetoothUserService;
     private PerUserCarServiceHelper mMockPerUserCarServiceHelper;
@@ -223,7 +223,9 @@
                 0, false);
         CarPropertyEvent event = new CarPropertyEvent(
                 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
-        mCabinEventListener.onEvent(event);
+        List<CarPropertyEvent> events = new ArrayList<>();
+        events.add(event);
+        mCabinEventListener.onEvent(events);
     }
 
     /**
@@ -231,8 +233,7 @@
      */
     private void makeMockServices() {
         mMockContext = mock(Context.class);
-        mMockCarCabinService = mock(CarCabinService.class);
-        mMockCarSensorService = mock(CarSensorService.class);
+        mMockCarPropertyService = mock(CarPropertyService.class);
         mMockCarUxRService = mock(CarUxRestrictionsManagerService.class);
         mMockPerUserCarServiceHelper = mock(PerUserCarServiceHelper.class);
         mMockPerUserCarService = mock(ICarUserService.class);
@@ -296,7 +297,7 @@
                 .thenReturn(mMockBluetoothUserService);
 
         mBluetoothDeviceConnectionPolicyTest = BluetoothDeviceConnectionPolicy.create(mMockContext,
-                mMockCarCabinService, mMockCarSensorService, mMockPerUserCarServiceHelper,
+                mMockCarPropertyService, mMockPerUserCarServiceHelper,
                 mMockCarUxRService, mMockCarBluetoothService);
         mBluetoothDeviceConnectionPolicyTest.setAllowReadWriteToSettings(false);
         mBluetoothDeviceConnectionPolicyTest.init();
diff --git a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
index 11b3c5c..8ac7d9b 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
@@ -26,9 +26,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.car.hardware.CarPropertyValue;
 import android.car.hardware.CarSensorEvent;
 import android.car.hardware.CarSensorManager;
-import android.car.hardware.ICarSensorEventListener;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarPropertyEventListener;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -81,7 +83,7 @@
     private CountDownLatch mLatch;
     @Mock private Context mMockContext;
     @Mock private LocationManager mMockLocationManager;
-    @Mock private CarSensorService mMockCarSensorService;
+    @Mock private CarPropertyService mMockCarPropertyService;
     @Mock private CarPowerManagementService mMockCarPowerManagementService;
 
     /**
@@ -93,7 +95,7 @@
         mContext = InstrumentationRegistry.getTargetContext();
         mLatch = new CountDownLatch(1);
         mCarLocationService = new CarLocationService(mMockContext, mMockCarPowerManagementService,
-                mMockCarSensorService) {
+                mMockCarPropertyService) {
             @Override
             void asyncOperation(Runnable operation) {
                 super.asyncOperation(() -> {
@@ -147,8 +149,8 @@
         assertTrue(ArrayUtils.contains(actions, Intent.ACTION_LOCKED_BOOT_COMPLETED));
         assertTrue(ArrayUtils.contains(actions, LocationManager.MODE_CHANGED_ACTION));
         assertTrue(ArrayUtils.contains(actions, LocationManager.GPS_ENABLED_CHANGE_ACTION));
-        verify(mMockCarSensorService).registerOrUpdateSensorListener(
-                eq(CarSensorManager.SENSOR_TYPE_IGNITION_STATE), eq(0), any());
+        verify(mMockCarPropertyService).registerListener(
+                eq(CarSensorManager.SENSOR_TYPE_IGNITION_STATE), eq(0.0f), any());
     }
 
     /**
@@ -158,7 +160,7 @@
     public void testUnregistersEventReceivers() {
         mCarLocationService.release();
         verify(mMockContext).unregisterReceiver(mCarLocationService);
-        verify(mMockCarSensorService).unregisterSensorListener(
+        verify(mMockCarPropertyService).unregisterListener(
                 eq(CarSensorManager.SENSOR_TYPE_IGNITION_STATE), any());
     }
 
@@ -423,15 +425,17 @@
 
     private void sendIgnitionOffEvent() throws RemoteException {
         mCarLocationService.init();
-        ArgumentCaptor<ICarSensorEventListener> argument =
-                ArgumentCaptor.forClass(ICarSensorEventListener.class);
-        verify(mMockCarSensorService).registerOrUpdateSensorListener(
-                eq(CarSensorManager.SENSOR_TYPE_IGNITION_STATE), eq(0), argument.capture());
-        ICarSensorEventListener carSensorEventListener = argument.getValue();
-        CarSensorEvent ignitionOff = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_IGNITION_STATE,
-                System.currentTimeMillis(), 0, 1, 0);
-        ignitionOff.intValues[0] = CarSensorEvent.IGNITION_STATE_OFF;
-        List<CarSensorEvent> events = new ArrayList<>(Arrays.asList(ignitionOff));
-        carSensorEventListener.onSensorChanged(events);
+        ArgumentCaptor<ICarPropertyEventListener> argument =
+                ArgumentCaptor.forClass(ICarPropertyEventListener.class);
+        verify(mMockCarPropertyService).registerListener(
+                eq(CarSensorManager.SENSOR_TYPE_IGNITION_STATE), eq(0.0f), argument.capture());
+        ICarPropertyEventListener carPropertyEventListener = argument.getValue();
+        int intValues = CarSensorEvent.IGNITION_STATE_OFF;
+        CarPropertyValue ignitionOff = new CarPropertyValue(
+                CarSensorManager.SENSOR_TYPE_IGNITION_STATE, 0, 0,
+                System.currentTimeMillis(), intValues);
+        CarPropertyEvent event = new CarPropertyEvent(0, ignitionOff);
+        List<CarPropertyEvent> events = new ArrayList<>(Arrays.asList(event));
+        carPropertyEventListener.onEvent(events);
     }
 }
diff --git a/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java b/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java
index 3f556d5..405c014 100644
--- a/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java
+++ b/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java
@@ -35,7 +35,7 @@
 
     public MockedPowerHalService(boolean isPowerStateSupported, boolean isDeepSleepAllowed,
             boolean isTimedWakeupAllowed) {
-        super(new VehicleHal(null, null, null, null, null, null, null));
+        super(new VehicleHal(null, null, null, null));
         mIsPowerStateSupported = isPowerStateSupported;
         mIsDeepSleepAllowed = isDeepSleepAllowed;
         mIsTimedWakeupAllowed = isTimedWakeupAllowed;
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java
index 3b12d87..08544bd 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java
@@ -20,6 +20,7 @@
 import static java.lang.Integer.toHexString;
 
 import android.car.Car;
+import android.car.hardware.CarPropertyConfig;
 import android.car.hardware.CarPropertyValue;
 import android.car.hardware.hvac.CarHvacManager;
 import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
@@ -58,6 +59,8 @@
     // are in CONTINUOUS mode. They should be omitted when testing ON_CHANGE properties.
     private static final Set<Integer> CONTINUOUS_HVAC_PROPS;
 
+    private Integer mNumPropEventsToSkip;
+
     static {
         CONTINUOUS_HVAC_PROPS = new ArraySet<>();
         CONTINUOUS_HVAC_PROPS.add(VehicleProperty.ENV_OUTSIDE_TEMPERATURE);
@@ -75,7 +78,13 @@
         public void onChangeEvent(CarPropertyValue carPropertyValue) {
             VehiclePropValue event = Utils.fromHvacPropertyValue(carPropertyValue);
             if (!CONTINUOUS_HVAC_PROPS.contains(event.prop)) {
-                mVerifier.verify(Utils.fromHvacPropertyValue(carPropertyValue));
+                synchronized (mNumPropEventsToSkip) {
+                    if (mNumPropEventsToSkip == 0) {
+                        mVerifier.verify(Utils.fromHvacPropertyValue(carPropertyValue));
+                    } else {
+                        mNumPropEventsToSkip--;
+                    }
+                }
             }
         }
 
@@ -85,6 +94,19 @@
         }
     }
 
+    private Integer calculateNumPropEventsToSkip(CarHvacManager hvacMgr) {
+        int numToSkip = 0;
+        try {
+            for (CarPropertyConfig c: hvacMgr.getPropertyList()) {
+                if (!CONTINUOUS_HVAC_PROPS.contains(c.getPropertyId())) {
+                    numToSkip += c.getAreaCount();
+                }
+            }
+        } catch (Exception e) {
+            Log.d(TAG, "Unhandled exception thrown: ", e);
+        }
+        return Integer.valueOf(numToSkip);
+    }
     @Test
     public void testHvacOperations() throws Exception {
         Log.d(TAG, "Prepare HVAC test data");
@@ -93,6 +115,8 @@
 
         Log.d(TAG, "Start listening to the HAL");
         CarHvacManager hvacMgr = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+        // Calculate number of properties to skip due to registration event
+        mNumPropEventsToSkip = calculateNumPropEventsToSkip(hvacMgr);
         CarHvacEventCallback callback = new CarHvacOnChangeEventListener(verifier);
         hvacMgr.registerCallback(callback);
 
@@ -106,7 +130,6 @@
 
         Log.d(TAG, "Send command to VHAL to stop generation");
         hvacGenerator.stop();
-
         hvacMgr.unregisterCallback(callback);
 
         assertTrue("Detected mismatched events: " + verifier.getResultString(),
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
index a248269..477c2c5 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
@@ -16,11 +16,12 @@
 
 package com.android.car.vehiclehal.test;
 
-import static java.lang.Integer.toHexString;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.fail;
 
+import static java.lang.Integer.toHexString;
+
 import android.hardware.automotive.vehicle.V2_0.IVehicle;
 import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
 import android.hardware.automotive.vehicle.V2_0.StatusCode;
@@ -92,11 +93,20 @@
         }
     }
 
-    public synchronized void injectEvent(VehiclePropValue value) {
+    public synchronized void injectEvent(VehiclePropValue value, boolean setProperty) {
         List<IVehicleCallback> callbacks = mSubscribers.get(value.prop);
         assertNotNull("Injecting event failed for property: " + value.prop
                         + ". No listeners found", callbacks);
-        for (IVehicleCallback callback : callbacks) {
+
+        if (setProperty) {
+            // Update property if requested
+            VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(value.prop);
+            if (handler != null) {
+                handler.onPropertySet(value);
+            }
+        }
+
+        for (IVehicleCallback callback: callbacks) {
             try {
                 callback.onPropertyEvent(Lists.newArrayList(value));
             } catch (RemoteException e) {
@@ -106,6 +116,10 @@
         }
     }
 
+    public synchronized void injectEvent(VehiclePropValue value) {
+        injectEvent(value, false);
+    }
+
     public synchronized void injectError(int errorCode, int propertyId, int areaId) {
         List<IVehicleCallback> callbacks = mSubscribers.get(propertyId);
         assertNotNull("Injecting error failed for property: " + propertyId
@@ -175,6 +189,14 @@
                 subscribers = new ArrayList<>();
                 mSubscribers.put(opt.propId, subscribers);
                 notifyAll();
+            } else {
+                for (IVehicleCallback s : subscribers) {
+                    if (callback.asBinder() == s.asBinder()) {
+                        // Remove callback that was registered previously for this property
+                        subscribers.remove(callback);
+                        break;
+                    }
+                }
             }
             subscribers.add(callback);
         }