More details in Hal with sensor pipeline improvements for batching

- updated CarSensorEvent to use int array instead of byte as most
  use cases involve int value manipulations.
- removed unused sensor types in CarSensorManager. They can brought back
  later as necessary.
- CarSensorManager subscription works for several sensors.

bug: 22701368

Change-Id: I2fb8901fe5266693aeae5f1b3f2b3adb75056a93
(cherry picked from commit d68bc574a27aee0fb6750ed301d247645d7bbb1e)
diff --git a/service/src/com/android/car/BootReceiver.java b/service/src/com/android/car/BootReceiver.java
index 5bfa96c..c13a1f3 100644
--- a/service/src/com/android/car/BootReceiver.java
+++ b/service/src/com/android/car/BootReceiver.java
@@ -23,7 +23,7 @@
 import android.content.Intent;
 import android.util.Log;
 
-import com.android.car.hal.Hal;
+import com.android.car.hal.VehicleHal;
 
 
 /**
@@ -34,7 +34,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         Log.w(CarLog.TAG_SERVICE, "Starting...");
-        Hal hal = Hal.getInstance(context.getApplicationContext());
+        VehicleHal hal = VehicleHal.getInstance(context.getApplicationContext());
         Intent carServiceintent = new Intent();
         carServiceintent.setPackage(context.getPackageName());
         carServiceintent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index f8bdff9..bd6d718 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -20,4 +20,5 @@
 
     public static final String TAG_SERVICE = "CAR.SERVICE";
     public static final String TAG_SENSOR = "CAR.SENSOR";
+    public static final String TAG_HAL = "CAR.HAL";
 }
diff --git a/service/src/com/android/car/CarSensorEventFactory.java b/service/src/com/android/car/CarSensorEventFactory.java
new file mode 100644
index 0000000..35c29fa
--- /dev/null
+++ b/service/src/com/android/car/CarSensorEventFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.support.car.CarSensorEvent;
+
+//TODO add memory pool and recycling
+public class CarSensorEventFactory {
+
+    public static CarSensorEvent createBooleanEvent(int sensorType, long timeStampNs,
+            boolean value) {
+        CarSensorEvent event = new CarSensorEvent(sensorType, timeStampNs, 0, 1);
+        event.intValues[0] = value ? 1 : 0;
+        return event;
+    }
+
+    public static CarSensorEvent createIntEvent(int sensorType, long timeStampNs, int value) {
+        CarSensorEvent event = new CarSensorEvent(sensorType, timeStampNs, 0, 1);
+        event.intValues[0] = value;
+        return event;
+    }
+
+    public static CarSensorEvent createFloatEvent(int sensorType, long timeStampNs, float value) {
+        CarSensorEvent event = new CarSensorEvent(sensorType, timeStampNs, 1, 0);
+        event.floatValues[0] = value;
+        return event;
+    }
+
+    public static void returnToPool(CarSensorEvent event) {
+        //TODO
+    }
+}
diff --git a/service/src/com/android/car/CarSensorService.java b/service/src/com/android/car/CarSensorService.java
index cd899af..d28f838 100644
--- a/service/src/com/android/car/CarSensorService.java
+++ b/service/src/com/android/car/CarSensorService.java
@@ -19,8 +19,13 @@
 import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.SensorEvent;
 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;
@@ -29,37 +34,61 @@
 import android.support.car.CarSensorManager;
 import android.support.car.ICarSensor;
 import android.support.car.ICarSensorEventListener;
+import android.support.car.CarSensorManager.CarSensorEventListener;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
-import com.android.car.hal.Hal;
-import com.android.car.hal.SensorHal;
-import com.android.car.hal.SensorHalBase;
-import com.android.car.hal.SensorHalBase.SensorListener;
+import com.android.car.hal.VehicleHal;
+import com.android.car.hal.HalProperty;
+import com.android.car.hal.SensorHalService;
+import com.android.car.hal.SensorHalServiceBase;
+import com.android.car.hal.SensorHalServiceBase.SensorListener;
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.ConcurrentModificationException;
+import java.util.HashMap;
 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, SensorHal.SensorListener {
+        implements CarServiceBase, SensorHalService.SensorListener {
 
     /**
      * Abstraction for logical sensor which is not physical sensor but presented as sensor to
      * upper layer. Currently {@link CarSensorManager#SENSOR_TYPE_NIGHT} and
      * {@link CarSensorManager#SENSOR_TYPE_DRIVING_STATUS} falls into this category.
      * Implementation can call {@link CarSensorService#onSensorData(CarSensorEvent)} when there
-     * is state change for the given sensor after {@link SensorHalBase#init()}
+     * is state change for the given sensor after {@link SensorHalServiceBase#init()}
      * is called.
      */
-    public static abstract class LogicalSensorHalBase extends SensorHalBase {
+    public static abstract class LogicalSensorHalBase extends SensorHalServiceBase {
+
+        @Override
+        public void handleBooleanHalEvent(int property, boolean value, long timeStamp) {
+            //no-op default version as logical sensor does not come from HAL.
+        }
+
+        @Override
+        public void handleIntHalEvent(int property, int value, long timeStamp) {
+            //no-op
+        }
+
+        @Override
+        public void handleFloatHalEvent(int property, float value, long timeStamp) {
+            //no-op
+        }
+
+        @Override
+        public List<HalProperty> takeSupportedProperties(List<HalProperty> allProperties) {
+            return null;
+        }
 
         /** Sensor service is ready and all vehicle sensors are available. */
         public abstract void onSensorServiceReady();
@@ -90,7 +119,7 @@
     @GuardedBy("mSensorLock")
     private final SparseArray<SensorRecord> mSensorRecords = new SparseArray<>();
 
-    private final SensorHal mSensorHal;
+    private final SensorHalService mSensorHal;
     private int[] mCarProvidedSensors;
     private int[] mSupportedSensors;
     private final AtomicBoolean mSensorDiscovered = new AtomicBoolean(false);
@@ -98,12 +127,20 @@
     private final Context mContext;
 
     private final DrivingStatePolicy mDrivingStatePolicy;
+    private boolean mUseDefaultDrivingPolicy = true;
     private final DayNightModePolicy mDayNightModePolicy;
+    private boolean mUseDefaultDayNightModePolicy = true;
+
+    private final HandlerThread mHandlerThread;
+    private final SensorDispatchHandler mSensorDispatchHandler;
 
     public CarSensorService(Context context) {
         mContext = context;
+        mHandlerThread = new HandlerThread("SENSOR", Process.THREAD_PRIORITY_AUDIO);
+        mHandlerThread.start();
+        mSensorDispatchHandler = new SensorDispatchHandler(mHandlerThread.getLooper());
         // This triggers sensor hal init as well.
-        mSensorHal = Hal.getInstance(context.getApplicationContext()).getSensorHal();
+        mSensorHal = VehicleHal.getInstance(context.getApplicationContext()).getSensorHal();
         mDrivingStatePolicy = new DrivingStatePolicy(context);
         mDayNightModePolicy = new DayNightModePolicy(context);
     }
@@ -112,23 +149,27 @@
     public void init() {
         // Watch out the order. registerSensorListener can lead into onSensorHalReady call.
         // So it should be done last.
-        mDrivingStatePolicy.init();
-        mDayNightModePolicy.init();
         mSensorLock.lock();
         try {
             mSupportedSensors = refreshSupportedSensorsLocked();
-            addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
-                    mDrivingStatePolicy.getDefaultValue(
-                            CarSensorManager.SENSOR_TYPE_DRIVING_STATUS));
-            addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_NIGHT,
-                    mDrivingStatePolicy.getDefaultValue(
-                            CarSensorManager.SENSOR_TYPE_NIGHT));
+            if (mUseDefaultDrivingPolicy) {
+                mDrivingStatePolicy.init();
+                addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+                        mDrivingStatePolicy.getDefaultValue(
+                                CarSensorManager.SENSOR_TYPE_DRIVING_STATUS));
+                mDrivingStatePolicy.registerSensorListener(this);
+            }
+            if (mUseDefaultDayNightModePolicy) {
+                mDayNightModePolicy.init();
+                addNewSensorRecordLocked(CarSensorManager.SENSOR_TYPE_NIGHT,
+                        mDrivingStatePolicy.getDefaultValue(
+                                CarSensorManager.SENSOR_TYPE_NIGHT));
+                mDayNightModePolicy.registerSensorListener(this);
+            }
 
         } finally {
             mSensorLock.unlock();
         }
-        mDrivingStatePolicy.registerSensorListener(this);
-        mDayNightModePolicy.registerSensorListener(this);
         mSensorHal.registerSensorListener(this);
     }
 
@@ -140,10 +181,15 @@
 
     @Override
     public void release() {
-        mDrivingStatePolicy.release();
-        mDayNightModePolicy.release();
+        mHandlerThread.quit();
         tryHoldSensorLock();
         try {
+            if (mUseDefaultDrivingPolicy) {
+                mDrivingStatePolicy.release();
+            }
+            if (mUseDefaultDayNightModePolicy) {
+                mDayNightModePolicy.release();
+            }
             for (int i = mSensorListeners.size() - 1; i >= 0; --i) {
                 SensorListeners listener = mSensorListeners.valueAt(i);
                 listener.release();
@@ -171,7 +217,7 @@
     }
 
     @Override
-    public void onSensorHalReady(SensorHalBase hal) {
+    public void onSensorHalReady(SensorHalServiceBase hal) {
         if (hal == mSensorHal) {
             mCarProvidedSensors = mSensorHal.getSupportedSensors();
             if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
@@ -181,27 +227,42 @@
             mSensorLock.lock();
             try {
                 mSupportedSensors = refreshSupportedSensorsLocked();
+                if (mUseDefaultDrivingPolicy) {
+                    mDrivingStatePolicy.onSensorServiceReady();
+                }
+                if (mUseDefaultDayNightModePolicy) {
+                    mDayNightModePolicy.onSensorServiceReady();
+                }
             } finally {
                 mSensorLock.unlock();
             }
-            mDrivingStatePolicy.onSensorServiceReady();
-            mDayNightModePolicy.onSensorServiceReady();
         }
     }
 
-    private void processSensorDataLocked(CarSensorEvent event) {
-        SensorRecord record = mSensorRecords.get(event.sensorType);
-        if (record != null) {
-            record.lastEvent = event;
-        } else {
-            if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                Log.d(CarLog.TAG_SENSOR, "sensor data received without matching record");
+    private void processSensorData(List<CarSensorEvent> events) {
+        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.timeStampNs < event.timeStampNs) {
+                    record.lastEvent = event;
+                    //TODO recycle event
+                } else { // wrong timestamp, throw away this.
+                    //TODO recycle new event
+                    continue;
+                }
+                SensorListeners listeners = mSensorListeners.get(event.sensorType);
+                if (listeners != null) {
+                    listeners.queueSensorEvent(event);
+                }
             }
         }
-        if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
-            Log.v(CarLog.TAG_SENSOR, "onSensorData type: " + event.sensorType);
+        for (SensorClient client: mClients) {
+            client.dispatchSensorUpdate();
         }
-        notifySensorEventToClientLocked(event, event.sensorType);
+        mSensorLock.unlock();
     }
 
     /**
@@ -210,13 +271,13 @@
      * @param event
      */
     @Override
-    public void onSensorData(CarSensorEvent event) {
-        mSensorLock.lock();
-        try {
-            processSensorDataLocked(event);
-        } finally {
-            mSensorLock.unlock();
-        }
+    public void onSensorEvents(List<CarSensorEvent> events) {
+        mSensorDispatchHandler.handleSensorEvents(events);
+    }
+
+    @Override
+    public void onSensorEvent(CarSensorEvent event) {
+        mSensorDispatchHandler.handleSensorEvent(event);
     }
 
     @Override
@@ -280,12 +341,13 @@
             // If we have a cached event for this sensor, send the event.
             SensorRecord record = mSensorRecords.get(sensorType);
             if (record != null && record.lastEvent != null) {
-                sensorClient.onSensorUpdate(record.lastEvent);
+                sensorClient.queueSensorEvent(record.lastEvent);
+                sensorClient.dispatchSensorUpdate();
             }
             if (sensorListeners == null) {
                 sensorListeners = new SensorListeners(rate);
                 mSensorListeners.put(sensorType, sensorListeners);
-                shouldStartSensors = isSensorRealLocked(sensorType);
+                shouldStartSensors = true;
             } else {
                 oldRate = Integer.valueOf(sensorListeners.getRate());
                 sensorClientWithRate = sensorListeners.findSensorClientWithRate(sensorClient);
@@ -298,7 +360,7 @@
             }
             if (sensorListeners.getRate() > rate) {
                 sensorListeners.setRate(rate);
-                shouldStartSensors = isSensorRealLocked(sensorType);
+                shouldStartSensors = sensorSupportRate(sensorType);
             }
             sensorClient.addSensor(sensorType);
         } finally {
@@ -325,6 +387,30 @@
         return true;
     }
 
+    private boolean sensorSupportRate(int sensorType) {
+        switch (sensorType) {
+            case CarSensorManager.SENSOR_TYPE_COMPASS:
+            case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
+            case CarSensorManager.SENSOR_TYPE_RPM:
+            case CarSensorManager.SENSOR_TYPE_LOCATION:
+            case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
+            case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE:
+            case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
+                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_DRIVING_STATUS:
+            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;
@@ -365,7 +451,7 @@
         if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
             Log.v(CarLog.TAG_SENSOR, "startSensor " + sensorType + " with rate " + rate);
         }
-        SensorHalBase sensorHal = getSensorHal(sensorType);
+        SensorHalServiceBase sensorHal = getSensorHal(sensorType);
         if (sensorHal != null) {
             if (!sensorHal.isReady()) {
                 Log.w(CarLog.TAG_SENSOR, "Sensor channel not available.");
@@ -430,11 +516,11 @@
             }
             sensorListeners.removeSensorClientWithRate(clientWithRate);
             if (sensorListeners.getNumberOfClients() == 0) {
-                shouldStopSensor = isSensorRealLocked(sensorType);
+                shouldStopSensor = true;
                 mSensorListeners.remove(sensorType);
             } else if (sensorListeners.updateRate()) { // rate changed
                 newRate = sensorListeners.getRate();
-                shouldRestartSensor = isSensorRealLocked(sensorType);
+                shouldRestartSensor = sensorSupportRate(sensorType);
             }
             if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
                 Log.d(CarLog.TAG_SENSOR, "unregister succeeded");
@@ -453,7 +539,7 @@
         if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
             Log.d(CarLog.TAG_SENSOR, "stopSensor " + sensorType);
         }
-        SensorHalBase sensorHal = getSensorHal(sensorType);
+        SensorHalServiceBase sensorHal = getSensorHal(sensorType);
         if (sensorHal == null || !sensorHal.isReady()) {
             Log.w(CarLog.TAG_SENSOR, "Sensor channel not available.");
             return;
@@ -471,14 +557,24 @@
         sensorHal.requestSensorStop(sensorType);
     }
 
-    private SensorHalBase getSensorHal(int sensorType) {
-        switch (sensorType) {
-            case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
-                return mDrivingStatePolicy;
-            case CarSensorManager.SENSOR_TYPE_NIGHT:
-                return mDayNightModePolicy;
-            default:
-                return mSensorHal;
+    private SensorHalServiceBase getSensorHal(int sensorType) {
+        try {
+            mSensorLock.lock();
+            switch (sensorType) {
+                case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
+                    if (mUseDefaultDrivingPolicy) {
+                        return mDrivingStatePolicy;
+                    }
+                    break;
+                case CarSensorManager.SENSOR_TYPE_NIGHT:
+                    if (mUseDefaultDayNightModePolicy) {
+                        return mDayNightModePolicy;
+                    }
+                    break;
+            }
+            return mSensorHal;
+        } finally {
+            mSensorLock.unlock();
         }
     }
 
@@ -499,16 +595,36 @@
 
     private int[] refreshSupportedSensorsLocked() {
         int numCarSensors = (mCarProvidedSensors == null) ? 0 : mCarProvidedSensors.length;
+        for (int i = 0; i < numCarSensors; i++) {
+            int sensor = mCarProvidedSensors[i];
+            if (sensor == CarSensorManager.SENSOR_TYPE_DRIVING_STATUS) {
+                mUseDefaultDrivingPolicy = false;
+            } else if (sensor == CarSensorManager.SENSOR_TYPE_NIGHT) {
+                mUseDefaultDayNightModePolicy = false;
+            }
+        }
+        int totalNumSensors = numCarSensors;
+        if (mUseDefaultDrivingPolicy) {
+            totalNumSensors++;
+        }
+        if (mUseDefaultDayNightModePolicy) {
+            totalNumSensors++;
+        }
         // Two logical sensors are always added.
-        int[] supportedSensors = new int[numCarSensors + 2];
+        int[] supportedSensors = new int[totalNumSensors];
         int index = 0;
-        supportedSensors[index] = CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
-        index++;
-        supportedSensors[index] = CarSensorManager.SENSOR_TYPE_NIGHT;
-        index++;
+        if (mUseDefaultDrivingPolicy) {
+            supportedSensors[index] = CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
+            index++;
+        }
+        if (mUseDefaultDayNightModePolicy) {
+            supportedSensors[index] = CarSensorManager.SENSOR_TYPE_NIGHT;
+            index++;
+        }
 
         for (int i = 0; i < numCarSensors; i++) {
             int sensor = mCarProvidedSensors[i];
+
             if (mSensorRecords.get(sensor) == null) {
                 SensorRecord record = new SensorRecord();
                 mSensorRecords.put(sensor, record);
@@ -531,15 +647,6 @@
         return false;
     }
 
-    private void notifySensorEventToClientLocked(CarSensorEvent event, int sensor) {
-        SensorListeners listeners = mSensorListeners.get(sensor);
-        if (listeners != null) {
-            listeners.onSensorUpdate(event);
-        } else {
-            Log.w(CarLog.TAG_SENSOR, "sensor event while no listener, sensor:" + sensor);
-        }
-    }
-
     /**
      * Find SensorClient from client list and return it.
      * This should be called with mClients locked.
@@ -569,11 +676,91 @@
         }
     }
 
+    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 IBinder.DeathRecipient {
         /** callback for sensor events */
         private final ICarSensorEventListener mListener;
         private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
+        private final LinkedList<CarSensorEvent> mSensorsToDispatch =
+                new LinkedList<CarSensorEvent>();
 
         /** when false, it is already released */
         private volatile boolean mActive = true;
@@ -628,24 +815,33 @@
             removeClient(this);
         }
 
-        void onSensorUpdate(CarSensorEvent event) {
-            try {
-                if (mActive) {
-                    mListener.onSensorChanged(event);
-                } else {
-                    if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
-                        Log.d(CarLog.TAG_SENSOR, "sensor update while client is already released");
-                    }
-                }
-            } catch (RemoteException e) {
-                //ignore. crash will be handled by death handler
+        void queueSensorEvent(CarSensorEvent event) {
+            mSensorsToDispatch.add(event);
+        }
+
+        void dispatchSensorUpdate() {
+            if (mSensorsToDispatch.size() == 0) {
+                return;
             }
+            if (mActive) {
+                try {
+                    mListener.onSensorChanged(mSensorsToDispatch);
+                } 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");
+                }
+            }
+            mSensorsToDispatch.clear();
         }
 
         void release() {
             if (mActive) {
                 mListener.asBinder().unlinkToDeath(this, 0);
                 mActiveSensors.clear();
+                mSensorsToDispatch.clear();
                 mActive = false;
             }
         }
@@ -745,12 +941,9 @@
             return null;
         }
 
-        void onSensorUpdate(CarSensorEvent event) {
-            if (Log.isLoggable(CarLog.TAG_SENSOR, Log.VERBOSE)) {
-                Log.v(CarLog.TAG_SENSOR, "onSensorUpdate to clients: " + mSensorClients.size());
-            }
+        void queueSensorEvent(CarSensorEvent event) {
             for (SensorClientWithRate clientWithRate: mSensorClients) {
-                clientWithRate.getSensorClient().onSensorUpdate(event);
+                clientWithRate.getSensorClient().queueSensorEvent(event);
             }
         }
 
@@ -777,7 +970,6 @@
                                 + " active: " + record.enabled);
                         // only print sensor data which is not related with location
                         if (sensor != CarSensorManager.SENSOR_TYPE_LOCATION &&
-                                sensor != CarSensorManager.SENSOR_TYPE_DEAD_RECKONING &&
                                 sensor != CarSensorManager.SENSOR_TYPE_GPS_SATELLITE) {
                             writer.println(" " + record.lastEvent.toString());
                         }
@@ -826,8 +1018,12 @@
             writer.println("concurrent modification happened");
         }
         writer.println("**driving policy**");
-        mDrivingStatePolicy.dump(writer);
+        if (mUseDefaultDrivingPolicy) {
+            mDrivingStatePolicy.dump(writer);
+        }
         writer.println("**day/night policy**");
-        mDayNightModePolicy.dump(writer);
+        if (mUseDefaultDayNightModePolicy) {
+            mDayNightModePolicy.dump(writer);
+        }
     }
 }
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 32e47ff..a342e05 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -18,6 +18,8 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import com.android.car.hal.VehicleHal;
+
 import android.app.Service;
 import android.support.car.Car;
 import android.content.Intent;
@@ -57,5 +59,9 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         writer.println("*dump car service*");
+        writer.println("*dump HAL*");
+        VehicleHal.getInstance(getApplicationContext()).dump(writer);
+        writer.println("*dump services*");
+        ICarImpl.getInstance(this).dump(writer);
     }
 }
diff --git a/service/src/com/android/car/CarServiceApplication.java b/service/src/com/android/car/CarServiceApplication.java
new file mode 100644
index 0000000..49cd8f0
--- /dev/null
+++ b/service/src/com/android/car/CarServiceApplication.java
@@ -0,0 +1,35 @@
+/*
+ * 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.app.Application;
+
+public class CarServiceApplication extends Application {
+
+    @Override
+    public void onCreate() {
+        // TODO Auto-generated method stub
+        super.onCreate();
+    }
+
+    @Override
+    public void onTerminate() {
+        // TODO Auto-generated method stub
+        super.onTerminate();
+    }
+
+}
diff --git a/service/src/com/android/car/DayNightModePolicy.java b/service/src/com/android/car/DayNightModePolicy.java
index 2a9c426..23f36d9 100644
--- a/service/src/com/android/car/DayNightModePolicy.java
+++ b/service/src/com/android/car/DayNightModePolicy.java
@@ -21,7 +21,7 @@
 import android.support.car.CarSensorEvent;
 import android.support.car.CarSensorManager;
 
-import com.android.car.hal.SensorHalBase.SensorListener;
+import com.android.car.hal.SensorHalServiceBase.SensorListener;
 
 import java.io.PrintWriter;
 
diff --git a/service/src/com/android/car/DrivingStatePolicy.java b/service/src/com/android/car/DrivingStatePolicy.java
index de91318..107b228 100644
--- a/service/src/com/android/car/DrivingStatePolicy.java
+++ b/service/src/com/android/car/DrivingStatePolicy.java
@@ -24,9 +24,11 @@
 import android.support.car.ICarSensorEventListener;
 import android.util.Log;
 
-import com.android.car.hal.SensorHalBase.SensorListener;
+import com.android.car.hal.SensorHalServiceBase.SensorListener;
 
 import java.io.PrintWriter;
+import java.util.List;
+
 
 /**
  * Logical sensor implementing driving state policy. This policy sets only two states:
@@ -47,8 +49,10 @@
     private final ICarSensorEventListener mICarSensorEventListener =
             new ICarSensorEventListener.Stub() {
         @Override
-        public void onSensorChanged(CarSensorEvent event) {
-            handleSensorEvent(event);
+        public void onSensorChanged(List<CarSensorEvent> events) {
+            for (CarSensorEvent event: events) {
+                handleSensorEvent(event);
+            }
         }
     };
 
@@ -118,7 +122,7 @@
     @Override
     public synchronized boolean requestSensorStart(int sensorType, int rate) {
         mStarted = true;
-        mSensorListener.onSensorData(createEvent(mDringState));
+        mSensorListener.onSensorEvent(createEvent(mDringState));
         return true;
     }
 
@@ -149,7 +153,7 @@
                 int drivingState = recalcDrivingStateLocked();
                 if (drivingState != mDringState && mSensorListener != null) {
                     mDringState = drivingState;
-                    mSensorListener.onSensorData(createEvent(mDringState));
+                    mSensorListener.onSensorEvent(createEvent(mDringState));
                 }
                 break;
             default:
@@ -184,24 +188,24 @@
     }
 
     private boolean isParkingBrakeApplied(CarSensorEvent event) {
-        return event.byteValues[0] == 1;
+        return event.intValues[0] == 1;
     }
 
     private boolean isGearInParkingOrNeutral(CarSensorEvent event) {
-        byte gear = event.byteValues[0];
-        return (gear == ((byte) CarSensorEvent.GEAR_NEUTRAL & 0xff)) ||
-                (gear == ((byte) CarSensorEvent.GEAR_PARK & 0xff));
+        int gear = event.intValues[0];
+        return (gear == CarSensorEvent.GEAR_NEUTRAL) ||
+                (gear == CarSensorEvent.GEAR_PARK);
     }
 
     private boolean isGearInParking(CarSensorEvent event) {
-        byte gear = event.byteValues[0];
-        return gear == ((byte) CarSensorEvent.GEAR_PARK & 0xff);
+        int gear = event.intValues[0];
+        return gear == CarSensorEvent.GEAR_PARK;
     }
 
     private CarSensorEvent createEvent(int drivingState) {
         CarSensorEvent event = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
                 SystemClock.elapsedRealtimeNanos(), 0, 1);
-        event.byteValues[0] = (byte) (drivingState & 0xff);
+        event.intValues[0] = drivingState;
         return event;
     }
 }
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 081a33a..598ac8d 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.car;
 
+import java.io.PrintWriter;
+
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
@@ -27,7 +29,7 @@
 import android.support.car.ICarSensor;
 import android.util.Log;
 
-import com.android.car.hal.Hal;
+import com.android.car.hal.VehicleHal;
 import com.android.internal.annotations.GuardedBy;
 
 public class ICarImpl extends ICar.Stub {
@@ -37,7 +39,7 @@
     private static ICarImpl sInstance = null;
 
     private final Context mContext;
-    private final Hal mHal;
+    private final VehicleHal mHal;
 
     private final CarSensorService mCarSensorService;
 
@@ -59,7 +61,7 @@
 
     public ICarImpl(Context serviceContext) {
         mContext = serviceContext;
-        mHal = Hal.getInstance(serviceContext.getApplicationContext());
+        mHal = VehicleHal.getInstance(serviceContext.getApplicationContext());
         mCarSensorService = new CarSensorService(serviceContext);
     }
 
@@ -69,7 +71,7 @@
 
     private void release() {
         mCarSensorService.release();
-        Hal.releaseInstance();
+        VehicleHal.releaseInstance();
     }
 
     @Override
@@ -125,4 +127,9 @@
         //TODO
         return false;
     }
+
+    void dump(PrintWriter writer) {
+        writer.println("**CarSensorService");
+        mCarSensorService.dump(writer);
+    }
 }
diff --git a/service/src/com/android/car/hal/Hal.java b/service/src/com/android/car/hal/Hal.java
deleted file mode 100644
index 58e4319..0000000
--- a/service/src/com/android/car/hal/Hal.java
+++ /dev/null
@@ -1,60 +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.content.Context;
-
-/**
- * Abstraction for vehicle HAL.
- */
-public class Hal {
-
-    private final SensorHal mSensorHal;
-
-    private static Hal sInstance;
-
-    public static synchronized Hal getInstance(Context applicationContext) {
-        if (sInstance == null) {
-            sInstance = new Hal(applicationContext);
-            sInstance.init();
-        }
-        return sInstance;
-    }
-
-    public static synchronized void releaseInstance() {
-        if (sInstance != null) {
-            sInstance.release();
-            sInstance = null;
-        }
-    }
-
-    public Hal(Context applicationContext) {
-        mSensorHal = new SensorHal();
-    }
-
-    public void init() {
-        mSensorHal.init();
-    }
-
-    public void release() {
-        mSensorHal.release();
-    }
-
-    public SensorHal getSensorHal() {
-        return mSensorHal;
-    }
-}
diff --git a/service/src/com/android/car/hal/HalProperty.java b/service/src/com/android/car/hal/HalProperty.java
new file mode 100644
index 0000000..fe70246
--- /dev/null
+++ b/service/src/com/android/car/hal/HalProperty.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * Store configuration information for each HAL property.
+ */
+public class HalProperty {
+    //TODO add all missing items
+    public final int propertyType;
+    public final int dataType;
+    public final int accessType;
+    public final int changeMode;
+    public final float minSampleRate;
+    public final float maxSampleRate;
+
+    public HalProperty(int propertyType, int dataType, int accessType, int changeMode,
+            float minSampleRate, float maxSampleRate) {
+        this.propertyType = propertyType;
+        this.dataType = dataType;
+        this.accessType = accessType;
+        this.changeMode = changeMode;
+        this.minSampleRate = minSampleRate;
+        this.maxSampleRate = maxSampleRate;
+    }
+
+    @Override
+    public String toString() {
+        return "property:" + Integer.toHexString(propertyType) +
+                " data type:" + Integer.toHexString(dataType) +
+                " access type:" + Integer.toHexString(accessType) +
+                " change mode:" + Integer.toHexString(changeMode) +
+                " min sample rate:" + minSampleRate + " max sample rate:" + maxSampleRate;
+    }
+}
diff --git a/service/src/com/android/car/hal/HalPropertyConst.java b/service/src/com/android/car/hal/HalPropertyConst.java
new file mode 100644
index 0000000..1616644
--- /dev/null
+++ b/service/src/com/android/car/hal/HalPropertyConst.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+//TODO Auto generate from vehicle.h
+package com.android.car.hal;
+
+public class HalPropertyConst {
+    public static final int VEHICLE_PROPERTY_INFO_VIN = 0x00000100;
+    public static final int VEHICLE_PROPERTY_INFO_MAKE = 0x00000101;
+    public static final int VEHICLE_PROPERTY_INFO_MODEL = 0x00000102;
+    public static final int VEHICLE_PROPERTY_INFO_MODEL_YEAR  = 0x00000103;
+    public static final int VEHICLE_PROPERTY_PERF_VEHICLE_SPEED = 0x00000207;
+    public static final int VEHICLE_PROPERTY_GEAR_SELECTION  = 0x00000400;
+    public static final int VEHICLE_PROPERTY_CURRENT_GEAR = 0x00000401;
+    public static final int VEHICLE_PROPERTY_PARKING_BRAKE_ON = 0x00000402;
+    public static final int VEHICLE_PROPERTY_DRIVING_STATUS = 0x00000404;
+    public static final int VEHICLE_PROPERTY_NIGHT_MODE = 0x00000407;
+
+    public static class VehicleValueType {
+        public static final int VEHICLE_VALUE_TYPE_SHOUD_NOT_USE       = 0x00;
+        public static final int VEHICLE_VALUE_TYPE_STRING              = 0x01;
+        public static final int VEHICLE_VALUE_TYPE_FLOAT               = 0x02;
+        public static final int VEHICLE_VALUE_TYPE_INT64               = 0x03;
+        public static final int VEHICLE_VALUE_TYPE_INT32               = 0x04;
+        public static final int VEHICLE_VALUE_TYPE_BOOLEAN             = 0x05;
+        public static final int VEHICLE_VALUE_TYPE_HVAC_FAN_SPEED           = 0x06;
+        public static final int VEHICLE_VALUE_TYPE_HVAC_FAN_DIRECTION       = 0x07;
+        public static final int  VEHICLE_VALUE_TYPE_HVAC_ZONE_TEMPERATURE    = 0x08;
+        public static final int VEHICLE_VALUE_TYPE_HVAC_DEFROST_ON          = 0x09;
+        public static final int VEHICLE_VALUE_TYPE_HVAC_AC_ON               = 0x0A;
+    }
+
+    public static class VehiclePropChangeMode {
+        public static final int VEHICLE_PROP_CHANGE_MODE_STATIC         = 0x00;
+        public static final int VEHICLE_PROP_CHANGE_MODE_ON_CHANGE      = 0x01;
+        public static final int VEHICLE_PROP_CHANGE_MODE_CONTINUOUS     = 0x02;
+    }
+
+    public static class VehiclePropAccess {
+        public static final int PROP_ACCESS_READ  = 0x01;
+        public static final int PROP_ACCESS_WRITE = 0x02;
+        public static final int PROP_ACCESS_READ_WRITE = 0x03;
+    }
+
+    public int getVehicleValueType(int property) {
+        switch (property) {
+            case VEHICLE_PROPERTY_INFO_VIN:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_STRING;
+            case VEHICLE_PROPERTY_INFO_MAKE:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_STRING;
+            case VEHICLE_PROPERTY_INFO_MODEL:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_STRING;
+            case VEHICLE_PROPERTY_INFO_MODEL_YEAR:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+            case VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
+            case VEHICLE_PROPERTY_GEAR_SELECTION:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+            case VEHICLE_PROPERTY_CURRENT_GEAR:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+            case VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN;
+            case VEHICLE_PROPERTY_DRIVING_STATUS:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+            case VEHICLE_PROPERTY_NIGHT_MODE:
+                return VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN;
+        }
+        return VehicleValueType.VEHICLE_VALUE_TYPE_SHOUD_NOT_USE;
+    }
+}
diff --git a/service/src/com/android/car/hal/HalServiceBase.java b/service/src/com/android/car/hal/HalServiceBase.java
new file mode 100644
index 0000000..680b5b8
--- /dev/null
+++ b/service/src/com/android/car/hal/HalServiceBase.java
@@ -0,0 +1,52 @@
+/*
+ * 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 java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Common interface for all HAL service like sensor HAL.
+ * Each HAL service is connected with XyzService supporting XyzManager,
+ * and will translate HAL data into car api specific format.
+ */
+public abstract class HalServiceBase {
+    /** initialize */
+    public abstract void init();
+
+    /** release and stop operation */
+    public abstract void release();
+
+    /**
+     * return supported properties among all properties.
+     * @return null if no properties are supported
+     */
+    /**
+     * Take supported properties from given allProperties and return List of supported properties.
+     * @param allProperties
+     * @return null if no properties are supported.
+     */
+    public abstract List<HalProperty> takeSupportedProperties(List<HalProperty> allProperties);
+
+    public abstract void handleBooleanHalEvent(int property, boolean value, long timeStamp);
+
+    public abstract void handleIntHalEvent(int property, int value, long timeStamp);
+
+    public abstract void handleFloatHalEvent(int property, float value, long timeStamp);
+
+    public abstract void dump(PrintWriter writer);
+}
diff --git a/service/src/com/android/car/hal/SensorHal.java b/service/src/com/android/car/hal/SensorHal.java
deleted file mode 100644
index bc8fd5c..0000000
--- a/service/src/com/android/car/hal/SensorHal.java
+++ /dev/null
@@ -1,75 +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 java.io.PrintWriter;
-
-/**
- * TODO
- * Sensor HAL implementation for physical sensors in car.
- */
-public class SensorHal extends SensorHalBase {
-
-    private boolean mIsReady = false;
-
-    @Override
-    public synchronized void init() {
-        //TODO
-        mIsReady = true;
-    }
-
-    @Override
-    public synchronized void release() {
-        //TODO
-    }
-
-    @Override
-    public synchronized void registerSensorListener(SensorHalBase.SensorListener listener) {
-        //TODO
-        if (mIsReady) {
-            listener.onSensorHalReady(this);
-        }
-    }
-
-    @Override
-    public synchronized boolean isReady() {
-        //TODO
-        return true;
-    }
-
-    @Override
-    public synchronized int[] getSupportedSensors() {
-        //TODO
-        return null;
-    }
-
-    @Override
-    public synchronized boolean requestSensorStart(int sensorType, int rate) {
-        //TODO
-        return false;
-    }
-
-    @Override
-    public synchronized void requestSensorStop(int sensorType) {
-        //TODO
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-        // TODO Auto-generated method stub
-    }
-}
diff --git a/service/src/com/android/car/hal/SensorHalBase.java b/service/src/com/android/car/hal/SensorHalBase.java
deleted file mode 100644
index 12331b2..0000000
--- a/service/src/com/android/car/hal/SensorHalBase.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.android.car.hal;
-
-import android.support.car.CarSensorEvent;
-
-import java.io.PrintWriter;
-
-
-/**
- * Common base for all SensorHal implementation.
- * It is wholly based on subscription and there is no explicit API for polling, but each sensor
- * should report its initial state immediately after {@link #requestSensorStart(int, int)} call.
- * It is ok to report sensor data {@link SensorListener#onSensorData(CarSensorEvent)} inside
- * the {@link #requestSensorStart(int, int)} call.
- */
-public abstract class SensorHalBase {
-    /**
-     * Listener for monitoring sensor event. Only sensor service will implement this.
-     */
-    public interface SensorListener {
-        /**
-         * Sensor Hal is ready and is fully accessible.
-         * This will be called after {@link SensorHalBase#init()}.
-         */
-        void onSensorHalReady(SensorHalBase hal);
-        /**
-         * Sensor data is available.
-         * @param event
-         */
-        void onSensorData(CarSensorEvent event);
-    }
-
-    /**
-     * Do necessary initialization. After this, {@link #getSupportedSensors()} should work.
-     */
-    public abstract void init();
-
-    public abstract void release();
-
-    public abstract void registerSensorListener(SensorListener listener);
-
-    /**
-     * Sensor HAL should be ready after init call.
-     * @return
-     */
-    public abstract boolean isReady();
-
-    public abstract int[] getSupportedSensors();
-
-    public abstract boolean requestSensorStart(int sensorType, int rate);
-
-    public abstract void requestSensorStop(int sensorType);
-
-    public abstract void dump(PrintWriter writer);
-}
diff --git a/service/src/com/android/car/hal/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
new file mode 100644
index 0000000..50edeed
--- /dev/null
+++ b/service/src/com/android/car/hal/SensorHalService.java
@@ -0,0 +1,267 @@
+/*
+ * 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.support.car.CarSensorEvent;
+import android.support.car.CarSensorManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.car.CarLog;
+import com.android.car.CarSensorEventFactory;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Sensor HAL implementation for physical sensors in car.
+ */
+public class SensorHalService extends SensorHalServiceBase {
+
+    private static final boolean DBG_EVENTS = true;
+
+    private static final int SENSOR_TYPE_INVALD = -1;
+
+    private final VehicleHal mHal;
+    private boolean mIsReady = false;
+    private SensorHalServiceBase.SensorListener mSensorListener;
+    private final SparseArray<HalProperty> mSensorToHalProperty = new SparseArray<HalProperty>();
+
+    public SensorHalService(VehicleHal hal) {
+        mHal = hal;
+    }
+
+    @Override
+    public synchronized void init() {
+        //TODO
+        mIsReady = true;
+    }
+
+    @Override
+    public synchronized List<HalProperty> takeSupportedProperties(List<HalProperty> allProperties) {
+        LinkedList<HalProperty> supportedProperties = new LinkedList<HalProperty>();
+        for (HalProperty halProperty : allProperties) {
+            int sensor = getSensorTypeFromHalProperty(halProperty.propertyType);
+            if (sensor != SENSOR_TYPE_INVALD &&
+                    halProperty.changeMode !=
+                    HalPropertyConst.VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC &&
+                    (halProperty.accessType == HalPropertyConst.VehiclePropAccess.PROP_ACCESS_READ
+                    || halProperty.accessType ==
+                    HalPropertyConst.VehiclePropAccess.PROP_ACCESS_WRITE)) {
+                supportedProperties.add(halProperty);
+                mSensorToHalProperty.append(sensor, halProperty);
+            }
+        }
+        return supportedProperties;
+    }
+
+    @Override
+    public synchronized void release() {
+        mSensorToHalProperty.clear();
+        mIsReady = false;
+    }
+
+    @Override
+    public void handleBooleanHalEvent(int property, boolean value, long timeStamp) {
+        if (DBG_EVENTS) {
+            Log.i(CarLog.TAG_SENSOR, "boolean event, property:" + property + " value:" + value);
+        }
+        switch (property) {
+            case HalPropertyConst.VEHICLE_PROPERTY_NIGHT_MODE:
+            case HalPropertyConst.VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+                break;
+            default:
+                throw new RuntimeException("handleBooleanHalEvent wrong property " + property);
+        }
+        int sensorType = getSensorTypeFromHalProperty(property);
+        if (sensorType == SENSOR_TYPE_INVALD) {
+            throw new RuntimeException("handleBooleanHalEvent no sensor defined for property " +
+                    property);
+        }
+        CarSensorEvent event = CarSensorEventFactory.createBooleanEvent(sensorType, timeStamp,
+                value);
+        SensorHalServiceBase.SensorListener sensorListener = null;
+        synchronized (this) {
+            sensorListener = mSensorListener;
+        }
+        sensorListener.onSensorEvent(event);
+    }
+
+    @Override
+    public void handleIntHalEvent(int property, int value, long timeStamp) {
+        if (DBG_EVENTS) {
+            Log.i(CarLog.TAG_SENSOR, "int event, property:" + property + " value:" + value);
+        }
+        switch (property) {
+            case HalPropertyConst.VEHICLE_PROPERTY_GEAR_SELECTION:
+            case HalPropertyConst.VEHICLE_PROPERTY_DRIVING_STATUS:
+                break;
+            default:
+                throw new RuntimeException("handleIntHalEvent wrong property " + property);
+        }
+        int sensorType = getSensorTypeFromHalProperty(property);
+        if (sensorType == SENSOR_TYPE_INVALD) {
+            throw new RuntimeException("handleIntHalEvent no sensor defined for property " +
+                    property);
+        }
+        CarSensorEvent event = CarSensorEventFactory.createIntEvent(sensorType, timeStamp, value);
+        SensorHalServiceBase.SensorListener sensorListener = null;
+        synchronized (this) {
+            sensorListener = mSensorListener;
+        }
+        sensorListener.onSensorEvent(event);
+    }
+
+    @Override
+    public void handleFloatHalEvent(int property, float value, long timeStamp) {
+        if (DBG_EVENTS) {
+            Log.i(CarLog.TAG_SENSOR, "float event, property:" + property + " value:" + value);
+        }
+        switch (property) {
+            case HalPropertyConst.VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
+                break;
+            default:
+                throw new RuntimeException("handleFloatHalEvent wrong property " + property);
+        }
+        int sensorType = getSensorTypeFromHalProperty(property);
+        if (sensorType == SENSOR_TYPE_INVALD) {
+            throw new RuntimeException("handleFloatHalEvent no sensor defined for property " +
+                    property);
+        }
+        CarSensorEvent event = CarSensorEventFactory.createFloatEvent(sensorType, timeStamp, value);
+        SensorHalServiceBase.SensorListener sensorListener = null;
+        synchronized (this) {
+            sensorListener = mSensorListener;
+        }
+        sensorListener.onSensorEvent(event);
+    }
+
+    @Override
+    public synchronized void registerSensorListener(SensorHalServiceBase.SensorListener listener) {
+        mSensorListener = listener;
+        if (mIsReady) {
+            listener.onSensorHalReady(this);
+        }
+    }
+
+    @Override
+    public synchronized boolean isReady() {
+        return mIsReady;
+    }
+
+    @Override
+    public synchronized int[] getSupportedSensors() {
+        int[] supportedSensors = new int[mSensorToHalProperty.size()];
+        for (int i = 0; i < supportedSensors.length; i++) {
+            supportedSensors[i] = mSensorToHalProperty.keyAt(i);
+        }
+        return supportedSensors;
+    }
+
+    @Override
+    public synchronized boolean requestSensorStart(int sensorType, int rate) {
+        HalProperty halProp = mSensorToHalProperty.get(sensorType);
+        if (halProp == null) {
+            return false;
+        }
+        //TODO calculate sampling rate properly
+        int r = mHal.subscribeProperty(halProp.propertyType,
+                fixSamplingRateForProperty(halProp, rate));
+        return r == 0;
+    }
+
+    private float fixSamplingRateForProperty(HalProperty prop, int carSensorManagerRate) {
+        if (prop.changeMode ==
+                HalPropertyConst.VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE) {
+            return 0;
+        }
+        float rate = 1.0f;
+        switch (carSensorManagerRate) {
+            case CarSensorManager.SENSOR_RATE_FASTEST:
+            case CarSensorManager.SENSOR_RATE_FAST:
+                rate = 10f;
+                break;
+            case CarSensorManager.SENSOR_RATE_UI:
+                rate = 5f;
+                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 synchronized void requestSensorStop(int sensorType) {
+        HalProperty halProp = mSensorToHalProperty.get(sensorType);
+        if (halProp == null) {
+            return;
+        }
+        mHal.unsubscribeProperty(halProp.propertyType);
+    }
+
+    public synchronized void onSensorEvents(List<CarSensorEvent> events) {
+        if (mSensorListener != null) {
+            mSensorListener.onSensorEvents(events);
+        }
+    }
+
+    public synchronized void onSensorEvent(CarSensorEvent event) {
+        if (mSensorListener != null) {
+            mSensorListener.onSensorEvent(event);
+        }
+    }
+
+    /**
+     * Covert hal property to sensor type. This is also used to check if specific property
+     * is supported by sensor hal or not.
+     * @param halPropertyType
+     * @return
+     */
+    static int getSensorTypeFromHalProperty(int halPropertyType) {
+        switch (halPropertyType) {
+            case HalPropertyConst.VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
+                return CarSensorManager.SENSOR_TYPE_CAR_SPEED;
+            case HalPropertyConst.VEHICLE_PROPERTY_GEAR_SELECTION:
+                return CarSensorManager.SENSOR_TYPE_GEAR;
+            case HalPropertyConst.VEHICLE_PROPERTY_NIGHT_MODE:
+                return CarSensorManager.SENSOR_TYPE_NIGHT;
+            case HalPropertyConst.VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+                return CarSensorManager.SENSOR_TYPE_PARKING_BRAKE;
+            case HalPropertyConst.VEHICLE_PROPERTY_DRIVING_STATUS:
+                return CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
+            default:
+                Log.e(CarLog.TAG_SENSOR, "unknown sensor property from HAL " + halPropertyType);
+                return SENSOR_TYPE_INVALD;
+        }
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        writer.println("***Sensor HAL***");
+        writer.println("****Supported properties****");
+        for (int i = 0; i < mSensorToHalProperty.size(); i++) {
+            writer.println(mSensorToHalProperty.valueAt(i).toString());
+        }
+    }
+}
diff --git a/service/src/com/android/car/hal/SensorHalServiceBase.java b/service/src/com/android/car/hal/SensorHalServiceBase.java
new file mode 100644
index 0000000..cf6e20a
--- /dev/null
+++ b/service/src/com/android/car/hal/SensorHalServiceBase.java
@@ -0,0 +1,72 @@
+/*
+ * 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.support.car.CarSensorEvent;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+
+/**
+ * Common base for all SensorHal implementation.
+ * It is wholly based on subscription and there is no explicit API for polling, but each sensor
+ * should report its initial state immediately after {@link #requestSensorStart(int, int)} call.
+ * It is ok to report sensor data {@link SensorListener#onSensorData(CarSensorEvent)} inside
+ * the {@link #requestSensorStart(int, int)} call.
+ */
+public abstract class SensorHalServiceBase  extends HalServiceBase {
+    /**
+     * Listener for monitoring sensor event. Only sensor service will implement this.
+     */
+    public interface SensorListener {
+        /**
+         * Sensor Hal is ready and is fully accessible.
+         * This will be called after {@link SensorHalServiceBase#init()}.
+         */
+        void onSensorHalReady(SensorHalServiceBase hal);
+        /**
+         * Sensor events are available.
+         * @param events
+         */
+        void onSensorEvents(List<CarSensorEvent> events);
+
+        /**
+         * A sensor event is available.
+         * @param event
+         */
+        void onSensorEvent(CarSensorEvent event);
+    }
+
+    public abstract void registerSensorListener(SensorListener listener);
+
+    /**
+     * Sensor HAL should be ready after init call.
+     * @return
+     */
+    public abstract boolean isReady();
+
+    /**
+     * This should work after {@link #init()}.
+     * @return
+     */
+    public abstract int[] getSupportedSensors();
+
+    public abstract boolean requestSensorStart(int sensorType, int rate);
+
+    public abstract void requestSensorStop(int sensorType);
+}
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
new file mode 100644
index 0000000..68daf68
--- /dev/null
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -0,0 +1,295 @@
+/*
+ * 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.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.support.car.CarSensorEvent;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.car.CarLog;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing
+ * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase}
+ * implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding
+ * Car*Service for Car*Manager API.
+ */
+public class VehicleHal {
+
+    private static final boolean DBG = true;
+
+    /**
+     * Interface for mocking Hal which is used for testing, but should be kept even in release.
+     */
+    public interface HalMock {
+        //TODO
+    };
+
+    static {
+        System.loadLibrary("jni_carservice");
+    }
+
+    private static VehicleHal sInstance;
+
+    private final HandlerThread mHandlerThread;
+    private final DefaultHandler mDefaultHandler;
+
+    public static synchronized VehicleHal getInstance(Context applicationContext) {
+        if (sInstance == null) {
+            sInstance = new VehicleHal(applicationContext);
+            // init is handled in a separate thread to prevent blocking the calling thread for too
+            // long.
+            sInstance.startInit();
+        }
+        return sInstance;
+    }
+
+    public static synchronized void releaseInstance() {
+        if (sInstance != null) {
+            sInstance.release();
+            sInstance = null;
+        }
+    }
+
+    private final SensorHalService mSensorHal;
+    @SuppressWarnings({"UnusedDeclaration"})
+    private long mNativePtr; // used by native code
+
+    private static final int NUM_MAX_PROPERTY_ENTRIES = 100;
+    private static final int NUM_MAX_DATA_ENTRIES = 1000;
+    /**
+     * Contain property - data type - int length - float length. Hal will fill this array
+     * before calling {@link #onHalDataEvents(int)}.
+     */
+    private final int[] mNativepPropertyInfos = new int[4 * NUM_MAX_PROPERTY_ENTRIES];
+    /**
+     * Contains timestamp for all events. Hal will fill this in native side.
+     */
+    private final long[] mNativeTimestampsNs = new long[NUM_MAX_PROPERTY_ENTRIES];
+    /**
+     * Contain int data passed for HAL events.
+     */
+    private final int[] mNativeIntData = new int[NUM_MAX_DATA_ENTRIES];
+    private final float[] mNativeFloatData = new float[NUM_MAX_DATA_ENTRIES];
+
+    /** stores handler for each HAL property. Property events are sent to handler. */
+    private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<HalServiceBase>();
+    /** This is for iterating all HalServices with fixed order. */
+    private final HalServiceBase[] mAllServices;
+
+    private VehicleHal(Context applicationContext) {
+        mHandlerThread = new HandlerThread("HAL");
+        mHandlerThread.start();
+        mDefaultHandler = new DefaultHandler(mHandlerThread.getLooper());
+        // passing this should be safe as long as it is just kept and not used in constructor
+        mSensorHal = new SensorHalService(this);
+        mAllServices = new HalServiceBase[] { mSensorHal };
+    }
+
+    private void startInit() {
+        mDefaultHandler.requestInit();
+    }
+
+    private void doInit() {
+        mNativePtr = nativeInit(mNativepPropertyInfos, mNativeTimestampsNs, mNativeIntData,
+                mNativeFloatData);
+        HalProperty[] properties = getSupportedProperties(mNativePtr);
+        LinkedList<HalProperty> allProperties = new LinkedList<HalProperty>();
+        for (HalProperty prop : properties) {
+            allProperties.add(prop);
+            if (DBG) {
+                Log.i(CarLog.TAG_HAL, "property: " + prop);
+            }
+        }
+        for (HalServiceBase service: mAllServices) {
+            List<HalProperty> taken = service.takeSupportedProperties(allProperties);
+            if (DBG) {
+                Log.i(CarLog.TAG_HAL, "HalService " + service + " take properties " + taken.size());
+            }
+            for (HalProperty p: taken) {
+                mPropertyHandlers.append(p.propertyType, service);
+            }
+            allProperties.removeAll(taken);
+            service.init();
+        }
+    }
+
+    private void release() {
+        // release in reverse order from init
+        for (int i = mAllServices.length - 1; i >= 0; i--) {
+            mAllServices[i].release();
+        }
+        mHandlerThread.quit();
+        nativeRelease(mNativePtr);
+    }
+
+    public SensorHalService getSensorHal() {
+        return mSensorHal;
+    }
+
+    /**
+     * Start mocking HAL with given mock. Actual H/W will be stop functioning until mocking is
+     * stopped. The call will be blocked until all pending events are delivered to upper layer.
+     */
+    public void startHalMocking(HalMock mock) {
+        //TODO
+    }
+
+    public void stopHalMocking() {
+        //TODO
+    }
+
+    public int subscribeProperty(int property, float samplingRateHz) {
+        return subscribeProperty(mNativePtr, property, samplingRateHz);
+    }
+
+    public void unsubscribeProperty(int property) {
+        unsubscribeProperty(mNativePtr, property);
+    }
+
+    /**
+     * Notification from Hal layer for incoming Hal events. This version allows passing multiple
+     * events in one call to reduce JNI overhead. Native side should combine multiple events if
+     * possible to reduce overhead. The end result is more like simpler serialization but it should
+     * not be as expensive as full serialization like protobuf and this should reduce JNI overhead
+     * a lot.
+     * @param numEvents Number of events passed this time.
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    private void onHalDataEvents(int numEvents) {
+        int propertyInfoIndex = 0;
+        int intDataIndex = 0;
+        int floatDataIndex = 0;
+        for (int i = 0; i < numEvents; i++) {
+            int property = mNativepPropertyInfos[propertyInfoIndex];
+            int dataType = mNativepPropertyInfos[propertyInfoIndex + 1];
+            int intLength = mNativepPropertyInfos[propertyInfoIndex + 2];
+            int floatLength = mNativepPropertyInfos[propertyInfoIndex + 3];
+            long timeStamp = mNativeTimestampsNs[i];
+            propertyInfoIndex += 4;
+            HalServiceBase service = mPropertyHandlers.get(property);
+            if (service == null) {
+                Log.e(CarLog.TAG_HAL, "No service for event:" + property);
+                intDataIndex += intLength;
+                floatDataIndex += floatLength;
+                continue;
+            }
+            switch (dataType) {
+                case HalPropertyConst.VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN:
+                    if (intLength != 1 && floatLength != 0) {
+                        Log.e(CarLog.TAG_HAL, "Wrong data type");
+                    } else {
+                        boolean value = mNativeIntData[intDataIndex] == 1;
+                        service.handleBooleanHalEvent(property, value, timeStamp);
+                    }
+                    break;
+                case HalPropertyConst.VehicleValueType.VEHICLE_VALUE_TYPE_INT32:
+                    if (intLength != 1 && floatLength != 0) {
+                        Log.e(CarLog.TAG_HAL, "Wrong data type");
+                    } else {
+                        int value = mNativeIntData[intDataIndex];
+                        service.handleIntHalEvent(property, value, timeStamp);
+                    }
+                    break;
+                case HalPropertyConst.VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT:
+                    if (intLength != 0 && floatLength != 1) {
+                        Log.e(CarLog.TAG_HAL, "Wrong data type");
+                    } else {
+                        float value = mNativeFloatData[floatDataIndex];
+                        service.handleFloatHalEvent(property, value, timeStamp);
+                    }
+                    break;
+                //TODO handle other types
+                default:
+                    Log.e(CarLog.TAG_HAL, "onHalEvents not implemented for type " + dataType);
+                    break;
+            }
+            intDataIndex += intLength;
+            floatDataIndex += floatLength;
+        }
+    }
+
+    /**
+     * Init native part with passing arrays to use for
+     * {@link #onHalDataEvents(int)}.
+     * @param props
+     * @param timestamps
+     * @param dataTypes
+     * @param intValues
+     * @param floatValues
+     * @return
+     */
+    private native long nativeInit(int[] propetyInfos, long[] timestamps, int[] intValues,
+            float[] floatValues);
+    private native void nativeRelease(long nativePtr);
+    private native HalProperty[] getSupportedProperties(long nativePtr);
+    private native void setIntProperty(long nativePtr, int property, int value);
+    private native int getIntProperty(long nativePtr, int property);
+    private native void setFloatProperty(long nativePtr, int property, float value);
+    private native float getFloatProperty(long nativePtr, int property);
+
+    /**
+     * subsribe property.
+     * @param nativePtr
+     * @param propertyHandle
+     * @param sampleRateHz
+     * @return error code, 0 for ok.
+     */
+    private native int subscribeProperty(long nativePtr, int property, float sampleRateHz);
+    private native void unsubscribeProperty(long nativePtr, int property);
+
+    private class DefaultHandler extends Handler {
+        private static final int MSG_INIT = 0;
+
+        private DefaultHandler(Looper looper) {
+            super(looper);
+        }
+
+        private void requestInit() {
+            Message msg = obtainMessage(MSG_INIT);
+            sendMessage(msg);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_INIT:
+                    doInit();
+                    break;
+                default:
+                    Log.w(CarLog.TAG_HAL, "unown message:" + msg.what, new RuntimeException());
+                    break;
+            }
+        }
+    }
+
+    public void dump(PrintWriter writer) {
+        writer.println("**dump HAL services**");
+        for (HalServiceBase service: mAllServices) {
+            service.dump(writer);
+        }
+    }
+}