Create CarPropertyService and CarPropertyManager

CarPropertyService replaces Cabin, Hvac, Info, Sensor,
and VendorExtension services.

Bug: 78782959
Bug: 36649684
Bug: 68056035
Test: runtest -x packages/services/Car/tests/android_car_api_test
      runtest -x packages/services/Car/tests/carservice_unit_test

Change-Id: Ic0a94805f83cc0222fb2bcf9674b6031bc050986
(cherry picked from commit c9ee6cda8a5ed73d82a0cdbd0e3154d76ec3db01)
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/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..7f0eeea 100644
--- a/service/src/com/android/car/CarDrivingStateService.java
+++ b/service/src/com/android/car/CarDrivingStateService.java
@@ -21,10 +21,13 @@
 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 +39,76 @@
 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
+    private final List<DrivingStateClient> mDrivingStateClients = new ArrayList<>();
+    // Array of properties that the service needs to listen to from CarPropertyService 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 static final int[] REQUIRED_PROPERTIES = {
+            VehicleProperty.PERF_VEHICLE_SPEED,
+            VehicleProperty.GEAR_SELECTION};
     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;
 
-    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()) {
+        if (!checkPropertySupport()) {
             Log.e(TAG, "init failure.  Driving state will always be fully restrictive");
             return;
         }
-        subscribeToSensors();
+        subscribeToProperties();
     }
 
     @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 +116,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 +154,7 @@
                 Log.e(TAG, "Cannot link death recipient to binder " + e);
                 return;
             }
-            mDivingStateClients.add(client);
+            mDrivingStateClients.add(client);
         }
     }
 
@@ -158,7 +170,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 +198,7 @@
             return;
         }
         listener.asBinder().unlinkToDeath(client, 0);
-        mDivingStateClients.remove(client);
+        mDrivingStateClients.remove(client);
     }
 
     /**
@@ -222,7 +234,7 @@
             }
             listenerBinder.unlinkToDeath(this, 0);
             synchronized (CarDrivingStateService.this) {
-                mDivingStateClients.remove(this);
+                mDrivingStateClients.remove(this);
             }
         }
 
@@ -265,29 +277,60 @@
     }
 
     /**
-     * {@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:
+                        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;
+                    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,22 +338,21 @@
                     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;
         }
     }
@@ -333,42 +375,6 @@
      */
     @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;
-            }
-        }
-
-        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:
             1. If gear == parked, then Driving State is parked.
@@ -378,29 +384,22 @@
                 2c. if speed unavailable, then driving state is unknown
             This logic needs to be tested and iterated on.  Tracked in b/69859926
          */
-        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 (DBG) {
+            Log.d(TAG, "Last known Gear:" + mLastGear + " Last known speed:" + mLastSpeed);
         }
-        return drivingState;
-    }
+        if (mLastGearTimestamp == NOT_RECEIVED) {
+            return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
+        } else if (mLastGear == VehicleGear.GEAR_PARK) {
+            return CarDrivingStateEvent.DRIVING_STATE_PARKED;
+        }
 
-    private boolean isSpeedZero(CarSensorEvent event) {
-        return event.floatValues[0] == 0f;
-    }
-
-    private boolean isGearInParking(CarSensorEvent event) {
-        int gear = event.intValues[0];
-        return gear == CarSensorEvent.GEAR_PARK;
+        if (mLastSpeedTimestamp == NOT_RECEIVED) {
+            return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
+        } else if (mLastSpeed == 0f) {
+            return CarDrivingStateEvent.DRIVING_STATE_IDLING;
+        } else {
+            return CarDrivingStateEvent.DRIVING_STATE_MOVING;
+        }
     }
 
     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..449a485 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,10 @@
     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 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,13 +64,13 @@
 
 
     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);
@@ -88,9 +90,9 @@
         // 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);
     }
 
     @Override
@@ -280,10 +282,10 @@
             return;
         }
         int drivingState = event.eventValue;
-        CarSensorEvent speed = mCarSensorService.getLatestSensorEvent(
-                CarSensorManager.SENSOR_TYPE_CAR_SPEED);
-        if (speed != null) {
-            mCurrentMovingSpeed = speed.floatValues[0];
+        CarPropertyValue value = mCarPropertyService.getProperty(VehicleProperty.PERF_VEHICLE_SPEED,
+                0);
+        if (value != null) {
+            mCurrentMovingSpeed = (Float) value.getValue();
         } 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 +305,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());
                         }
                     }
                 }
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 9db071b..6c2ba36 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -61,21 +61,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;
@@ -110,10 +106,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);
@@ -121,22 +117,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());
@@ -149,23 +140,19 @@
         mAllServices = new CarServiceBase[] {
             mSystemActivityMonitoringService,
             mCarPowerManagementService,
-            mCarSensorService,
+            mCarPropertyService,
             mCarDrivingStateService,
             mCarUXRestrictionsService,
             mCarPackageManagerService,
             mCarInputService,
             mCarLocationService,
             mGarageModeService,
-            mCarInfoService,
             mAppFocusService,
             mCarAudioService,
-            mCarCabinService,
-            mCarHvacService,
             mCarNightService,
             mInstrumentClusterService,
             mCarProjectionService,
             mSystemStateControllerService,
-            mCarVendorExtensionService,
             mCarBluetoothService,
             mCarDiagnosticService,
             mPerUserCarServiceHelper,
@@ -220,26 +207,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 =
@@ -251,9 +235,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;
@@ -306,10 +287,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);
     }
@@ -318,10 +295,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);
     }
@@ -330,10 +303,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,
@@ -358,6 +327,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..f12da3a 100644
--- a/service/src/com/android/car/hal/CarPropertyUtils.java
+++ b/service/src/com/android/car/hal/CarPropertyUtils.java
@@ -51,11 +51,15 @@
         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()];
+            int i = 0;
+            for (int val : v.int32Values) {
+                values[i] = val == 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);
@@ -76,15 +80,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[]) {
@@ -203,10 +215,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