Use -Werror in packages/services/Car/tools am: 7f50f1e4bd
am: 20fdefebcf

Change-Id: I348d255ecd58df9db355b2ad920d4c97c5fb21fb
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 4bf7e9a..67d5bef 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -54,6 +54,12 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car7_intermediates/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/)
 
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/src/android/car/hardware/ICarDiagnostic*.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car7_intermediates/src/android/car/hardware/ICarDiagnostic*.java)
+
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car7_intermediates/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/TrustAgent/Android.mk b/TrustAgent/Android.mk
index dc70ef3..a57308a 100644
--- a/TrustAgent/Android.mk
+++ b/TrustAgent/Android.mk
@@ -13,6 +13,8 @@
 # limitations under the License.
 #
 
+ifneq ($(TARGET_BUILD_PDK), true)
+
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -37,3 +39,4 @@
 
 include $(BUILD_PACKAGE)
 
+endif
diff --git a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
index 99bbf22..997f028 100644
--- a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
+++ b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
@@ -41,7 +41,9 @@
  * A sample trust agent that demonstrates how to use the escrow token unlock APIs. </p>
  *
  * This trust agent runs during direct boot and binds to a BLE service that listens for remote
- * devices to trigger an unlock.
+ * devices to trigger an unlock. <p/>
+ *
+ * The permissions for this agent must be enabled as priv-app permissions for it to start.
  */
 public class CarBleTrustAgent extends TrustAgentService {
     public static final String ACTION_REVOKE_TRUST = "revoke-trust-action";
diff --git a/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java b/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java
index dac4b79..6e187e2 100644
--- a/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java
+++ b/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java
@@ -21,7 +21,13 @@
 import android.widget.TextView;
 
 /**
- * Activity to allow the user to add an escrow token to a remote device.
+ * Activity to allow the user to add an escrow token to a remote device. <p/>
+ *
+ * For this to work properly, the correct permissions must be set in the system config.  In AOSP,
+ * this config is in frameworks/base/core/res/res/values/config.xml <p/>
+ *
+ * The config must set config_allowEscrowTokenForTrustAgent to true.  For the desired car
+ * experience, the config should also set config_strongAuthRequiredOnBoot to false.
  */
 public class PhoneEnrolmentActivity extends Activity {
     private Button mScanButton;
diff --git a/car-cluster-logging-renderer/Android.mk b/car-cluster-logging-renderer/Android.mk
index 8fb7674..dd1d0ca 100644
--- a/car-cluster-logging-renderer/Android.mk
+++ b/car-cluster-logging-renderer/Android.mk
@@ -13,7 +13,7 @@
 # limitations under the License.
 #
 #
-
+ifneq ($(TARGET_BUILD_PDK),true)
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
@@ -32,3 +32,4 @@
 LOCAL_JAVA_LIBRARIES += android.car
 
 include $(BUILD_PACKAGE)
+endif
diff --git a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java b/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
index 527da11..b6ed388 100644
--- a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
+++ b/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
@@ -19,7 +19,9 @@
 import android.car.cluster.renderer.NavigationRenderer;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.util.Log;
+import com.google.android.collect.Lists;
 
 /**
  * Dummy implementation of {@link LoggingClusterRenderingService} to log all interaction.
@@ -36,6 +38,7 @@
                 Log.i(TAG, "getNavigationProperties");
                 CarNavigationInstrumentCluster config =
                         CarNavigationInstrumentCluster.createCluster(1000);
+                config.getExtra().putIntegerArrayList("dummy", Lists.newArrayList(1, 2, 3, 4));
                 Log.i(TAG, "getNavigationProperties, returns: " + config);
                 return config;
             }
@@ -67,6 +70,11 @@
                         + ", displayDistanceMillis: " + displayDistanceMillis
                         + ", displayDistanceUnit: " + displayDistanceUnit);
             }
+
+            @Override
+            public void onEvent(int eventType, Bundle bundle) {
+                Log.i(TAG, "onEvent, eventType: " + eventType + ", bundle: " + bundle);
+            }
         };
 
         Log.i(TAG, "createNavigationRenderer, returns: " + navigationRenderer);
diff --git a/car-lib/api/2.txt b/car-lib/api/2.txt
new file mode 100644
index 0000000..fb94b19
--- /dev/null
+++ b/car-lib/api/2.txt
@@ -0,0 +1,297 @@
+package android.car {
+
+  public final class Car {
+    method public void connect() throws java.lang.IllegalStateException;
+    method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection, android.os.Handler);
+    method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection);
+    method public void disconnect();
+    method public int getCarConnectionType();
+    method public java.lang.Object getCarManager(java.lang.String) throws android.car.CarNotConnectedException;
+    method public boolean isConnected();
+    method public boolean isConnecting();
+    field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
+    field public static final java.lang.String AUDIO_SERVICE = "audio";
+    field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
+    field public static final java.lang.String INFO_SERVICE = "info";
+    field public static final java.lang.String PACKAGE_SERVICE = "package";
+    field public static final java.lang.String PERMISSION_CAR_CONTROL_AUDIO_VOLUME = "android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
+    field public static final java.lang.String PERMISSION_FUEL = "android.car.permission.CAR_FUEL";
+    field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
+    field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+    field public static final java.lang.String SENSOR_SERVICE = "sensor";
+    field public static final int VERSION = 2; // 0x2
+  }
+
+  public final class CarAppFocusManager {
+    method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int);
+    method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback);
+    method public void addFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int) throws android.car.CarNotConnectedException;
+    method public boolean isOwningFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int) throws android.car.CarNotConnectedException;
+    method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int);
+    method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener);
+    method public int requestAppFocus(int, android.car.CarAppFocusManager.OnAppFocusOwnershipCallback) throws android.car.CarNotConnectedException, java.lang.SecurityException;
+    field public static final int APP_FOCUS_REQUEST_FAILED = 0; // 0x0
+    field public static final int APP_FOCUS_REQUEST_SUCCEEDED = 1; // 0x1
+    field public static final int APP_FOCUS_TYPE_NAVIGATION = 1; // 0x1
+    field public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; // 0x2
+  }
+
+  public static abstract interface CarAppFocusManager.OnAppFocusChangedListener {
+    method public abstract void onAppFocusChanged(int, boolean);
+  }
+
+  public static abstract interface CarAppFocusManager.OnAppFocusOwnershipCallback {
+    method public abstract void onAppFocusOwnershipGranted(int);
+    method public abstract void onAppFocusOwnershipLost(int);
+  }
+
+  public final class CarInfoManager {
+    method public java.lang.String getManufacturer() throws android.car.CarNotConnectedException;
+    method public java.lang.String getModel() throws android.car.CarNotConnectedException;
+    method public java.lang.String getModelYear() throws android.car.CarNotConnectedException;
+    method public java.lang.String getVehicleId() throws android.car.CarNotConnectedException;
+  }
+
+  public class CarNotConnectedException extends java.lang.Exception {
+    ctor public CarNotConnectedException();
+    ctor public CarNotConnectedException(java.lang.String);
+    ctor public CarNotConnectedException(java.lang.String, java.lang.Throwable);
+    ctor public CarNotConnectedException(java.lang.Exception);
+  }
+
+}
+
+package android.car.app.menu {
+
+  public abstract class CarMenuCallbacks {
+    ctor public CarMenuCallbacks();
+    method public abstract android.car.app.menu.RootMenu getRootMenu(android.os.Bundle);
+    method public abstract void onCarMenuClosed();
+    method public abstract void onCarMenuClosing();
+    method public abstract void onCarMenuOpened();
+    method public abstract void onCarMenuOpening();
+    method public abstract void onItemClicked(java.lang.String);
+    method public abstract boolean onItemLongClicked(java.lang.String);
+    method public abstract boolean onMenuClicked();
+    method public abstract void subscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+    method public abstract void unsubscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+  }
+
+  public class CarMenuConstants {
+    ctor public CarMenuConstants();
+  }
+
+  public static class CarMenuConstants.MenuItemConstants {
+    ctor public CarMenuConstants.MenuItemConstants();
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_FIRSTITEM = 2; // 0x2
+    field public static final java.lang.String KEY_EMPTY_PLACEHOLDER = "android.car.app.menu.empty_placeholder";
+    field public static final java.lang.String KEY_FLAGS = "android.car.app.menu.flags";
+    field public static final java.lang.String KEY_ID = "android.car.app.menu.id";
+    field public static final java.lang.String KEY_LEFTICON = "android.car.app.menu.leftIcon";
+    field public static final java.lang.String KEY_REMOTEVIEWS = "android.car.app.menu.remoteViews";
+    field public static final java.lang.String KEY_RIGHTICON = "android.car.app.menu.rightIcon";
+    field public static final java.lang.String KEY_RIGHTTEXT = "android.car.app.menu.rightText";
+    field public static final java.lang.String KEY_TEXT = "android.car.app.menu.text";
+    field public static final java.lang.String KEY_TITLE = "android.car.app.menu.title";
+    field public static final java.lang.String KEY_WIDGET = "android.car.app.menu.widget";
+    field public static final java.lang.String KEY_WIDGET_STATE = "android.car.app.menu.widget_state";
+    field public static final int WIDGET_CHECKBOX = 1; // 0x1
+    field public static final int WIDGET_TEXT_VIEW = 2; // 0x2
+  }
+
+  public static abstract class CarMenuConstants.MenuItemConstants.MenuItemFlags implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class CarMenuConstants.MenuItemConstants.WidgetTypes implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class CarUiEntry {
+    ctor public CarUiEntry(android.content.Context, android.content.Context);
+    method public abstract void closeDrawer();
+    method public abstract android.view.View getContentView();
+    method public abstract int getFragmentContainerId();
+    method public abstract java.lang.CharSequence getSearchBoxText();
+    method public abstract void hideMenuButton();
+    method public abstract void hideTitle();
+    method public abstract void onPause();
+    method public abstract void onRestoreInstanceState(android.os.Bundle);
+    method public abstract void onResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract void openDrawer();
+    method public abstract void restoreMenuDrawable();
+    method public abstract void setAutoLightDarkMode();
+    method public abstract void setBackground(android.graphics.Bitmap);
+    method public abstract void setCarMenuCallbacks(android.car.app.menu.CarMenuCallbacks);
+    method public abstract void setDarkMode();
+    method public abstract void setLightMode();
+    method public abstract void setMenuButtonBitmap(android.graphics.Bitmap);
+    method public abstract void setMenuButtonColor(int);
+    method public abstract void setScrimColor(int);
+    method public abstract void setSearchBoxColors(int, int, int, int);
+    method public abstract void setSearchBoxEditListener(android.car.app.menu.SearchBoxEditListener);
+    method public abstract void setSearchBoxEndView(android.view.View);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void showMenu(java.lang.String, java.lang.String);
+    method public abstract void showSearchBox(android.view.View.OnClickListener);
+    method public abstract void showTitle();
+    method public abstract void showToast(java.lang.String, long);
+    method public abstract android.widget.EditText startInput(java.lang.String, android.view.View.OnClickListener);
+    method public abstract void stopInput();
+    field protected final android.content.Context mAppContext;
+    field protected final android.content.Context mUiLibContext;
+  }
+
+  public class RootMenu {
+    ctor public RootMenu(java.lang.String);
+    ctor public RootMenu(java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getBundle();
+    method public java.lang.String getId();
+  }
+
+  public abstract class SearchBoxEditListener {
+    ctor public SearchBoxEditListener();
+    method public abstract void onEdit(java.lang.String);
+    method public abstract void onSearch(java.lang.String);
+  }
+
+  public abstract class SubscriptionCallbacks {
+    ctor public SubscriptionCallbacks();
+    method public abstract void onChildChanged(java.lang.String, android.os.Bundle);
+    method public abstract void onChildrenLoaded(java.lang.String, java.util.List<android.os.Bundle>);
+    method public abstract void onError(java.lang.String);
+  }
+
+}
+
+package android.car.content.pm {
+
+  public final class CarPackageManager {
+    method public boolean isActivityAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+    method public boolean isServiceAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+  }
+
+}
+
+package android.car.hardware {
+
+  public class CarSensorEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.hardware.CarSensorEvent> CREATOR;
+    field public static final int DRIVE_STATUS_FULLY_RESTRICTED = 31; // 0x1f
+    field public static final int DRIVE_STATUS_LIMIT_MESSAGE_LEN = 16; // 0x10
+    field public static final int DRIVE_STATUS_NO_CONFIG = 8; // 0x8
+    field public static final int DRIVE_STATUS_NO_KEYBOARD_INPUT = 2; // 0x2
+    field public static final int DRIVE_STATUS_NO_VIDEO = 1; // 0x1
+    field public static final int DRIVE_STATUS_NO_VOICE_INPUT = 4; // 0x4
+    field public static final int DRIVE_STATUS_UNRESTRICTED = 0; // 0x0
+    field public static final int GEAR_DRIVE = 100; // 0x64
+    field public static final int GEAR_EIGHTH = 8; // 0x8
+    field public static final int GEAR_FIFTH = 5; // 0x5
+    field public static final int GEAR_FIRST = 1; // 0x1
+    field public static final int GEAR_FOURTH = 4; // 0x4
+    field public static final int GEAR_NEUTRAL = 0; // 0x0
+    field public static final int GEAR_NINTH = 9; // 0x9
+    field public static final int GEAR_PARK = 101; // 0x65
+    field public static final int GEAR_REVERSE = 102; // 0x66
+    field public static final int GEAR_SECOND = 2; // 0x2
+    field public static final int GEAR_SEVENTH = 7; // 0x7
+    field public static final int GEAR_SIXTH = 6; // 0x6
+    field public static final int GEAR_TENTH = 10; // 0xa
+    field public static final int GEAR_THIRD = 3; // 0x3
+    field public static final int IGNITION_STATE_ACC = 3; // 0x3
+    field public static final int IGNITION_STATE_LOCK = 1; // 0x1
+    field public static final int IGNITION_STATE_OFF = 2; // 0x2
+    field public static final int IGNITION_STATE_ON = 4; // 0x4
+    field public static final int IGNITION_STATE_START = 5; // 0x5
+    field public static final int IGNITION_STATE_UNDEFINED = 0; // 0x0
+    field public static final int INDEX_ENVIRONMENT_PRESSURE = 1; // 0x1
+    field public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0; // 0x0
+    field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
+    field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
+    field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+    field public final float[] floatValues;
+    field public final int[] intValues;
+    field public int sensorType;
+    field public long timestamp;
+  }
+
+  public static class CarSensorEvent.EnvironmentData {
+    field public float pressure;
+    field public float temperature;
+    field public long timestamp;
+  }
+
+  public final class CarSensorManager {
+    method public android.car.hardware.CarSensorEvent getLatestSensorEvent(int) throws android.car.CarNotConnectedException;
+    method public int[] getSupportedSensors() throws android.car.CarNotConnectedException;
+    method public boolean isSensorSupported(int) throws android.car.CarNotConnectedException;
+    method public static boolean isSensorSupported(int[], int);
+    method public boolean registerListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+    method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener);
+    method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int);
+    field public static final int SENSOR_RATE_FAST = 1; // 0x1
+    field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
+    field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
+    field public static final int SENSOR_RATE_UI = 2; // 0x2
+    field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
+    field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
+    field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
+    field public static final int SENSOR_TYPE_FUEL_LEVEL = 5; // 0x5
+    field public static final int SENSOR_TYPE_GEAR = 7; // 0x7
+    field public static final int SENSOR_TYPE_IGNITION_STATE = 22; // 0x16
+    field public static final int SENSOR_TYPE_NIGHT = 9; // 0x9
+    field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
+    field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
+    field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+    field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+  }
+
+  public static abstract interface CarSensorManager.OnSensorChangedListener {
+    method public abstract void onSensorChanged(android.car.hardware.CarSensorEvent);
+  }
+
+}
+
+package android.car.media {
+
+  public final class CarAudioManager {
+    method public void abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+    method public android.media.AudioAttributes getAudioAttributesForCarUsage(int) throws android.car.CarNotConnectedException;
+    method public int getStreamMaxVolume(int) throws android.car.CarNotConnectedException;
+    method public int getStreamMinVolume(int) throws android.car.CarNotConnectedException;
+    method public int getStreamVolume(int) throws android.car.CarNotConnectedException;
+    method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+    method public void setStreamVolume(int, int, int) throws android.car.CarNotConnectedException;
+    field public static final int CAR_AUDIO_USAGE_ALARM = 6; // 0x6
+    field public static final int CAR_AUDIO_USAGE_DEFAULT = 0; // 0x0
+    field public static final int CAR_AUDIO_USAGE_MUSIC = 1; // 0x1
+    field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
+    field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
+    field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+    field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
+    field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
+    field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
+    field public static final int CAR_AUDIO_USAGE_VOICE_COMMAND = 5; // 0x5
+  }
+
+}
+
+package android.car.settings {
+
+  public class CarSettings {
+    ctor public CarSettings();
+  }
+
+  public static final class CarSettings.Global {
+    ctor public CarSettings.Global();
+    field public static final java.lang.String KEY_GARAGE_MODE_ENABLED = "android.car.GARAGE_MODE_ENABLED";
+    field public static final java.lang.String KEY_GARAGE_MODE_MAINTENANCE_WINDOW = "android.car.GARAGE_MODE_MAINTENANCE_WINDOW";
+    field public static final java.lang.String KEY_GARAGE_MODE_WAKE_UP_TIME = "android.car.GARAGE_MODE_WAKE_UP_TIME";
+  }
+
+}
+
diff --git a/car-lib/api/current.txt b/car-lib/api/current.txt
index fb94b19..242c337 100644
--- a/car-lib/api/current.txt
+++ b/car-lib/api/current.txt
@@ -11,6 +11,8 @@
     method public boolean isConnecting();
     field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
     field public static final java.lang.String AUDIO_SERVICE = "audio";
+    field public static final java.lang.String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
+    field public static final java.lang.String CAR_INTENT_ACTION_MEDIA_TEMPLATE = "android.car.intent.action.MEDIA_TEMPLATE";
     field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
     field public static final java.lang.String INFO_SERVICE = "info";
     field public static final java.lang.String PACKAGE_SERVICE = "package";
@@ -18,8 +20,9 @@
     field public static final java.lang.String PERMISSION_FUEL = "android.car.permission.CAR_FUEL";
     field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
     field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+    field public static final java.lang.String PERMISSION_VEHICLE_DYNAMICS_STATE = "android.car.permission.VEHICLE_DYNAMICS_STATE";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
-    field public static final int VERSION = 2; // 0x2
+    field public static final int VERSION = 3; // 0x3
   }
 
   public final class CarAppFocusManager {
@@ -213,8 +216,14 @@
     field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
     field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
     field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+    field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1
+    field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2
+    field public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4; // 0x4
+    field public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3; // 0x3
+    field public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0; // 0x0
     field public final float[] floatValues;
     field public final int[] intValues;
+    field public final long[] longValues;
     field public int sensorType;
     field public long timestamp;
   }
@@ -237,6 +246,7 @@
     field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
     field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
     field public static final int SENSOR_RATE_UI = 2; // 0x2
+    field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
     field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
     field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
     field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
@@ -247,7 +257,9 @@
     field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
     field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
     field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+    field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25; // 0x19
     field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+    field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23; // 0x17
   }
 
   public static abstract interface CarSensorManager.OnSensorChangedListener {
@@ -272,6 +284,7 @@
     field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
     field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
     field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+    field public static final int CAR_AUDIO_USAGE_RINGTONE = 10; // 0xa
     field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
     field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
     field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
diff --git a/car-lib/api/system-1.txt b/car-lib/api/system-1.txt
index 763114b..253c874 100644
--- a/car-lib/api/system-1.txt
+++ b/car-lib/api/system-1.txt
@@ -77,9 +77,9 @@
   public final class CarProjectionManager {
     method public void onCarDisconnected();
     method public void registerProjectionRunner(android.content.Intent) throws android.car.CarNotConnectedException;
-    method public void regsiterProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
+    method public void registerProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
     method public void unregisterProjectionRunner(android.content.Intent);
-    method public void unregsiterProjectionListener();
+    method public void unregisterProjectionListener();
     field public static final int PROJECTION_LONG_PRESS_VOICE_SEARCH = 2; // 0x2
     field public static final int PROJECTION_VOICE_SEARCH = 1; // 0x1
   }
diff --git a/car-lib/api/system-2.txt b/car-lib/api/system-2.txt
new file mode 100644
index 0000000..e8540ad
--- /dev/null
+++ b/car-lib/api/system-2.txt
@@ -0,0 +1,752 @@
+package android.car {
+
+  public final class Car {
+    method public void connect() throws java.lang.IllegalStateException;
+    method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection, android.os.Handler);
+    method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection);
+    method public void disconnect();
+    method public int getCarConnectionType();
+    method public java.lang.Object getCarManager(java.lang.String) throws android.car.CarNotConnectedException;
+    method public boolean isConnected();
+    method public boolean isConnecting();
+    field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
+    field public static final java.lang.String AUDIO_SERVICE = "audio";
+    field public static final java.lang.String CABIN_SERVICE = "cabin";
+    field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
+    field public static final java.lang.String DIAGNOSTIC_SERVICE = "diagnostic";
+    field public static final java.lang.String HVAC_SERVICE = "hvac";
+    field public static final java.lang.String INFO_SERVICE = "info";
+    field public static final java.lang.String PACKAGE_SERVICE = "package";
+    field public static final java.lang.String PERMISSION_CAR_CABIN = "android.car.permission.CAR_CABIN";
+    field public static final java.lang.String PERMISSION_CAR_CONTROL_AUDIO_VOLUME = "android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
+    field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
+    field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_READ = "android.car.permission.DIAGNOSTIC_READ";
+    field public static final java.lang.String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC";
+    field public static final java.lang.String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION";
+    field public static final java.lang.String PERMISSION_CAR_RADIO = "android.car.permission.CAR_RADIO";
+    field public static final java.lang.String PERMISSION_CAR_TEST_SERVICE = "android.car.permission.CAR_TEST_SERVICE";
+    field public static final java.lang.String PERMISSION_CONTROL_APP_BLOCKING = "android.car.permission.CONTROL_APP_BLOCKING";
+    field public static final java.lang.String PERMISSION_FUEL = "android.car.permission.CAR_FUEL";
+    field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
+    field public static final deprecated java.lang.String PERMISSION_MOCK_VEHICLE_HAL = "android.car.permission.CAR_MOCK_VEHICLE_HAL";
+    field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+    field public static final java.lang.String PERMISSION_VENDOR_EXTENSION = "android.car.permission.CAR_VENDOR_EXTENSION";
+    field public static final java.lang.String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
+    field public static final java.lang.String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
+    field public static final java.lang.String PROJECTION_SERVICE = "projection";
+    field public static final java.lang.String RADIO_SERVICE = "radio";
+    field public static final java.lang.String SENSOR_SERVICE = "sensor";
+    field public static final java.lang.String TEST_SERVICE = "car-service-test";
+    field public static final java.lang.String VENDOR_EXTENSION_SERVICE = "vendor_extension";
+    field public static final int VERSION = 2; // 0x2
+    field public static final java.lang.String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service";
+  }
+
+  public final class CarAppFocusManager {
+    method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int);
+    method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback);
+    method public void addFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int) throws android.car.CarNotConnectedException;
+    method public boolean isOwningFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int) throws android.car.CarNotConnectedException;
+    method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int);
+    method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener);
+    method public int requestAppFocus(int, android.car.CarAppFocusManager.OnAppFocusOwnershipCallback) throws android.car.CarNotConnectedException, java.lang.SecurityException;
+    field public static final int APP_FOCUS_REQUEST_FAILED = 0; // 0x0
+    field public static final int APP_FOCUS_REQUEST_SUCCEEDED = 1; // 0x1
+    field public static final int APP_FOCUS_TYPE_NAVIGATION = 1; // 0x1
+    field public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; // 0x2
+  }
+
+  public static abstract interface CarAppFocusManager.OnAppFocusChangedListener {
+    method public abstract void onAppFocusChanged(int, boolean);
+  }
+
+  public static abstract interface CarAppFocusManager.OnAppFocusOwnershipCallback {
+    method public abstract void onAppFocusOwnershipGranted(int);
+    method public abstract void onAppFocusOwnershipLost(int);
+  }
+
+  public final class CarInfoManager {
+    method public java.lang.String getManufacturer() throws android.car.CarNotConnectedException;
+    method public java.lang.String getModel() throws android.car.CarNotConnectedException;
+    method public java.lang.String getModelYear() throws android.car.CarNotConnectedException;
+    method public java.lang.String getVehicleId() throws android.car.CarNotConnectedException;
+  }
+
+  public class CarNotConnectedException extends java.lang.Exception {
+    ctor public CarNotConnectedException();
+    ctor public CarNotConnectedException(java.lang.String);
+    ctor public CarNotConnectedException(java.lang.String, java.lang.Throwable);
+    ctor public CarNotConnectedException(java.lang.Exception);
+  }
+
+  public final class CarProjectionManager {
+    method public void onCarDisconnected();
+    method public void registerProjectionRunner(android.content.Intent) throws android.car.CarNotConnectedException;
+    method public void regsiterProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
+    method public void unregisterProjectionRunner(android.content.Intent);
+    method public void unregsiterProjectionListener();
+    field public static final int PROJECTION_LONG_PRESS_VOICE_SEARCH = 2; // 0x2
+    field public static final int PROJECTION_VOICE_SEARCH = 1; // 0x1
+  }
+
+  public static abstract interface CarProjectionManager.CarProjectionListener {
+    method public abstract void onVoiceAssistantRequest(boolean);
+  }
+
+  public final class VehicleAreaType {
+    field public static final int VEHICLE_AREA_TYPE_DOOR = 4; // 0x4
+    field public static final int VEHICLE_AREA_TYPE_MIRROR = 5; // 0x5
+    field public static final int VEHICLE_AREA_TYPE_NONE = 0; // 0x0
+    field public static final int VEHICLE_AREA_TYPE_SEAT = 3; // 0x3
+    field public static final int VEHICLE_AREA_TYPE_WINDOW = 2; // 0x2
+    field public static final int VEHICLE_AREA_TYPE_ZONE = 1; // 0x1
+  }
+
+  public final class VehicleDoor {
+    field public static final int DOOR_HOOD = 268435456; // 0x10000000
+    field public static final int DOOR_REAR = 536870912; // 0x20000000
+    field public static final int DOOR_ROW_1_LEFT = 1; // 0x1
+    field public static final int DOOR_ROW_1_RIGHT = 4; // 0x4
+    field public static final int DOOR_ROW_2_LEFT = 16; // 0x10
+    field public static final int DOOR_ROW_2_RIGHT = 64; // 0x40
+    field public static final int DOOR_ROW_3_LEFT = 256; // 0x100
+    field public static final int DOOR_ROW_3_RIGHT = 1024; // 0x400
+  }
+
+  public final class VehicleMirror {
+    field public static final int MIRROR_DRIVER_CENTER = 4; // 0x4
+    field public static final int MIRROR_DRIVER_LEFT = 1; // 0x1
+    field public static final int MIRROR_DRIVER_RIGHT = 2; // 0x2
+  }
+
+  public final class VehicleSeat {
+    field public static final int SEAT_ROW_1_CENTER = 2; // 0x2
+    field public static final int SEAT_ROW_1_LEFT = 1; // 0x1
+    field public static final int SEAT_ROW_1_RIGHT = 4; // 0x4
+    field public static final int SEAT_ROW_2_CENTER = 32; // 0x20
+    field public static final int SEAT_ROW_2_LEFT = 16; // 0x10
+    field public static final int SEAT_ROW_2_RIGHT = 64; // 0x40
+    field public static final int SEAT_ROW_3_CENTER = 512; // 0x200
+    field public static final int SEAT_ROW_3_LEFT = 256; // 0x100
+    field public static final int SEAT_ROW_3_RIGHT = 1024; // 0x400
+  }
+
+  public final class VehicleWindow {
+    field public static final int WINDOW_FRONT_WINDSHIELD = 1; // 0x1
+    field public static final int WINDOW_REAR_WINDSHIELD = 2; // 0x2
+    field public static final int WINDOW_ROOF_TOP = 4; // 0x4
+    field public static final int WINDOW_ROW_1_LEFT = 16; // 0x10
+    field public static final int WINDOW_ROW_1_RIGHT = 32; // 0x20
+    field public static final int WINDOW_ROW_2_LEFT = 256; // 0x100
+    field public static final int WINDOW_ROW_2_RIGHT = 512; // 0x200
+    field public static final int WINDOW_ROW_3_LEFT = 4096; // 0x1000
+    field public static final int WINDOW_ROW_3_RIGHT = 8192; // 0x2000
+  }
+
+  public final class VehicleZone {
+    field public static final int ZONE_ALL = -2147483648; // 0x80000000
+    field public static final int ZONE_ROW_1_ALL = 8; // 0x8
+    field public static final int ZONE_ROW_1_CENTER = 2; // 0x2
+    field public static final int ZONE_ROW_1_LEFT = 1; // 0x1
+    field public static final int ZONE_ROW_1_RIGHT = 4; // 0x4
+    field public static final int ZONE_ROW_2_ALL = 128; // 0x80
+    field public static final int ZONE_ROW_2_CENTER = 32; // 0x20
+    field public static final int ZONE_ROW_2_LEFT = 16; // 0x10
+    field public static final int ZONE_ROW_2_RIGHT = 64; // 0x40
+    field public static final int ZONE_ROW_3_ALL = 2048; // 0x800
+    field public static final int ZONE_ROW_3_CENTER = 512; // 0x200
+    field public static final int ZONE_ROW_3_LEFT = 256; // 0x100
+    field public static final int ZONE_ROW_3_RIGHT = 1024; // 0x400
+    field public static final int ZONE_ROW_4_ALL = 32768; // 0x8000
+    field public static final int ZONE_ROW_4_CENTER = 8192; // 0x2000
+    field public static final int ZONE_ROW_4_LEFT = 4096; // 0x1000
+    field public static final int ZONE_ROW_4_RIGHT = 16384; // 0x4000
+  }
+
+  public final class VehicleZoneUtil {
+    method public static int getFirstZone(int);
+    method public static int getNextZone(int, int) throws java.lang.IllegalArgumentException;
+    method public static int getNumberOfZones(int);
+    method public static int[] listAllZones(int);
+    method public static int zoneToIndex(int, int) throws java.lang.IllegalArgumentException;
+  }
+
+}
+
+package android.car.app.menu {
+
+  public abstract class CarMenuCallbacks {
+    ctor public CarMenuCallbacks();
+    method public abstract android.car.app.menu.RootMenu getRootMenu(android.os.Bundle);
+    method public abstract void onCarMenuClosed();
+    method public abstract void onCarMenuClosing();
+    method public abstract void onCarMenuOpened();
+    method public abstract void onCarMenuOpening();
+    method public abstract void onItemClicked(java.lang.String);
+    method public abstract boolean onItemLongClicked(java.lang.String);
+    method public abstract boolean onMenuClicked();
+    method public abstract void subscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+    method public abstract void unsubscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+  }
+
+  public class CarMenuConstants {
+    ctor public CarMenuConstants();
+  }
+
+  public static class CarMenuConstants.MenuItemConstants {
+    ctor public CarMenuConstants.MenuItemConstants();
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_FIRSTITEM = 2; // 0x2
+    field public static final java.lang.String KEY_EMPTY_PLACEHOLDER = "android.car.app.menu.empty_placeholder";
+    field public static final java.lang.String KEY_FLAGS = "android.car.app.menu.flags";
+    field public static final java.lang.String KEY_ID = "android.car.app.menu.id";
+    field public static final java.lang.String KEY_LEFTICON = "android.car.app.menu.leftIcon";
+    field public static final java.lang.String KEY_REMOTEVIEWS = "android.car.app.menu.remoteViews";
+    field public static final java.lang.String KEY_RIGHTICON = "android.car.app.menu.rightIcon";
+    field public static final java.lang.String KEY_RIGHTTEXT = "android.car.app.menu.rightText";
+    field public static final java.lang.String KEY_TEXT = "android.car.app.menu.text";
+    field public static final java.lang.String KEY_TITLE = "android.car.app.menu.title";
+    field public static final java.lang.String KEY_WIDGET = "android.car.app.menu.widget";
+    field public static final java.lang.String KEY_WIDGET_STATE = "android.car.app.menu.widget_state";
+    field public static final int WIDGET_CHECKBOX = 1; // 0x1
+    field public static final int WIDGET_TEXT_VIEW = 2; // 0x2
+  }
+
+  public static abstract class CarMenuConstants.MenuItemConstants.MenuItemFlags implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class CarMenuConstants.MenuItemConstants.WidgetTypes implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class CarUiEntry {
+    ctor public CarUiEntry(android.content.Context, android.content.Context);
+    method public abstract void closeDrawer();
+    method public abstract android.view.View getContentView();
+    method public abstract int getFragmentContainerId();
+    method public abstract java.lang.CharSequence getSearchBoxText();
+    method public abstract void hideMenuButton();
+    method public abstract void hideTitle();
+    method public abstract void onPause();
+    method public abstract void onRestoreInstanceState(android.os.Bundle);
+    method public abstract void onResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract void openDrawer();
+    method public abstract void restoreMenuDrawable();
+    method public abstract void setAutoLightDarkMode();
+    method public abstract void setBackground(android.graphics.Bitmap);
+    method public abstract void setCarMenuCallbacks(android.car.app.menu.CarMenuCallbacks);
+    method public abstract void setDarkMode();
+    method public abstract void setLightMode();
+    method public abstract void setMenuButtonBitmap(android.graphics.Bitmap);
+    method public abstract void setMenuButtonColor(int);
+    method public abstract void setScrimColor(int);
+    method public abstract void setSearchBoxColors(int, int, int, int);
+    method public abstract void setSearchBoxEditListener(android.car.app.menu.SearchBoxEditListener);
+    method public abstract void setSearchBoxEndView(android.view.View);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void showMenu(java.lang.String, java.lang.String);
+    method public abstract void showSearchBox(android.view.View.OnClickListener);
+    method public abstract void showTitle();
+    method public abstract void showToast(java.lang.String, long);
+    method public abstract android.widget.EditText startInput(java.lang.String, android.view.View.OnClickListener);
+    method public abstract void stopInput();
+    field protected final android.content.Context mAppContext;
+    field protected final android.content.Context mUiLibContext;
+  }
+
+  public class RootMenu {
+    ctor public RootMenu(java.lang.String);
+    ctor public RootMenu(java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getBundle();
+    method public java.lang.String getId();
+  }
+
+  public abstract class SearchBoxEditListener {
+    ctor public SearchBoxEditListener();
+    method public abstract void onEdit(java.lang.String);
+    method public abstract void onSearch(java.lang.String);
+  }
+
+  public abstract class SubscriptionCallbacks {
+    ctor public SubscriptionCallbacks();
+    method public abstract void onChildChanged(java.lang.String, android.os.Bundle);
+    method public abstract void onChildrenLoaded(java.lang.String, java.util.List<android.os.Bundle>);
+    method public abstract void onError(java.lang.String);
+  }
+
+}
+
+package android.car.cluster.renderer {
+
+  public class DisplayConfiguration implements android.os.Parcelable {
+    ctor public DisplayConfiguration(android.os.Parcel);
+    ctor public DisplayConfiguration(android.graphics.Rect);
+    ctor public DisplayConfiguration(android.graphics.Rect, android.graphics.Rect);
+    method public int describeContents();
+    method public android.graphics.Rect getPrimaryRegion();
+    method public android.graphics.Rect getSecondaryRegion();
+    method public boolean hasSecondaryRegion();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.cluster.renderer.DisplayConfiguration> CREATOR;
+  }
+
+  public abstract class InstrumentClusterRenderer {
+    ctor public InstrumentClusterRenderer();
+    method protected abstract android.car.cluster.renderer.NavigationRenderer createNavigationRenderer();
+    method public synchronized android.car.cluster.renderer.NavigationRenderer getNavigationRenderer();
+    method public final synchronized void initialize();
+    method public abstract void onCreate(android.content.Context);
+    method public abstract void onStart();
+    method public abstract void onStop();
+  }
+
+  public abstract class InstrumentClusterRenderingService extends android.app.Service {
+    ctor public InstrumentClusterRenderingService();
+    method protected abstract android.car.cluster.renderer.NavigationRenderer getNavigationRenderer();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected void onKeyEvent(android.view.KeyEvent);
+  }
+
+  public abstract class NavigationRenderer {
+    ctor public NavigationRenderer();
+    method public abstract android.car.navigation.CarNavigationInstrumentCluster getNavigationProperties();
+    method public abstract void onNextTurnChanged(int, java.lang.CharSequence, int, int, android.graphics.Bitmap, int);
+    method public abstract void onNextTurnDistanceChanged(int, int, int, int);
+    method public abstract void onStartNavigation();
+    method public abstract void onStopNavigation();
+  }
+
+}
+
+package android.car.content.pm {
+
+  public class AppBlockingPackageInfo implements android.os.Parcelable {
+    ctor public AppBlockingPackageInfo(java.lang.String, int, int, int, android.content.pm.Signature[], java.lang.String[]);
+    ctor public AppBlockingPackageInfo(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.content.pm.AppBlockingPackageInfo> CREATOR;
+    field public static final int FLAG_SYSTEM_APP = 1; // 0x1
+    field public static final int FLAG_WHOLE_ACTIVITY = 2; // 0x2
+    field public final java.lang.String[] activities;
+    field public final int flags;
+    field public final int maxRevisionCode;
+    field public final int minRevisionCode;
+    field public final java.lang.String packageName;
+    field public final android.content.pm.Signature[] signatures;
+  }
+
+  public class CarAppBlockingPolicy implements android.os.Parcelable {
+    ctor public CarAppBlockingPolicy(android.car.content.pm.AppBlockingPackageInfo[], android.car.content.pm.AppBlockingPackageInfo[]);
+    ctor public CarAppBlockingPolicy(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.content.pm.CarAppBlockingPolicy> CREATOR;
+    field public final android.car.content.pm.AppBlockingPackageInfo[] blacklists;
+    field public final android.car.content.pm.AppBlockingPackageInfo[] whitelists;
+  }
+
+  public abstract class CarAppBlockingPolicyService extends android.app.Service {
+    ctor public CarAppBlockingPolicyService();
+    method protected abstract android.car.content.pm.CarAppBlockingPolicy getAppBlockingPolicy();
+    method public android.os.IBinder onBind(android.content.Intent);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.car.content.pm.CarAppBlockingPolicyService";
+  }
+
+  public final class CarPackageManager {
+    method public boolean isActivityAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+    method public boolean isActivityBackedBySafeActivity(android.content.ComponentName) throws android.car.CarNotConnectedException;
+    method public boolean isServiceAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+    method public void setAppBlockingPolicy(java.lang.String, android.car.content.pm.CarAppBlockingPolicy, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException, java.lang.SecurityException;
+    field public static final int FLAG_SET_POLICY_ADD = 2; // 0x2
+    field public static final int FLAG_SET_POLICY_REMOVE = 4; // 0x4
+    field public static final int FLAG_SET_POLICY_WAIT_FOR_CHANGE = 1; // 0x1
+  }
+
+}
+
+package android.car.hardware {
+
+  public class CarPropertyConfig<T> implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAreaCount();
+    method public int[] getAreaIds();
+    method public int getAreaType();
+    method public int getFirstAndOnlyAreaId();
+    method public T getMaxValue(int);
+    method public T getMaxValue();
+    method public T getMinValue(int);
+    method public T getMinValue();
+    method public int getPropertyId();
+    method public java.lang.Class<T> getPropertyType();
+    method public boolean hasArea(int);
+    method public boolean isGlobalProperty();
+    method public static <T> android.car.hardware.CarPropertyConfig.Builder<T> newBuilder(java.lang.Class<T>, int, int, int);
+    method public static <T> android.car.hardware.CarPropertyConfig.Builder<T> newBuilder(java.lang.Class<T>, int, int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.hardware.CarPropertyConfig> CREATOR;
+  }
+
+  public static class CarPropertyConfig.AreaConfig<T> implements android.os.Parcelable {
+    method public int describeContents();
+    method public T getMaxValue();
+    method public T getMinValue();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.hardware.CarPropertyConfig.AreaConfig<java.lang.Object>> CREATOR;
+  }
+
+  public static class CarPropertyConfig.Builder<T> {
+    method public android.car.hardware.CarPropertyConfig.Builder<T> addArea(int);
+    method public android.car.hardware.CarPropertyConfig.Builder<T> addAreaConfig(int, T, T);
+    method public android.car.hardware.CarPropertyConfig.Builder<T> addAreas(int[]);
+    method public android.car.hardware.CarPropertyConfig<T> build();
+  }
+
+  public class CarPropertyValue<T> implements android.os.Parcelable {
+    ctor public CarPropertyValue(int, T);
+    ctor public CarPropertyValue(int, int, T);
+    ctor public CarPropertyValue(android.os.Parcel);
+    method public int describeContents();
+    method public int getAreaId();
+    method public int getPropertyId();
+    method public T getValue();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.hardware.CarPropertyValue> CREATOR;
+  }
+
+  public class CarSensorEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.hardware.CarSensorEvent> CREATOR;
+    field public static final int DRIVE_STATUS_FULLY_RESTRICTED = 31; // 0x1f
+    field public static final int DRIVE_STATUS_LIMIT_MESSAGE_LEN = 16; // 0x10
+    field public static final int DRIVE_STATUS_NO_CONFIG = 8; // 0x8
+    field public static final int DRIVE_STATUS_NO_KEYBOARD_INPUT = 2; // 0x2
+    field public static final int DRIVE_STATUS_NO_VIDEO = 1; // 0x1
+    field public static final int DRIVE_STATUS_NO_VOICE_INPUT = 4; // 0x4
+    field public static final int DRIVE_STATUS_UNRESTRICTED = 0; // 0x0
+    field public static final int GEAR_DRIVE = 100; // 0x64
+    field public static final int GEAR_EIGHTH = 8; // 0x8
+    field public static final int GEAR_FIFTH = 5; // 0x5
+    field public static final int GEAR_FIRST = 1; // 0x1
+    field public static final int GEAR_FOURTH = 4; // 0x4
+    field public static final int GEAR_NEUTRAL = 0; // 0x0
+    field public static final int GEAR_NINTH = 9; // 0x9
+    field public static final int GEAR_PARK = 101; // 0x65
+    field public static final int GEAR_REVERSE = 102; // 0x66
+    field public static final int GEAR_SECOND = 2; // 0x2
+    field public static final int GEAR_SEVENTH = 7; // 0x7
+    field public static final int GEAR_SIXTH = 6; // 0x6
+    field public static final int GEAR_TENTH = 10; // 0xa
+    field public static final int GEAR_THIRD = 3; // 0x3
+    field public static final int IGNITION_STATE_ACC = 3; // 0x3
+    field public static final int IGNITION_STATE_LOCK = 1; // 0x1
+    field public static final int IGNITION_STATE_OFF = 2; // 0x2
+    field public static final int IGNITION_STATE_ON = 4; // 0x4
+    field public static final int IGNITION_STATE_START = 5; // 0x5
+    field public static final int IGNITION_STATE_UNDEFINED = 0; // 0x0
+    field public static final int INDEX_ENVIRONMENT_PRESSURE = 1; // 0x1
+    field public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0; // 0x0
+    field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
+    field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
+    field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+    field public final float[] floatValues;
+    field public final int[] intValues;
+    field public int sensorType;
+    field public long timestamp;
+  }
+
+  public static class CarSensorEvent.EnvironmentData {
+    field public float pressure;
+    field public float temperature;
+    field public long timestamp;
+  }
+
+  public final class CarSensorManager {
+    method public android.car.hardware.CarSensorEvent getLatestSensorEvent(int) throws android.car.CarNotConnectedException;
+    method public int[] getSupportedSensors() throws android.car.CarNotConnectedException;
+    method public boolean isSensorSupported(int) throws android.car.CarNotConnectedException;
+    method public static boolean isSensorSupported(int[], int);
+    method public boolean registerListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+    method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener);
+    method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int);
+    field public static final int SENSOR_RATE_FAST = 1; // 0x1
+    field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
+    field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
+    field public static final int SENSOR_RATE_UI = 2; // 0x2
+    field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
+    field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
+    field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
+    field public static final int SENSOR_TYPE_FUEL_LEVEL = 5; // 0x5
+    field public static final int SENSOR_TYPE_GEAR = 7; // 0x7
+    field public static final int SENSOR_TYPE_IGNITION_STATE = 22; // 0x16
+    field public static final int SENSOR_TYPE_NIGHT = 9; // 0x9
+    field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
+    field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
+    field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+    field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+  }
+
+  public static abstract interface CarSensorManager.OnSensorChangedListener {
+    method public abstract void onSensorChanged(android.car.hardware.CarSensorEvent);
+  }
+
+  public final class CarVendorExtensionManager {
+    method public <E> E getGlobalProperty(java.lang.Class<E>, int) throws android.car.CarNotConnectedException;
+    method public java.util.List<android.car.hardware.CarPropertyConfig> getProperties() throws android.car.CarNotConnectedException;
+    method public <E> E getProperty(java.lang.Class<E>, int, int) throws android.car.CarNotConnectedException;
+    method public void registerCallback(android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback) throws android.car.CarNotConnectedException;
+    method public <E> void setGlobalProperty(java.lang.Class<E>, int, E) throws android.car.CarNotConnectedException;
+    method public <E> void setProperty(java.lang.Class<E>, int, int, E) throws android.car.CarNotConnectedException;
+    method public void unregisterCallback(android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback);
+  }
+
+  public static abstract interface CarVendorExtensionManager.CarVendorExtensionCallback {
+    method public abstract void onChangeEvent(android.car.hardware.CarPropertyValue);
+    method public abstract void onErrorEvent(int, int);
+  }
+
+}
+
+package android.car.hardware.cabin {
+
+  public final class CarCabinManager {
+    method public boolean getBooleanProperty(int, int) throws android.car.CarNotConnectedException;
+    method public float getFloatProperty(int, int) throws android.car.CarNotConnectedException;
+    method public int getIntProperty(int, int) throws android.car.CarNotConnectedException;
+    method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
+    method public static boolean isZonedProperty(int);
+    method public synchronized void registerCallback(android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback) throws android.car.CarNotConnectedException;
+    method public void setBooleanProperty(int, int, boolean) throws android.car.CarNotConnectedException;
+    method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
+    method public void setIntProperty(int, int, int) throws android.car.CarNotConnectedException;
+    method public synchronized void unregisterCallback(android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback);
+    field public static final int ID_DOOR_LOCK = 3; // 0x3
+    field public static final int ID_DOOR_MOVE = 2; // 0x2
+    field public static final int ID_DOOR_POS = 1; // 0x1
+    field public static final int ID_MIRROR_FOLD = 4102; // 0x1006
+    field public static final int ID_MIRROR_LOCK = 4101; // 0x1005
+    field public static final int ID_MIRROR_Y_MOVE = 4100; // 0x1004
+    field public static final int ID_MIRROR_Y_POS = 4099; // 0x1003
+    field public static final int ID_MIRROR_Z_MOVE = 4098; // 0x1002
+    field public static final int ID_MIRROR_Z_POS = 4097; // 0x1001
+    field public static final int ID_SEAT_BACKREST_ANGLE_1_MOVE = 8201; // 0x2009
+    field public static final int ID_SEAT_BACKREST_ANGLE_1_POS = 8200; // 0x2008
+    field public static final int ID_SEAT_BACKREST_ANGLE_2_MOVE = 8203; // 0x200b
+    field public static final int ID_SEAT_BACKREST_ANGLE_2_POS = 8202; // 0x200a
+    field public static final int ID_SEAT_BELT_BUCKLED = 8195; // 0x2003
+    field public static final int ID_SEAT_BELT_HEIGHT_MOVE = 8197; // 0x2005
+    field public static final int ID_SEAT_BELT_HEIGHT_POS = 8196; // 0x2004
+    field public static final int ID_SEAT_DEPTH_MOVE = 8207; // 0x200f
+    field public static final int ID_SEAT_DEPTH_POS = 8206; // 0x200e
+    field public static final int ID_SEAT_FORE_AFT_MOVE = 8199; // 0x2007
+    field public static final int ID_SEAT_FORE_AFT_POS = 8198; // 0x2006
+    field public static final int ID_SEAT_HEADREST_ANGLE_MOVE = 8217; // 0x2019
+    field public static final int ID_SEAT_HEADREST_ANGLE_POS = 8216; // 0x2018
+    field public static final int ID_SEAT_HEADREST_FORE_AFT_MOVE = 8219; // 0x201b
+    field public static final int ID_SEAT_HEADREST_FORE_AFT_POS = 8218; // 0x201a
+    field public static final int ID_SEAT_HEADREST_HEIGHT_MOVE = 8215; // 0x2017
+    field public static final int ID_SEAT_HEADREST_HEIGHT_POS = 8214; // 0x2016
+    field public static final int ID_SEAT_HEIGHT_MOVE = 8205; // 0x200d
+    field public static final int ID_SEAT_HEIGHT_POS = 8204; // 0x200c
+    field public static final int ID_SEAT_LUMBAR_FORE_AFT_MOVE = 8211; // 0x2013
+    field public static final int ID_SEAT_LUMBAR_FORE_AFT_POS = 8210; // 0x2012
+    field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 8213; // 0x2015
+    field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_POS = 8212; // 0x2014
+    field public static final int ID_SEAT_MEMORY_SELECT = 8193; // 0x2001
+    field public static final int ID_SEAT_MEMORY_SET = 8194; // 0x2002
+    field public static final int ID_SEAT_TILT_MOVE = 8209; // 0x2011
+    field public static final int ID_SEAT_TILT_POS = 8208; // 0x2010
+    field public static final int ID_WINDOW_LOCK = 12293; // 0x3005
+    field public static final int ID_WINDOW_MOVE = 12290; // 0x3002
+    field public static final int ID_WINDOW_POS = 12289; // 0x3001
+    field public static final int ID_WINDOW_VENT_MOVE = 12292; // 0x3004
+    field public static final int ID_WINDOW_VENT_POS = 12291; // 0x3003
+  }
+
+  public static abstract interface CarCabinManager.CarCabinEventCallback {
+    method public abstract void onChangeEvent(android.car.hardware.CarPropertyValue);
+    method public abstract void onErrorEvent(int, int);
+  }
+
+}
+
+package android.car.hardware.hvac {
+
+  public final class CarHvacManager {
+    method public boolean getBooleanProperty(int, int) throws android.car.CarNotConnectedException;
+    method public float getFloatProperty(int, int) throws android.car.CarNotConnectedException;
+    method public int getIntProperty(int, int) throws android.car.CarNotConnectedException;
+    method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
+    method public static boolean isZonedProperty(int);
+    method public synchronized void registerCallback(android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback) throws android.car.CarNotConnectedException;
+    method public void setBooleanProperty(int, int, boolean) throws android.car.CarNotConnectedException;
+    method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
+    method public void setIntProperty(int, int, int) throws android.car.CarNotConnectedException;
+    method public synchronized void unregisterCallback(android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback);
+    field public static final int FAN_POSITION_DEFROST = 4; // 0x4
+    field public static final int FAN_POSITION_DEFROST_AND_FLOOR = 5; // 0x5
+    field public static final int FAN_POSITION_FACE = 1; // 0x1
+    field public static final int FAN_POSITION_FACE_AND_FLOOR = 3; // 0x3
+    field public static final int FAN_POSITION_FLOOR = 2; // 0x2
+    field public static final int ID_MIRROR_DEFROSTER_ON = 1; // 0x1
+    field public static final int ID_OUTSIDE_AIR_TEMP = 3; // 0x3
+    field public static final int ID_STEERING_WHEEL_TEMP = 2; // 0x2
+    field public static final int ID_TEMPERATURE_UNITS = 4; // 0x4
+    field public static final int ID_WINDOW_DEFROSTER_ON = 20481; // 0x5001
+    field public static final int ID_ZONED_AC_ON = 16393; // 0x4009
+    field public static final int ID_ZONED_AIR_RECIRCULATION_ON = 16395; // 0x400b
+    field public static final int ID_ZONED_AUTOMATIC_MODE_ON = 16394; // 0x400a
+    field public static final int ID_ZONED_DUAL_ZONE_ON = 16397; // 0x400d
+    field public static final int ID_ZONED_FAN_POSITION = 16391; // 0x4007
+    field public static final int ID_ZONED_FAN_POSITION_AVAILABLE = 16390; // 0x4006
+    field public static final int ID_ZONED_FAN_SPEED_RPM = 16389; // 0x4005
+    field public static final int ID_ZONED_FAN_SPEED_SETPOINT = 16388; // 0x4004
+    field public static final int ID_ZONED_HVAC_POWER_ON = 16387; // 0x4003
+    field public static final int ID_ZONED_MAX_AC_ON = 16396; // 0x400c
+    field public static final int ID_ZONED_MAX_DEFROST_ON = 16398; // 0x400e
+    field public static final int ID_ZONED_SEAT_TEMP = 16392; // 0x4008
+    field public static final int ID_ZONED_TEMP_ACTUAL = 16386; // 0x4002
+    field public static final int ID_ZONED_TEMP_SETPOINT = 16385; // 0x4001
+  }
+
+  public static abstract interface CarHvacManager.CarHvacEventCallback {
+    method public abstract void onChangeEvent(android.car.hardware.CarPropertyValue);
+    method public abstract void onErrorEvent(int, int);
+  }
+
+}
+
+package android.car.hardware.radio {
+
+  public class CarRadioEvent implements android.os.Parcelable {
+    ctor public CarRadioEvent(int, android.car.hardware.radio.CarRadioPreset);
+    method public int describeContents();
+    method public int getEventType();
+    method public android.car.hardware.radio.CarRadioPreset getPreset();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.hardware.radio.CarRadioEvent> CREATOR;
+    field public static final int RADIO_PRESET = 0; // 0x0
+  }
+
+  public final class CarRadioManager {
+    method public android.car.hardware.radio.CarRadioPreset getPreset(int) throws android.car.CarNotConnectedException;
+    method public int getPresetCount() throws android.car.CarNotConnectedException;
+    method public synchronized void registerListener(android.car.hardware.radio.CarRadioManager.CarRadioEventListener) throws android.car.CarNotConnectedException;
+    method public boolean setPreset(android.car.hardware.radio.CarRadioPreset) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+    method public synchronized void unregisterListener();
+  }
+
+  public static abstract interface CarRadioManager.CarRadioEventListener {
+    method public abstract void onEvent(android.car.hardware.radio.CarRadioEvent);
+  }
+
+  public class CarRadioPreset implements android.os.Parcelable {
+    ctor public CarRadioPreset(int, int, int, int);
+    method public int describeContents();
+    method public int getBand();
+    method public int getChannel();
+    method public int getPresetNumber();
+    method public int getSubChannel();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.hardware.radio.CarRadioPreset> CREATOR;
+  }
+
+}
+
+package android.car.input {
+
+  public abstract class CarInputHandlingService extends android.app.Service {
+    ctor protected CarInputHandlingService(android.car.input.CarInputHandlingService.InputFilter[]);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract void onKeyEvent(android.view.KeyEvent, int);
+    field public static final int INPUT_CALLBACK_BINDER_CODE = 1; // 0x1
+    field public static final java.lang.String INPUT_CALLBACK_BINDER_KEY = "callback_binder";
+  }
+
+  public static class CarInputHandlingService.InputFilter implements android.os.Parcelable {
+    ctor public CarInputHandlingService.InputFilter(int, int);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public final int mKeyCode;
+    field public final int mTargetDisplay;
+  }
+
+}
+
+package android.car.media {
+
+  public final class CarAudioManager {
+    method public void abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+    method public android.media.AudioAttributes getAudioAttributesForCarUsage(int) throws android.car.CarNotConnectedException;
+    method public int getStreamMaxVolume(int) throws android.car.CarNotConnectedException;
+    method public int getStreamMinVolume(int) throws android.car.CarNotConnectedException;
+    method public int getStreamVolume(int) throws android.car.CarNotConnectedException;
+    method public boolean isMediaMuted() throws android.car.CarNotConnectedException;
+    method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+    method public boolean setMediaMute(boolean) throws android.car.CarNotConnectedException;
+    method public void setStreamVolume(int, int, int) throws android.car.CarNotConnectedException;
+    method public void setVolumeController(android.media.IVolumeController) throws android.car.CarNotConnectedException;
+    field public static final int CAR_AUDIO_USAGE_ALARM = 6; // 0x6
+    field public static final int CAR_AUDIO_USAGE_DEFAULT = 0; // 0x0
+    field public static final int CAR_AUDIO_USAGE_MUSIC = 1; // 0x1
+    field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
+    field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
+    field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+    field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
+    field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
+    field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
+    field public static final int CAR_AUDIO_USAGE_VOICE_COMMAND = 5; // 0x5
+  }
+
+}
+
+package android.car.navigation {
+
+  public class CarNavigationInstrumentCluster implements android.os.Parcelable {
+    ctor public CarNavigationInstrumentCluster(android.car.navigation.CarNavigationInstrumentCluster);
+    method public static android.car.navigation.CarNavigationInstrumentCluster createCluster(int);
+    method public static android.car.navigation.CarNavigationInstrumentCluster createCustomImageCluster(int, int, int, int);
+    method public int describeContents();
+    method public int getImageColorDepthBits();
+    method public int getImageHeight();
+    method public int getImageWidth();
+    method public int getMinIntervalMillis();
+    method public int getType();
+    method public boolean supportsCustomImages();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED = 1; // 0x1
+    field public static final int CLUSTER_TYPE_IMAGE_CODES_ONLY = 2; // 0x2
+    field public static final android.os.Parcelable.Creator<android.car.navigation.CarNavigationInstrumentCluster> CREATOR;
+  }
+
+}
+
+package android.car.settings {
+
+  public class CarSettings {
+    ctor public CarSettings();
+  }
+
+  public static final class CarSettings.Global {
+    ctor public CarSettings.Global();
+    field public static final java.lang.String KEY_GARAGE_MODE_ENABLED = "android.car.GARAGE_MODE_ENABLED";
+    field public static final java.lang.String KEY_GARAGE_MODE_MAINTENANCE_WINDOW = "android.car.GARAGE_MODE_MAINTENANCE_WINDOW";
+    field public static final java.lang.String KEY_GARAGE_MODE_WAKE_UP_TIME = "android.car.GARAGE_MODE_WAKE_UP_TIME";
+  }
+
+}
+
+package android.car.test {
+
+  public class CarTestManagerBinderWrapper {
+    ctor public CarTestManagerBinderWrapper(android.os.IBinder);
+    method public void onCarDisconnected();
+    field public final android.os.IBinder binder;
+  }
+
+}
+
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index b846481..6f32dd7 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -12,12 +12,17 @@
     field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
     field public static final java.lang.String AUDIO_SERVICE = "audio";
     field public static final java.lang.String CABIN_SERVICE = "cabin";
+    field public static final java.lang.String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
+    field public static final java.lang.String CAR_INTENT_ACTION_MEDIA_TEMPLATE = "android.car.intent.action.MEDIA_TEMPLATE";
     field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
+    field public static final java.lang.String DIAGNOSTIC_SERVICE = "diagnostic";
     field public static final java.lang.String HVAC_SERVICE = "hvac";
     field public static final java.lang.String INFO_SERVICE = "info";
     field public static final java.lang.String PACKAGE_SERVICE = "package";
     field public static final java.lang.String PERMISSION_CAR_CABIN = "android.car.permission.CAR_CABIN";
     field public static final java.lang.String PERMISSION_CAR_CONTROL_AUDIO_VOLUME = "android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
+    field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
+    field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_READ_ALL = "android.car.permission.DIAGNOSTIC_READ_ALL";
     field public static final java.lang.String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC";
     field public static final java.lang.String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION";
     field public static final java.lang.String PERMISSION_CAR_RADIO = "android.car.permission.CAR_RADIO";
@@ -27,13 +32,14 @@
     field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
     field public static final deprecated java.lang.String PERMISSION_MOCK_VEHICLE_HAL = "android.car.permission.CAR_MOCK_VEHICLE_HAL";
     field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+    field public static final java.lang.String PERMISSION_VEHICLE_DYNAMICS_STATE = "android.car.permission.VEHICLE_DYNAMICS_STATE";
     field public static final java.lang.String PERMISSION_VENDOR_EXTENSION = "android.car.permission.CAR_VENDOR_EXTENSION";
     field public static final java.lang.String PROJECTION_SERVICE = "projection";
     field public static final java.lang.String RADIO_SERVICE = "radio";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
     field public static final java.lang.String TEST_SERVICE = "car-service-test";
     field public static final java.lang.String VENDOR_EXTENSION_SERVICE = "vendor_extension";
-    field public static final int VERSION = 2; // 0x2
+    field public static final int VERSION = 3; // 0x3
   }
 
   public final class CarAppFocusManager {
@@ -75,10 +81,10 @@
 
   public final class CarProjectionManager {
     method public void onCarDisconnected();
+    method public void registerProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
     method public void registerProjectionRunner(android.content.Intent) throws android.car.CarNotConnectedException;
-    method public void regsiterProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
+    method public void unregisterProjectionListener();
     method public void unregisterProjectionRunner(android.content.Intent);
-    method public void unregsiterProjectionListener();
     field public static final int PROJECTION_LONG_PRESS_VOICE_SEARCH = 2; // 0x2
     field public static final int PROJECTION_VOICE_SEARCH = 1; // 0x1
   }
@@ -274,18 +280,6 @@
 
 package android.car.cluster.renderer {
 
-  public class DisplayConfiguration implements android.os.Parcelable {
-    ctor public DisplayConfiguration(android.os.Parcel);
-    ctor public DisplayConfiguration(android.graphics.Rect);
-    ctor public DisplayConfiguration(android.graphics.Rect, android.graphics.Rect);
-    method public int describeContents();
-    method public android.graphics.Rect getPrimaryRegion();
-    method public android.graphics.Rect getSecondaryRegion();
-    method public boolean hasSecondaryRegion();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.car.cluster.renderer.DisplayConfiguration> CREATOR;
-  }
-
   public abstract class InstrumentClusterRenderer {
     ctor public InstrumentClusterRenderer();
     method protected abstract android.car.cluster.renderer.NavigationRenderer createNavigationRenderer();
@@ -361,6 +355,265 @@
 
 }
 
+package android.car.diagnostic {
+
+  public class CarDiagnosticEvent implements android.os.Parcelable {
+    ctor public CarDiagnosticEvent(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.Integer getFuelSystemStatus();
+    method public java.lang.Integer getFuelType();
+    method public android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors getIgnitionMonitors();
+    method public java.lang.Integer getSecondaryAirStatus();
+    method public float getSystemFloatSensor(int, float);
+    method public java.lang.Float getSystemFloatSensor(int);
+    method public int getSystemIntegerSensor(int, int);
+    method public java.lang.Integer getSystemIntegerSensor(int);
+    method public float getVendorFloatSensor(int, float);
+    method public java.lang.Float getVendorFloatSensor(int);
+    method public int getVendorIntegerSensor(int, int);
+    method public java.lang.Integer getVendorIntegerSensor(int);
+    method public boolean isFreezeFrame();
+    method public boolean isLiveFrame();
+    method public void writeToJson(android.util.JsonWriter) throws java.io.IOException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.car.diagnostic.CarDiagnosticEvent> CREATOR;
+    field public final java.lang.String dtc;
+    field public final int frameType;
+    field public final long timestamp;
+  }
+
+  public static class CarDiagnosticEvent.Builder {
+    method public android.car.diagnostic.CarDiagnosticEvent.Builder atTimestamp(long);
+    method public android.car.diagnostic.CarDiagnosticEvent build();
+    method public static android.car.diagnostic.CarDiagnosticEvent.Builder newFreezeFrameBuilder();
+    method public static android.car.diagnostic.CarDiagnosticEvent.Builder newLiveFrameBuilder();
+    method public android.car.diagnostic.CarDiagnosticEvent.Builder withDtc(java.lang.String);
+    method public android.car.diagnostic.CarDiagnosticEvent.Builder withFloatValue(int, float);
+    method public android.car.diagnostic.CarDiagnosticEvent.Builder withIntValue(int, int);
+  }
+
+  public static class CarDiagnosticEvent.CommonIgnitionMonitors {
+    method public android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors asCompressionIgnitionMonitors();
+    method public android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors asSparkIgnitionMonitors();
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor components;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor fuelSystem;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor misfire;
+  }
+
+  public static final class CarDiagnosticEvent.CompressionIgnitionMonitors extends android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors {
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor EGROrVVT;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor NMHCCatalyst;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor NOxSCR;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor PMFilter;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor boostPressure;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor exhaustGasSensor;
+  }
+
+  public static final class CarDiagnosticEvent.FuelSystemStatus {
+    field public static final int CLOSED_LOOP = 2; // 0x2
+    field public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16; // 0x10
+    field public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4; // 0x4
+    field public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1; // 0x1
+    field public static final int OPEN_SYSTEM_FAILURE = 8; // 0x8
+  }
+
+  public static abstract class CarDiagnosticEvent.FuelSystemStatus.Status implements java.lang.annotation.Annotation {
+  }
+
+  public static final class CarDiagnosticEvent.FuelType {
+    field public static final int BIFUEL_RUNNING_CNG = 13; // 0xd
+    field public static final int BIFUEL_RUNNING_DIESEL = 23; // 0x17
+    field public static final int BIFUEL_RUNNING_ELECTRIC = 15; // 0xf
+    field public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16; // 0x10
+    field public static final int BIFUEL_RUNNING_ETHANOL = 11; // 0xb
+    field public static final int BIFUEL_RUNNING_GASOLINE = 9; // 0x9
+    field public static final int BIFUEL_RUNNING_LPG = 12; // 0xc
+    field public static final int BIFUEL_RUNNING_METHANOL = 10; // 0xa
+    field public static final int BIFUEL_RUNNING_PROPANE = 14; // 0xe
+    field public static final int CNG = 6; // 0x6
+    field public static final int DIESEL = 4; // 0x4
+    field public static final int ELECTRIC = 8; // 0x8
+    field public static final int ETHANOL = 3; // 0x3
+    field public static final int GASOLINE = 1; // 0x1
+    field public static final int HYBRID_DIESEL = 19; // 0x13
+    field public static final int HYBRID_ELECTRIC = 20; // 0x14
+    field public static final int HYBRID_ETHANOL = 18; // 0x12
+    field public static final int HYBRID_GASOLINE = 17; // 0x11
+    field public static final int HYBRID_REGENERATIVE = 22; // 0x16
+    field public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21; // 0x15
+    field public static final int LPG = 5; // 0x5
+    field public static final int METHANOL = 2; // 0x2
+    field public static final int NOT_AVAILABLE = 0; // 0x0
+    field public static final int PROPANE = 7; // 0x7
+  }
+
+  public static abstract class CarDiagnosticEvent.FuelType.Type implements java.lang.annotation.Annotation {
+  }
+
+  public static final class CarDiagnosticEvent.IgnitionMonitor {
+    field public final boolean available;
+    field public final boolean incomplete;
+  }
+
+  public static final class CarDiagnosticEvent.SecondaryAirStatus {
+    field public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2; // 0x2
+    field public static final int FROM_OUTSIDE_OR_OFF = 4; // 0x4
+    field public static final int PUMP_ON_FOR_DIAGNOSTICS = 8; // 0x8
+    field public static final int UPSTREAM = 1; // 0x1
+  }
+
+  public static abstract class CarDiagnosticEvent.SecondaryAirStatus.Status implements java.lang.annotation.Annotation {
+  }
+
+  public static final class CarDiagnosticEvent.SparkIgnitionMonitors extends android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors {
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor ACRefrigerant;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor EGR;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor catalyst;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor evaporativeSystem;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor heatedCatalyst;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor oxygenSensor;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor oxygenSensorHeater;
+    field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor secondaryAirSystem;
+  }
+
+  public final class CarDiagnosticManager {
+    method public boolean clearFreezeFrames(long...) throws android.car.CarNotConnectedException;
+    method public android.car.diagnostic.CarDiagnosticEvent getFreezeFrame(long) throws android.car.CarNotConnectedException;
+    method public long[] getFreezeFrameTimestamps() throws android.car.CarNotConnectedException;
+    method public android.car.diagnostic.CarDiagnosticEvent getLatestLiveFrame() throws android.car.CarNotConnectedException;
+    method public boolean isClearFreezeFramesSupported() throws android.car.CarNotConnectedException;
+    method public boolean isFreezeFrameNotificationSupported() throws android.car.CarNotConnectedException;
+    method public boolean isGetFreezeFrameSupported() throws android.car.CarNotConnectedException;
+    method public boolean isLiveFrameSupported() throws android.car.CarNotConnectedException;
+    method public void onCarDisconnected();
+    method public boolean registerListener(android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+    method public void unregisterListener(android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener);
+    field public static final int FRAME_TYPE_FREEZE = 1; // 0x1
+    field public static final int FRAME_TYPE_LIVE = 0; // 0x0
+  }
+
+  public static abstract class CarDiagnosticManager.FrameType implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract interface CarDiagnosticManager.OnDiagnosticEventListener {
+    method public abstract void onDiagnosticEvent(android.car.diagnostic.CarDiagnosticEvent);
+  }
+
+  public final class FloatSensorIndex {
+    field public static final int ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58; // 0x3a
+    field public static final int ABSOLUTE_LOAD_VALUE = 48; // 0x30
+    field public static final int ABSOLUTE_THROTTLE_POSITION_B = 51; // 0x33
+    field public static final int ABSOLUTE_THROTTLE_POSITION_C = 52; // 0x34
+    field public static final int ACCELERATOR_PEDAL_POSITION_D = 53; // 0x35
+    field public static final int ACCELERATOR_PEDAL_POSITION_E = 54; // 0x36
+    field public static final int ACCELERATOR_PEDAL_POSITION_F = 55; // 0x37
+    field public static final int CALCULATED_ENGINE_LOAD = 0; // 0x0
+    field public static final int CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44; // 0x2c
+    field public static final int CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46; // 0x2e
+    field public static final int CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45; // 0x2d
+    field public static final int CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47; // 0x2f
+    field public static final int COMMANDED_EVAPORATIVE_PURGE = 41; // 0x29
+    field public static final int COMMANDED_EXHAUST_GAS_RECIRCULATION = 39; // 0x27
+    field public static final int COMMANDED_THROTTLE_ACTUATOR = 56; // 0x38
+    field public static final int ENGINE_COOLANT_TEMPERATURE = 1; // 0x1
+    field public static final int ENGINE_FUEL_RATE = 70; // 0x46
+    field public static final int ENGINE_RPM = 8; // 0x8
+    field public static final int ETHANOL_FUEL_PERCENTAGE = 57; // 0x39
+    field public static final int EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43; // 0x2b
+    field public static final int EXHAUST_GAS_RECIRCULATION_ERROR = 40; // 0x28
+    field public static final int FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49; // 0x31
+    field public static final int FUEL_INJECTION_TIMING = 69; // 0x45
+    field public static final int FUEL_PRESSURE = 6; // 0x6
+    field public static final int FUEL_RAIL_GAUGE_PRESSURE = 38; // 0x26
+    field public static final int FUEL_RAIL_PRESSURE = 37; // 0x25
+    field public static final int FUEL_TANK_LEVEL_INPUT = 42; // 0x2a
+    field public static final int HYBRID_BATTERY_PACK_REMAINING_LIFE = 68; // 0x44
+    field public static final int INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7; // 0x7
+    field public static final int LAST_SYSTEM = 70; // 0x46
+    field public static final int LONG_TERM_FUEL_TRIM_BANK1 = 3; // 0x3
+    field public static final int LONG_TERM_FUEL_TRIM_BANK2 = 5; // 0x5
+    field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63; // 0x3f
+    field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64; // 0x40
+    field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65; // 0x41
+    field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66; // 0x42
+    field public static final int MAF_AIR_FLOW_RATE = 11; // 0xb
+    field public static final int OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15; // 0xf
+    field public static final int OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14; // 0xe
+    field public static final int OXYGEN_SENSOR1_VOLTAGE = 13; // 0xd
+    field public static final int OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18; // 0x12
+    field public static final int OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17; // 0x11
+    field public static final int OXYGEN_SENSOR2_VOLTAGE = 16; // 0x10
+    field public static final int OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21; // 0x15
+    field public static final int OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20; // 0x14
+    field public static final int OXYGEN_SENSOR3_VOLTAGE = 19; // 0x13
+    field public static final int OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24; // 0x18
+    field public static final int OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23; // 0x17
+    field public static final int OXYGEN_SENSOR4_VOLTAGE = 22; // 0x16
+    field public static final int OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27; // 0x1b
+    field public static final int OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26; // 0x1a
+    field public static final int OXYGEN_SENSOR5_VOLTAGE = 25; // 0x19
+    field public static final int OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30; // 0x1e
+    field public static final int OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29; // 0x1d
+    field public static final int OXYGEN_SENSOR6_VOLTAGE = 28; // 0x1c
+    field public static final int OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33; // 0x21
+    field public static final int OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32; // 0x20
+    field public static final int OXYGEN_SENSOR7_VOLTAGE = 31; // 0x1f
+    field public static final int OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36; // 0x24
+    field public static final int OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35; // 0x23
+    field public static final int OXYGEN_SENSOR8_VOLTAGE = 34; // 0x22
+    field public static final int RELATIVE_ACCELERATOR_PEDAL_POSITION = 67; // 0x43
+    field public static final int RELATIVE_THROTTLE_POSITION = 50; // 0x32
+    field public static final int SHORT_TERM_FUEL_TRIM_BANK1 = 2; // 0x2
+    field public static final int SHORT_TERM_FUEL_TRIM_BANK2 = 4; // 0x4
+    field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59; // 0x3b
+    field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60; // 0x3c
+    field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61; // 0x3d
+    field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62; // 0x3e
+    field public static final int THROTTLE_POSITION = 12; // 0xc
+    field public static final int TIMING_ADVANCE = 10; // 0xa
+    field public static final int VEHICLE_SPEED = 9; // 0x9
+    field public static final int VENDOR_START = 71; // 0x47
+  }
+
+  public final class IntegerSensorIndex {
+    field public static final int ABSOLUTE_BAROMETRIC_PRESSURE = 11; // 0xb
+    field public static final int AMBIENT_AIR_TEMPERATURE = 13; // 0xd
+    field public static final int COMMANDED_SECONDARY_AIR_STATUS = 5; // 0x5
+    field public static final int CONTROL_MODULE_VOLTAGE = 12; // 0xc
+    field public static final int DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10; // 0xa
+    field public static final int DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8; // 0x8
+    field public static final int DRIVER_DEMAND_PERCENT_TORQUE = 24; // 0x18
+    field public static final int ENGINE_ACTUAL_PERCENT_TORQUE = 25; // 0x19
+    field public static final int ENGINE_OIL_TEMPERATURE = 23; // 0x17
+    field public static final int ENGINE_PERCENT_TORQUE_DATA_IDLE = 27; // 0x1b
+    field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28; // 0x1c
+    field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29; // 0x1d
+    field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30; // 0x1e
+    field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31; // 0x1f
+    field public static final int ENGINE_REFERENCE_PERCENT_TORQUE = 26; // 0x1a
+    field public static final int FUEL_RAIL_ABSOLUTE_PRESSURE = 22; // 0x16
+    field public static final int FUEL_SYSTEM_STATUS = 0; // 0x0
+    field public static final int FUEL_TYPE = 21; // 0x15
+    field public static final int IGNITION_MONITORS_SUPPORTED = 2; // 0x2
+    field public static final int IGNITION_SPECIFIC_MONITORS = 3; // 0x3
+    field public static final int INTAKE_AIR_TEMPERATURE = 4; // 0x4
+    field public static final int LAST_SYSTEM = 31; // 0x1f
+    field public static final int MALFUNCTION_INDICATOR_LIGHT_ON = 1; // 0x1
+    field public static final int MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20; // 0x14
+    field public static final int MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16; // 0x10
+    field public static final int MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19; // 0x13
+    field public static final int MAX_OXYGEN_SENSOR_CURRENT = 18; // 0x12
+    field public static final int MAX_OXYGEN_SENSOR_VOLTAGE = 17; // 0x11
+    field public static final int NUM_OXYGEN_SENSORS_PRESENT = 6; // 0x6
+    field public static final int RUNTIME_SINCE_ENGINE_START = 7; // 0x7
+    field public static final int TIME_SINCE_TROUBLE_CODES_CLEARED = 15; // 0xf
+    field public static final int TIME_WITH_MALFUNCTION_LIGHT_ON = 14; // 0xe
+    field public static final int VENDOR_START = 32; // 0x20
+    field public static final int WARMUPS_SINCE_CODES_CLEARED = 9; // 0x9
+  }
+
+}
+
 package android.car.hardware {
 
   public class CarPropertyConfig<T> implements android.os.Parcelable {
@@ -446,8 +699,14 @@
     field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
     field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
     field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+    field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1
+    field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2
+    field public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4; // 0x4
+    field public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3; // 0x3
+    field public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0; // 0x0
     field public final float[] floatValues;
     field public final int[] intValues;
+    field public final long[] longValues;
     field public int sensorType;
     field public long timestamp;
   }
@@ -470,6 +729,7 @@
     field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
     field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
     field public static final int SENSOR_RATE_UI = 2; // 0x2
+    field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
     field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
     field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
     field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
@@ -480,7 +740,9 @@
     field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
     field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
     field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+    field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25; // 0x19
     field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+    field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23; // 0x17
   }
 
   public static abstract interface CarSensorManager.OnSensorChangedListener {
@@ -598,6 +860,7 @@
     field public static final int ID_ZONED_FAN_POSITION_AVAILABLE = 16390; // 0x4006
     field public static final int ID_ZONED_FAN_SPEED_RPM = 16389; // 0x4005
     field public static final int ID_ZONED_FAN_SPEED_SETPOINT = 16388; // 0x4004
+    field public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 16399; // 0x400f
     field public static final int ID_ZONED_HVAC_POWER_ON = 16387; // 0x4003
     field public static final int ID_ZONED_MAX_AC_ON = 16396; // 0x400c
     field public static final int ID_ZONED_MAX_DEFROST_ON = 16398; // 0x400e
@@ -690,6 +953,7 @@
     field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
     field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
     field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+    field public static final int CAR_AUDIO_USAGE_RINGTONE = 10; // 0xa
     field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
     field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
     field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 6ed5d73..9a9e531 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -20,8 +20,9 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.car.annotation.FutureFeature;
+import android.car.cluster.CarInstrumentClusterManager;
 import android.car.content.pm.CarPackageManager;
-import android.car.hardware.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticManager;
 import android.car.hardware.CarSensorManager;
 import android.car.hardware.CarVendorExtensionManager;
 import android.car.hardware.cabin.CarCabinManager;
@@ -29,8 +30,8 @@
 import android.car.hardware.radio.CarRadioManager;
 import android.car.media.CarAudioManager;
 import android.car.navigation.CarNavigationStatusManager;
+import android.car.CarBluetoothManager;
 import android.car.test.CarTestManagerBinderWrapper;
-import android.car.vms.VmsSubscriberManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -44,7 +45,6 @@
 import android.util.Log;
 
 import com.android.car.internal.FeatureConfiguration;
-import com.android.car.internal.FeatureUtil;
 import com.android.internal.annotations.GuardedBy;
 
 import java.lang.annotation.Retention;
@@ -62,8 +62,9 @@
      * Represent the version of Car API. This is only updated when there is API change.
      * 1 : N
      * 2 : O
+     * 3 : O-MR1
      */
-    public static final int VERSION = 2;
+    public static final int VERSION = 3;
 
     /** Service name for {@link CarSensorManager}, to be used in {@link #getCarManager(String)}. */
     public static final String SENSOR_SERVICE = "sensor";
@@ -79,11 +80,17 @@
 
     /** Service name for {@link CarAudioManager} */
     public static final String AUDIO_SERVICE = "audio";
+
     /**
      * Service name for {@link CarNavigationStatusManager}
      * @hide
      */
     public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
+    /**
+     * Service name for {@link CarInstrumentClusterManager}
+     * @hide
+     */
+    public static final String CAR_INSTRUMENT_CLUSTER_SERVICE = "cluster_service";
 
     /**
      * @hide
@@ -94,6 +101,7 @@
     /**
      * @hide
      */
+    @SystemApi
     public static final String DIAGNOSTIC_SERVICE = "diagnostic";
 
     /**
@@ -121,10 +129,9 @@
     public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension";
 
     /**
-     * @FutureFeature Cannot drop due to usage in non-flag protected place.
      * @hide
      */
-    public static final String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service";
+    public static final String BLUETOOTH_SERVICE = "car_bluetooth";
 
     /**
      * Service for testing. This is system app only feature.
@@ -143,6 +150,10 @@
     /** Permission necessary to access car's speed. */
     public static final String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
 
+    /** Permission necessary to access car's dynamics state. */
+    public static final String PERMISSION_VEHICLE_DYNAMICS_STATE =
+        "android.car.permission.VEHICLE_DYNAMICS_STATE";
+
     /**
      * Permission necessary to change car audio volume through {@link CarAudioManager}.
      */
@@ -164,6 +175,24 @@
             "android.car.permission.CAR_NAVIGATION_MANAGER";
 
     /**
+     * Permission necessary to start activities in the instrument cluster through
+     * {@link CarInstrumentClusterManager}
+     *
+     * @hide
+     */
+    public static final String PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL =
+            "android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL";
+
+    /**
+     * Application must have this permission in order to be launched in the instrument cluster
+     * display.
+     *
+     * @hide
+     */
+    public static final String PERMISSION_CAR_DISPLAY_IN_CLUSTER =
+            "android.car.permission.CAR_DISPLAY_IN_CLUSTER";
+
+    /**
      * Permission necessary to access car specific communication channel.
      * @hide
      */
@@ -225,35 +254,20 @@
             "android.car.permission.CAR_TEST_SERVICE";
 
     /**
-     * Permissions necessary to access VMS publisher APIs.
+     * Permissions necessary to read diagnostic information, including vendor-specific bits.
      *
      * @hide
      */
-    @FutureFeature
-    public static final String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
-
-    /**
-     * Permissions necessary to access VMS subscriber APIs.
-     *
-     * @hide
-     */
-    @FutureFeature
-    public static final String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
-
-    /**
-     * Permissions necessary to read diagnostic information.
-     *
-     * @hide
-     */
-    @FutureFeature
-    public static final String PERMISSION_CAR_DIAGNOSTIC_READ = "android.car.permission.DIAGNOSTIC_READ";
+    @SystemApi
+    public static final String PERMISSION_CAR_DIAGNOSTIC_READ_ALL =
+        "android.car.permission.DIAGNOSTIC_READ_ALL";
 
     /**
      * Permissions necessary to clear diagnostic information.
      *
      * @hide
      */
-    @FutureFeature
+    @SystemApi
     public static final String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
 
     /** Type of car connection: platform runs directly in car. */
@@ -273,6 +287,22 @@
      */
     public static final String CAR_NOT_CONNECTED_EXCEPTION_MSG = "CarNotConnected";
 
+    /**
+     * Activity Action: Provide media playing through a media template app.
+     * <p>Input: String extra mapped by {@link android.app.SearchManager#QUERY} is the query
+     * used to start the media. String extra mapped by {@link #CAR_EXTRA_MEDIA_PACKAGE} is the
+     * package name of the media app which user wants to play media on.
+     * <p>Output: nothing.
+     */
+    public static final String CAR_INTENT_ACTION_MEDIA_TEMPLATE =
+            "android.car.intent.action.MEDIA_TEMPLATE";
+
+    /**
+     * Used as a string extra field with {@link #CAR_INTENT_ACTION_MEDIA_TEMPLATE} to specify the
+     * media app that user wants to start the media on. Note: this is not the templated media app.
+     */
+    public static final String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
+
     /** @hide */
     public static final String CAR_SERVICE_INTERFACE_NAME = "android.car.ICar";
 
@@ -593,16 +623,16 @@
             case VENDOR_EXTENSION_SERVICE:
                 manager = new CarVendorExtensionManager(binder, mEventHandler);
                 break;
+            case CAR_INSTRUMENT_CLUSTER_SERVICE:
+                manager = new CarInstrumentClusterManager(binder, mEventHandler);
+                break;
             case TEST_SERVICE:
                 /* CarTestManager exist in static library. So instead of constructing it here,
                  * only pass binder wrapper so that CarTestManager can be constructed outside. */
                 manager = new CarTestManagerBinderWrapper(binder);
                 break;
-            case VMS_SUBSCRIBER_SERVICE:
-                if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-                    manager = new VmsSubscriberManager(binder, mEventHandler);
-                }
-                break;
+            case BLUETOOTH_SERVICE:
+                manager = new CarBluetoothManager(binder, mContext);
         }
         return manager;
     }
diff --git a/car-lib/src/android/car/CarBluetoothManager.java b/car-lib/src/android/car/CarBluetoothManager.java
new file mode 100644
index 0000000..841a055
--- /dev/null
+++ b/car-lib/src/android/car/CarBluetoothManager.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.car.CarLibLog;
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.car.ICarBluetooth;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * APIs for setting Car specific Bluetooth Connection Management policy
+ *
+ * @hide
+ */
+public final class CarBluetoothManager implements CarManagerBase {
+    private static final String TAG = "CarBluetoothManager";
+    private final Context mContext;
+    private final ICarBluetooth mService;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0,
+            BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1})
+    public @interface PriorityType {
+    }
+
+    public static final int BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0 = 0;
+    public static final int BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1 = 1;
+    // Write an empty string to clear a Primary or Secondary device.
+    public static final String BLUETOOTH_NO_PRIORITY_DEVICE = "";
+
+    /**
+     * Set the Auto Connect priority for a paired Bluetooth Device.
+     * For example, if a device is tagged as a Primary device (Priority 0) for a supported
+     * Bluetooth profile, every new Auto Connect attempt would start with trying to connect to
+     * *that* device. This priority is set at a Bluetooth profile granularity.
+     *
+     * @param deviceToSet   - Device to set priority (Tag)
+     * @param profileToSet  - BluetoothProfile to set priority for
+     * @param priorityToSet - What priority level to set to
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet,
+            @PriorityType int priorityToSet) throws CarNotConnectedException {
+        try {
+            mService.setBluetoothDeviceConnectionPriority(deviceToSet, profileToSet, priorityToSet);
+        } catch (RemoteException e) {
+            Log.e(CarLibLog.TAG_CAR, "setBluetoothDeviceConnectionPriority failed", e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Unset the Auto Connect priority for the given profile
+     *
+     * @param profileToClear  - Profile to unset priority
+     * @param priorityToClear - Which priority to clear (Primary or Secondary)
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public void clearBluetoothDeviceConnectionPriority(int profileToClear,
+            @PriorityType int priorityToClear) throws CarNotConnectedException {
+        try {
+            mService.clearBluetoothDeviceConnectionPriority(profileToClear, priorityToClear);
+        } catch (RemoteException e) {
+            Log.e(CarLibLog.TAG_CAR, "clearBluetoothDeviceConnectionPriority failed", e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Returns if there is a device that has been tagged with the given priority for the given
+     * profile.
+     *
+     * @param profile         - BluetoothProfile
+     * @param priorityToCheck - Priority to check
+     * @return true if there is a device present with the given priority, false if not
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean isPriorityDevicePresent(int profile, @PriorityType int priorityToCheck)
+            throws CarNotConnectedException {
+        try {
+            return mService.isPriorityDevicePresent(profile, priorityToCheck);
+        } catch (RemoteException e) {
+            Log.e(CarLibLog.TAG_CAR, "isPrioritySet failed", e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Returns the Bluetooth device address as a String that has been tagged with the given priority
+     * for the given profile.
+     *
+     * @param profile         - BluetoothProfile
+     * @param priorityToCheck - Priority to check
+     * @return BluetoothDevice address if present, null if absent
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public String getDeviceNameWithPriority(int profile, @PriorityType int priorityToCheck)
+            throws CarNotConnectedException {
+        try {
+            return mService.getDeviceNameWithPriority(profile, priorityToCheck);
+        } catch (RemoteException e) {
+            Log.e(CarLibLog.TAG_CAR, "getDeviceNameWithPriority failed", e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /** @hide */
+    public CarBluetoothManager(IBinder service, Context context) {
+        mContext = context;
+        mService = ICarBluetooth.Stub.asInterface(service);
+    }
+
+    @Override
+    public void onCarDisconnected() {
+    }
+}
diff --git a/car-lib/src/android/car/CarProjectionManager.java b/car-lib/src/android/car/CarProjectionManager.java
index 1e40fcd..d1824f6 100644
--- a/car-lib/src/android/car/CarProjectionManager.java
+++ b/car-lib/src/android/car/CarProjectionManager.java
@@ -69,13 +69,23 @@
     }
 
     /**
+     * Compatibility with previous APIs due to typo
+     * @throws CarNotConnectedException if the connection to the car service has been lost.
+     * @hide
+     */
+    public void regsiterProjectionListener(CarProjectionListener listener, int voiceSearchFilter)
+            throws CarNotConnectedException {
+        registerProjectionListener(listener, voiceSearchFilter);
+    }
+
+    /**
      * Register listener to monitor projection. Only one listener can be registered and
      * registering multiple times will lead into only the last listener to be active.
      * @param listener
      * @param voiceSearchFilter Flags of voice search requests to get notification.
      * @throws CarNotConnectedException if the connection to the car service has been lost.
      */
-    public void regsiterProjectionListener(CarProjectionListener listener, int voiceSearchFilter)
+    public void registerProjectionListener(CarProjectionListener listener, int voiceSearchFilter)
             throws CarNotConnectedException {
         if (listener == null) {
             throw new IllegalArgumentException("null listener");
@@ -83,7 +93,7 @@
         synchronized (this) {
             if (mListener == null || mVoiceSearchFilter != voiceSearchFilter) {
                 try {
-                    mService.regsiterProjectionListener(mBinderListener, voiceSearchFilter);
+                    mService.registerProjectionListener(mBinderListener, voiceSearchFilter);
                 } catch (RemoteException e) {
                     throw new CarNotConnectedException(e);
                 }
@@ -94,13 +104,22 @@
     }
 
     /**
+     * Compatibility with previous APIs due to typo
+     * @throws CarNotConnectedException if the connection to the car service has been lost.
+     * @hide
+     */
+    public void unregsiterProjectionListener() {
+       unregisterProjectionListener();
+    }
+
+    /**
      * Unregister listener and stop listening projection events.
      * @throws CarNotConnectedException if the connection to the car service has been lost.
      */
-    public void unregsiterProjectionListener() {
+    public void unregisterProjectionListener() {
         synchronized (this) {
             try {
-                mService.unregsiterProjectionListener(mBinderListener);
+                mService.unregisterProjectionListener(mBinderListener);
             } catch (RemoteException e) {
                 //ignore
             }
diff --git a/car-lib/src/android/car/ICarBluetooth.aidl b/car-lib/src/android/car/ICarBluetooth.aidl
new file mode 100644
index 0000000..a5fbb73
--- /dev/null
+++ b/car-lib/src/android/car/ICarBluetooth.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car;
+import android.bluetooth.BluetoothDevice;
+
+/** @hide */
+interface ICarBluetooth {
+    void setBluetoothDeviceConnectionPriority(in BluetoothDevice deviceToSet, in int profileToSet,
+                in int priorityToSet);
+    void clearBluetoothDeviceConnectionPriority(in int profileToClear,in int priorityToClear);
+    boolean isPriorityDevicePresent(in int profile, in int priorityToCheck);
+    String getDeviceNameWithPriority(in int profile, in int priorityToCheck);
+}
diff --git a/car-lib/src/android/car/ICarProjection.aidl b/car-lib/src/android/car/ICarProjection.aidl
index 96eb2a9..15831cd 100644
--- a/car-lib/src/android/car/ICarProjection.aidl
+++ b/car-lib/src/android/car/ICarProjection.aidl
@@ -42,10 +42,10 @@
      * Registers projection callback.
      * Re-registering same callback with different filter will cause only filter to update.
      */
-    void regsiterProjectionListener(ICarProjectionCallback callback, int filter) = 2;
+    void registerProjectionListener(ICarProjectionCallback callback, int filter) = 2;
 
     /**
      * Unregisters projection callback.
      */
-    void unregsiterProjectionListener(ICarProjectionCallback callback) = 3;
+    void unregisterProjectionListener(ICarProjectionCallback callback) = 3;
 }
diff --git a/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
new file mode 100644
index 0000000..758ce9e
--- /dev/null
+++ b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cluster;
+
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * API to work with instrument cluster.
+ *
+ * @hide
+ */
+public class CarInstrumentClusterManager implements CarManagerBase {
+    private static final String TAG = CarInstrumentClusterManager.class.getSimpleName();
+
+    /** @hide */
+    public static final String CATEGORY_NAVIGATION = "android.car.cluster.NAVIGATION";
+
+    /**
+     * When activity in the cluster is launched it will receive {@link ClusterActivityState} in the
+     * intent's extra thus activity will know information about unobscured area, etc. upon activity
+     * creation.
+     *
+     * @hide
+     */
+    public static final String KEY_EXTRA_ACTIVITY_STATE =
+            "android.car.cluster.ClusterActivityState";
+
+    private final EventHandler mHandler;
+    private final Map<String, Set<Callback>> mCallbacksByCategory = new HashMap<>(0);
+    private final Object mLock = new Object();
+    private final Map<String, Bundle> mActivityStatesByCategory = new HashMap<>(0);
+
+    private final IInstrumentClusterManagerService mService;
+
+    private ClusterManagerCallback mServiceToManagerCallback;
+
+    /**
+     * Starts activity in the instrument cluster.
+     *
+     * @hide
+     */
+    public void startActivity(Intent intent) throws CarNotConnectedException {
+        try {
+            mService.startClusterActivity(intent);
+        } catch (RemoteException e) {
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Caller of this method will receive immediate callback with the most recent state if state
+     * exists for given category.
+     *
+     * @param category category of the activity in the cluster,
+     *                         see {@link #CATEGORY_NAVIGATION}
+     * @param callback instance of {@link Callback} class to receive events.
+     *
+     * @hide
+     */
+    public void registerCallback(String category, Callback callback)
+            throws CarNotConnectedException {
+        Log.i(TAG, "registerCallback, category: " + category + ", callback: " + callback);
+        ClusterManagerCallback callbackToCarService = null;
+        synchronized (mLock) {
+            Set<Callback> callbacks = mCallbacksByCategory.get(category);
+            if (callbacks == null) {
+                callbacks = new HashSet<>(1);
+                mCallbacksByCategory.put(category, callbacks);
+            }
+            if (!callbacks.add(callback)) {
+                Log.w(TAG, "registerCallback: already registered");
+                return;  // already registered
+            }
+
+            if (mActivityStatesByCategory.containsKey(category)) {
+                Log.i(TAG, "registerCallback: sending activity state...");
+                callback.onClusterActivityStateChanged(
+                        category, mActivityStatesByCategory.get(category));
+            }
+
+            if (mServiceToManagerCallback == null) {
+                Log.i(TAG, "registerCallback: registering callback with car service...");
+                mServiceToManagerCallback = new ClusterManagerCallback();
+                callbackToCarService = mServiceToManagerCallback;
+            }
+        }
+        try {
+            mService.registerCallback(callbackToCarService);
+            Log.i(TAG, "registerCallback: done");
+        } catch (RemoteException e) {
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Unregisters given callback for all activity categories.
+     *
+     * @param callback previously registered callback
+     *
+     * @hide
+     */
+    public void unregisterCallback(Callback callback) throws CarNotConnectedException {
+        List<String> keysToRemove = new ArrayList<>(1);
+        synchronized (mLock) {
+            for (Map.Entry<String, Set<Callback>> entry : mCallbacksByCategory.entrySet()) {
+                Set<Callback> callbacks = entry.getValue();
+                if (callbacks.remove(callback) && callbacks.isEmpty()) {
+                    keysToRemove.add(entry.getKey());
+                }
+
+            }
+
+            for (String key: keysToRemove) {
+                mCallbacksByCategory.remove(key);
+            }
+
+            if (mCallbacksByCategory.isEmpty()) {
+                try {
+                    mService.unregisterCallback(mServiceToManagerCallback);
+                } catch (RemoteException e) {
+                    throw new CarNotConnectedException(e);
+                }
+                mServiceToManagerCallback = null;
+            }
+        }
+    }
+
+    /** @hide */
+    public CarInstrumentClusterManager(IBinder service, Handler handler) {
+        mService = IInstrumentClusterManagerService.Stub.asInterface(service);
+
+        mHandler = new EventHandler(handler.getLooper());
+    }
+
+    /** @hide */
+    public interface Callback {
+
+        /**
+         * Notify client that activity state was changed.
+         *
+         * @param category cluster activity category, see {@link #CATEGORY_NAVIGATION}
+         * @param clusterActivityState see {@link ClusterActivityState} how to read this bundle.
+         */
+        void onClusterActivityStateChanged(String category, Bundle clusterActivityState);
+    }
+
+    /** @hide */
+    @Override
+    public void onCarDisconnected() {
+    }
+
+    private class EventHandler extends Handler {
+
+        final static int MSG_ACTIVITY_STATE = 1;
+
+        EventHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            Log.i(TAG, "handleMessage, message: " + msg);
+            switch (msg.what) {
+                case MSG_ACTIVITY_STATE:
+                    Pair<String, Bundle> info = (Pair<String, Bundle>) msg.obj;
+                    String category = info.first;
+                    Bundle state = info.second;
+                    List<CarInstrumentClusterManager.Callback> callbacks = null;
+                    synchronized (mLock) {
+                        if (mCallbacksByCategory.containsKey(category)) {
+                            callbacks = new ArrayList<>(mCallbacksByCategory.get(category));
+                        }
+                    }
+                    Log.i(TAG, "handleMessage, callbacks: " + callbacks);
+                    if (callbacks != null) {
+                        for (CarInstrumentClusterManager.Callback cb : callbacks) {
+                            cb.onClusterActivityStateChanged(category, state);
+                        }
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected message: " + msg.what);
+            }
+        }
+    }
+
+    private class ClusterManagerCallback extends IInstrumentClusterManagerCallback.Stub {
+
+        @Override
+        public void setClusterActivityState(String category, Bundle clusterActivityState)
+                throws RemoteException {
+            Log.i(TAG, "setClusterActivityState, category: " + category);
+            synchronized (mLock) {
+                mActivityStatesByCategory.put(category, clusterActivityState);
+            }
+
+            mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_ACTIVITY_STATE,
+                    new Pair<>(category, clusterActivityState)));
+        }
+    }
+}
\ No newline at end of file
diff --git a/car-lib/src/android/car/cluster/ClusterActivityState.java b/car-lib/src/android/car/cluster/ClusterActivityState.java
new file mode 100644
index 0000000..9a6223c
--- /dev/null
+++ b/car-lib/src/android/car/cluster/ClusterActivityState.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cluster;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Bundle;
+
+/**
+ * Helper class that represents activity state in the cluster and can be serialized / deserialized
+ * to/from bundle.
+ * @hide
+ */
+public class ClusterActivityState {
+    private static final String KEY_VISIBLE = "android.car:activityState.visible";
+    private static final String KEY_UNOBSCURED_BOUNDS = "android.car:activityState.unobscured";
+    private static final String KEY_EXTRAS = "android.car:activityState.extras";
+
+    private boolean mVisible = true;
+    private Rect mUnobscuredBounds;
+    private Bundle mExtras;
+
+    public boolean isVisible() {
+        return mVisible;
+    }
+
+    @Nullable public Rect getUnobscuredBounds() {
+        return mUnobscuredBounds;
+    }
+
+    public ClusterActivityState setVisible(boolean visible) {
+        mVisible = visible;
+        return this;
+    }
+
+    public ClusterActivityState setUnobscuredBounds(Rect unobscuredBounds) {
+        mUnobscuredBounds = unobscuredBounds;
+        return this;
+    }
+
+    public ClusterActivityState setExtras(Bundle bundle) {
+        mExtras = bundle;
+        return this;
+    }
+
+    /** Use factory methods instead. */
+    private ClusterActivityState() {}
+
+    public static ClusterActivityState create(boolean visible, Rect unobscuredBounds) {
+        return new ClusterActivityState()
+                .setVisible(visible)
+                .setUnobscuredBounds(unobscuredBounds);
+    }
+
+    public static ClusterActivityState fromBundle(Bundle bundle) {
+        return new ClusterActivityState()
+                .setVisible(bundle.getBoolean(KEY_VISIBLE, true))
+                .setUnobscuredBounds((Rect) bundle.getParcelable(KEY_UNOBSCURED_BOUNDS))
+                .setExtras(bundle.getBundle(KEY_EXTRAS));
+    }
+
+    public Bundle toBundle() {
+        Bundle b = new Bundle();
+        b.putBoolean(KEY_VISIBLE, mVisible);
+        b.putParcelable(KEY_UNOBSCURED_BOUNDS, mUnobscuredBounds);
+        b.putBundle(KEY_EXTRAS, mExtras);
+        return b;
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getSimpleName() + " {"
+                + "visible: " + mVisible + ", "
+                + "unobscuredBounds: " + mUnobscuredBounds
+                + " }";
+    }
+}
diff --git a/car-lib/src/android/car/cluster/IInstrumentClusterManagerCallback.aidl b/car-lib/src/android/car/cluster/IInstrumentClusterManagerCallback.aidl
new file mode 100644
index 0000000..91a497d
--- /dev/null
+++ b/car-lib/src/android/car/cluster/IInstrumentClusterManagerCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster;
+
+import android.os.Bundle;
+
+/**
+ * Interface from Car Service to {@link android.car.cluster.CarInstrumentClusterManager}
+ * @hide
+ */
+interface IInstrumentClusterManagerCallback {
+    /**
+     * Notifies manager about changes in the cluster activity state.
+     *
+     * @param category cluster activity category to which this state applies,
+     *        see {@link android.car.cluster.CarInstrumentClusterManager} for details.
+     * @param clusterActivityState is a {@link Bundle} object,
+     *        see {@link android.car.cluster.ClusterActivityState} for how to construct the bundle.
+     * @hide
+     */
+    oneway void setClusterActivityState(String category, in Bundle clusterActivityState);
+}
diff --git a/car-lib/src/android/car/cluster/IInstrumentClusterManagerService.aidl b/car-lib/src/android/car/cluster/IInstrumentClusterManagerService.aidl
new file mode 100644
index 0000000..aaaeaee
--- /dev/null
+++ b/car-lib/src/android/car/cluster/IInstrumentClusterManagerService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster;
+
+import android.content.Intent;
+
+import android.car.cluster.IInstrumentClusterManagerCallback;
+
+/**
+ * API to communicate between {@link CarInstrumentClusterManager} and Car Service.
+ *
+ * @hide
+ */
+interface IInstrumentClusterManagerService {
+    oneway void startClusterActivity(in Intent intent);
+
+    oneway void registerCallback(in IInstrumentClusterManagerCallback callback);
+    oneway void unregisterCallback(in IInstrumentClusterManagerCallback callback);
+}
diff --git a/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java b/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
deleted file mode 100644
index 409c817..0000000
--- a/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.car.cluster.renderer;
-
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.graphics.Rect;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * TODO: need to properly define this class and make it immutable. bug: 32060601
- *
- * @hide
- */
-@SystemApi
-public class DisplayConfiguration implements Parcelable {
-    private final Rect mPrimaryRegion;
-
-    @Nullable
-    private final Rect mSecondaryRegion;
-
-    public static final Parcelable.Creator<DisplayConfiguration> CREATOR =
-            new Parcelable.Creator<DisplayConfiguration>() {
-
-                public DisplayConfiguration createFromParcel(Parcel in) {
-                    return new DisplayConfiguration(in);
-                }
-
-                public DisplayConfiguration[] newArray(int size) {
-                    return new DisplayConfiguration[size];
-                }
-            };
-
-
-    public DisplayConfiguration(Parcel in) {
-        mPrimaryRegion = in.readTypedObject(Rect.CREATOR);
-        mSecondaryRegion = in.readTypedObject(Rect.CREATOR);
-    }
-
-    public DisplayConfiguration(Rect primaryRegion) {
-        this(primaryRegion, null);
-    }
-
-    public DisplayConfiguration(Rect primaryRegion, @Nullable Rect secondaryRegion) {
-        mPrimaryRegion = primaryRegion;
-        mSecondaryRegion = secondaryRegion;
-    }
-
-
-    /** Region that will be fully visible in instrument cluster */
-    public Rect getPrimaryRegion() {
-        return mPrimaryRegion;
-    }
-
-    /**
-     * The region that includes primary region + may include some additional space that might
-     * be partially visible in the instrument cluster. It is useful to fade-out primary
-     * rendering for example or adding background image.
-     */
-    @Nullable
-    public Rect getSecondaryRegion() {
-        return mSecondaryRegion;
-    }
-
-    /** Returns true if secondary region is strongly greater then primary region */
-    public boolean hasSecondaryRegion() {
-        return mSecondaryRegion != null
-                && mSecondaryRegion.width() > mPrimaryRegion.width()
-                && mSecondaryRegion.height() > mPrimaryRegion.height();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeTypedObject(mPrimaryRegion, 0);
-        dest.writeTypedObject(mSecondaryRegion, 0);
-    }
-}
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
index 9fb4b56..3458975 100644
--- a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
@@ -15,6 +15,7 @@
  */
 package android.car.cluster.renderer;
 
+import android.car.cluster.renderer.IInstrumentClusterCallback;
 import android.car.cluster.renderer.IInstrumentClusterNavigation;
 import android.view.KeyEvent;
 
@@ -28,8 +29,8 @@
     IInstrumentClusterNavigation getNavigationService();
 
     /** Supplies Instrument Cluster Renderer with current owner of Navigation app context */
-    void setNavigationContextOwner(int uid, int pid);
+    oneway void setNavigationContextOwner(int uid, int pid);
 
     /** Called when key event that was addressed to instrument cluster display has been received. */
-    void onKeyEvent(in KeyEvent keyEvent);
+    oneway void onKeyEvent(in KeyEvent keyEvent);
 }
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterCallback.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterCallback.aidl
new file mode 100644
index 0000000..996dc9e
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterCallback.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.renderer;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+
+/**
+ * This interface defines the communication channel between the cluster vendor implementation and
+ * Car Service.
+ *
+ * @hide
+ */
+interface IInstrumentClusterCallback {
+    /**
+     * Notify Car Service how to launch an activity for particular category.
+     *
+     * @param category cluster activity category,
+     *        see {@link android.car.cluster.CarInstrumentClusterManager} for details.
+     * @param activityOptions this bundle will be converted to {@link android.app.ActivityOptions}
+     *        and used when starting an activity. It may contain information such as virtual display
+     *        id or activity stack id where to start cluster activity.
+     *
+     * @hide
+     */
+    void setClusterActivityLaunchOptions(String category, in Bundle activityOptions);
+
+    /**
+     * Activities launched on virtual display will be in onPause state most of the time, so they
+     * can't really know whether they visible on the screen or not. We need to propagate this
+     * information along with unobscured bounds (and possible other info) from instrument cluster
+     * vendor implementation to activity.
+     *
+     * @param category cluster activity category to which this state applies,
+     *        see {@link android.car.cluster.CarInstrumentClusterManager} for details.
+     * @param clusterActivityState is a {@link Bundle} object,
+     *        see {@link android.car.cluster.ClusterActivityState} for how to construct the bundle.
+     * @hide
+     */
+    void setClusterActivityState(String category, in Bundle clusterActivityState);
+}
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
index 00cc135..b1fb7b1 100644
--- a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
@@ -15,8 +15,9 @@
  */
 package android.car.cluster.renderer;
 
-import android.graphics.Bitmap;
 import android.car.navigation.CarNavigationInstrumentCluster;
+import android.graphics.Bitmap;
+import android.os.Bundle;
 
 /**
  * Binder API for Instrument Cluster Navigation.
@@ -31,5 +32,6 @@
         int turnSide);
     void onNextManeuverDistanceChanged(int distanceMeters, int timeSeconds,
         int displayDistanceMillis, int displayDistanceUnit);
+    void onEvent(int eventType, in Bundle bundle);
     CarNavigationInstrumentCluster getInstrumentClusterInfo();
 }
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index d110158..e34f2fa 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -18,11 +18,14 @@
 import android.annotation.CallSuper;
 import android.annotation.MainThread;
 import android.annotation.SystemApi;
+import android.app.ActivityOptions;
 import android.app.Service;
 import android.car.CarLibLog;
+import android.car.CarNotConnectedException;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -32,6 +35,8 @@
 import android.util.Pair;
 import android.view.KeyEvent;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -59,6 +64,14 @@
 
     private RendererBinder mRendererBinder;
 
+    /** @hide */
+    public static final String EXTRA_KEY_CALLBACK_SERVICE =
+            "android.car.cluster.IInstrumentClusterCallback";
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private IInstrumentClusterCallback mCallback;
+
     @Override
     @CallSuper
     public IBinder onBind(Intent intent) {
@@ -66,6 +79,15 @@
             Log.d(TAG, "onBind, intent: " + intent);
         }
 
+        if (intent.getExtras().containsKey(EXTRA_KEY_CALLBACK_SERVICE)) {
+            IBinder callbackBinder = intent.getExtras().getBinder(EXTRA_KEY_CALLBACK_SERVICE);
+            synchronized (mLock) {
+                mCallback = IInstrumentClusterCallback.Stub.asInterface(callbackBinder);
+            }
+        } else {
+            Log.w(TAG, "onBind, no callback in extra!");
+        }
+
         if (mRendererBinder == null) {
             mRendererBinder = new RendererBinder(getNavigationRenderer());
         }
@@ -82,6 +104,57 @@
     protected void onKeyEvent(KeyEvent keyEvent) {
     }
 
+    /**
+     *
+     * Sets configuration for activities that should be launched directly in the instrument
+     * cluster.
+     *
+     * @param category category of cluster activity
+     * @param activityOptions contains information of how to start cluster activity (on what display
+     *                        or activity stack.
+     *
+     * @hide
+     */
+    public void setClusterActivityLaunchOptions(String category,
+            ActivityOptions activityOptions) throws CarNotConnectedException {
+        IInstrumentClusterCallback cb;
+        synchronized (mLock) {
+            cb = mCallback;
+        }
+        if (cb == null) throw new CarNotConnectedException();
+        try {
+            cb.setClusterActivityLaunchOptions(category, activityOptions.toBundle());
+        } catch (RemoteException e) {
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     *
+     * @param category cluster activity category,
+     *        see {@link android.car.cluster.CarInstrumentClusterManager}
+     * @param state pass information about activity state,
+     *        see {@link android.car.cluster.ClusterActivityState}
+     * @return true if information was sent to Car Service
+     * @throws CarNotConnectedException
+     *
+     * @hide
+     */
+    public void setClusterActivityState(String category, Bundle state)
+            throws CarNotConnectedException {
+        IInstrumentClusterCallback cb;
+        synchronized (mLock) {
+            cb = mCallback;
+        }
+        if (cb == null) throw new CarNotConnectedException();
+        try {
+            cb.setClusterActivityState(category, state);
+        } catch (RemoteException e) {
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         writer.println("**" + getClass().getSimpleName() + "**");
@@ -89,12 +162,19 @@
         if (mRendererBinder != null) {
             writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
             String owner = "none";
-            if (mRendererBinder.mNavContextOwner != null) {
-                owner = "[uid: " + mRendererBinder.mNavContextOwner.first
-                        + ", pid: " + mRendererBinder.mNavContextOwner.second + "]";
+            synchronized (mLock) {
+                if (mRendererBinder.mNavContextOwner != null) {
+                    owner = "[uid: " + mRendererBinder.mNavContextOwner.first
+                            + ", pid: " + mRendererBinder.mNavContextOwner.second + "]";
+                }
             }
             writer.println("navigation focus owner: " + owner);
         }
+        IInstrumentClusterCallback cb;
+        synchronized (mLock) {
+            cb = mCallback;
+        }
+        writer.println("callback: " + cb);
     }
 
     private class RendererBinder extends IInstrumentCluster.Stub {
@@ -102,8 +182,10 @@
         private final NavigationRenderer mNavigationRenderer;
         private final UiHandler mUiHandler;
 
-        private volatile NavigationBinder mNavigationBinder;
-        private volatile Pair<Integer, Integer> mNavContextOwner;
+        @GuardedBy("mLock")
+        private NavigationBinder mNavigationBinder;
+        @GuardedBy("mLock")
+        private Pair<Integer, Integer> mNavContextOwner;
 
         RendererBinder(NavigationRenderer navigationRenderer) {
             mNavigationRenderer = navigationRenderer;
@@ -112,21 +194,25 @@
 
         @Override
         public IInstrumentClusterNavigation getNavigationService() throws RemoteException {
-            if (mNavigationBinder == null) {
-                mNavigationBinder = new NavigationBinder(mNavigationRenderer);
-                if (mNavContextOwner != null) {
-                    mNavigationBinder.setNavigationContextOwner(
-                            mNavContextOwner.first, mNavContextOwner.second);
+            synchronized (mLock) {
+                if (mNavigationBinder == null) {
+                    mNavigationBinder = new NavigationBinder(mNavigationRenderer);
+                    if (mNavContextOwner != null) {
+                        mNavigationBinder.setNavigationContextOwner(
+                                mNavContextOwner.first, mNavContextOwner.second);
+                    }
                 }
+                return mNavigationBinder;
             }
-            return mNavigationBinder;
         }
 
         @Override
         public void setNavigationContextOwner(int uid, int pid) throws RemoteException {
-            mNavContextOwner = new Pair<>(uid, pid);
-            if (mNavigationBinder != null) {
-                mNavigationBinder.setNavigationContextOwner(uid, pid);
+            synchronized (mLock) {
+                mNavContextOwner = new Pair<>(uid, pid);
+                if (mNavigationBinder != null) {
+                    mNavigationBinder.setNavigationContextOwner(uid, pid);
+                }
             }
         }
 
@@ -181,6 +267,12 @@
         }
 
         @Override
+        public void onEvent(int eventType, Bundle bundle) throws RemoteException {
+            assertContextOwnership();
+            mNavigationRenderer.onEvent(eventType, bundle);
+        }
+
+        @Override
         public CarNavigationInstrumentCluster getInstrumentClusterInfo() throws RemoteException {
             return mNavigationRenderer.getNavigationProperties();
         }
diff --git a/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
index 1719190..0958548 100644
--- a/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
+++ b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
@@ -19,6 +19,7 @@
 import android.annotation.UiThread;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 
 /**
  * Contains methods specified for Navigation App renderer in instrument cluster.
@@ -40,4 +41,7 @@
             int turnNumber, Bitmap image, int turnSide);
     abstract public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds,
             int displayDistanceMillis, int displayDistanceUnit);
+
+    /** @hide */
+    public void onEvent(int eventType, Bundle bundle) {}
 }
diff --git a/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java b/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
index 37e5d36..047ed90 100644
--- a/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
+++ b/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
@@ -18,6 +18,7 @@
 import android.annotation.Nullable;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -38,6 +39,7 @@
     private final static int MSG_NAV_STOP = 2;
     private final static int MSG_NAV_NEXT_TURN = 3;
     private final static int MSG_NAV_NEXT_TURN_DISTANCE = 4;
+    private final static int MSG_EVENT = 5;
 
     /** Creates thread-safe {@link NavigationRenderer}. Returns null if renderer == null */
     @Nullable
@@ -90,6 +92,11 @@
         mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_NEXT_TURN_DISTANCE, distance));
     }
 
+    @Override
+    public void onEvent(int eventType, Bundle bundle) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_EVENT, eventType, 0, bundle));
+    }
+
     private static class NavigationRendererHandler extends RendererHandler<NavigationRenderer> {
 
         NavigationRendererHandler(Looper looper, NavigationRenderer renderer) {
@@ -115,6 +122,10 @@
                     renderer.onNextTurnDistanceChanged(
                             d.meters, d.seconds, d.displayDistanceMillis, d.displayDistanceUnit);
                     break;
+                case MSG_EVENT:
+                    Bundle bundle = (Bundle) msg.obj;
+                    renderer.onEvent(msg.arg1, bundle);
+                    break;
                 default:
                     throw new IllegalArgumentException("Msg: " + msg.what);
             }
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.aidl
similarity index 95%
copy from car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
copy to car-lib/src/android/car/diagnostic/CarDiagnosticEvent.aidl
index 73b184e..cd9b9a7 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.car.hardware;
+package android.car.diagnostic;
 
 parcelable CarDiagnosticEvent;
diff --git a/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.java b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.java
new file mode 100644
index 0000000..e8ec2b5
--- /dev/null
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.java
@@ -0,0 +1,885 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.diagnostic;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.JsonWriter;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A CarDiagnosticEvent object corresponds to a single diagnostic event frame coming from the car.
+ *
+ * @hide
+ */
+@SystemApi
+public class CarDiagnosticEvent implements Parcelable {
+    /** Whether this frame represents a live or a freeze frame */
+    public final int frameType;
+
+    /**
+     * When this data was acquired in car or received from car. It is elapsed real-time of data
+     * reception from car in nanoseconds since system boot.
+     */
+    public final long timestamp;
+
+    /**
+     * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
+     * integer valued properties
+     */
+    private final SparseIntArray intValues;
+
+    /**
+     * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
+     * float valued properties
+     */
+    private final SparseArray<Float> floatValues;
+
+    /**
+     * Diagnostic Troubleshooting Code (DTC) that was detected and caused this frame to be stored
+     * (if a freeze frame). Always null for a live frame.
+     */
+    public final String dtc;
+
+    public CarDiagnosticEvent(Parcel in) {
+        frameType = in.readInt();
+        timestamp = in.readLong();
+        int len = in.readInt();
+        floatValues = new SparseArray<>(len);
+        for (int i = 0; i < len; ++i) {
+            int key = in.readInt();
+            float value = in.readFloat();
+            floatValues.put(key, value);
+        }
+        len = in.readInt();
+        intValues = new SparseIntArray(len);
+        for (int i = 0; i < len; ++i) {
+            int key = in.readInt();
+            int value = in.readInt();
+            intValues.put(key, value);
+        }
+        dtc = (String) in.readValue(String.class.getClassLoader());
+        // version 1 up to here
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(frameType);
+        dest.writeLong(timestamp);
+        dest.writeInt(floatValues.size());
+        for (int i = 0; i < floatValues.size(); ++i) {
+            int key = floatValues.keyAt(i);
+            dest.writeInt(key);
+            dest.writeFloat(floatValues.get(key));
+        }
+        dest.writeInt(intValues.size());
+        for (int i = 0; i < intValues.size(); ++i) {
+            int key = intValues.keyAt(i);
+            dest.writeInt(key);
+            dest.writeInt(intValues.get(key));
+        }
+        dest.writeValue(dtc);
+    }
+
+    /**
+     * Store the contents of this diagnostic event in a JsonWriter.
+     *
+     * The data is stored as a JSON object, with these fields:
+     *  type: either "live" or "freeze" depending on the type of frame;
+     *  timestamp: the timestamp at which this frame was generated;
+     *  intValues: an array of objects each of which has two elements:
+     *    id: the integer identifier of the sensor;
+     *    value: the integer value of the sensor;
+     *  floatValues: an array of objects each of which has two elements:
+     *    id: the integer identifier of the sensor;
+     *    value: the floating-point value of the sensor;
+     *  stringValue: the DTC for a freeze frame, omitted for a live frame
+     */
+    public void writeToJson(JsonWriter jsonWriter) throws IOException {
+        jsonWriter.beginObject();
+
+        jsonWriter.name("type");
+        switch (frameType) {
+            case CarDiagnosticManager.FRAME_TYPE_LIVE:
+                jsonWriter.value("live");
+                break;
+            case CarDiagnosticManager.FRAME_TYPE_FREEZE:
+                jsonWriter.value("freeze");
+                break;
+            default:
+                throw new IllegalStateException("unknown frameType " + frameType);
+        }
+
+        jsonWriter.name("timestamp").value(timestamp);
+
+        jsonWriter.name("intValues").beginArray();
+        for (int i = 0; i < intValues.size(); ++i) {
+            jsonWriter.beginObject();
+            jsonWriter.name("id").value(intValues.keyAt(i));
+            jsonWriter.name("value").value(intValues.valueAt(i));
+            jsonWriter.endObject();
+        }
+        jsonWriter.endArray();
+
+        jsonWriter.name("floatValues").beginArray();
+        for (int i = 0; i < floatValues.size(); ++i) {
+            jsonWriter.beginObject();
+            jsonWriter.name("id").value(floatValues.keyAt(i));
+            jsonWriter.name("value").value(floatValues.valueAt(i));
+            jsonWriter.endObject();
+        }
+        jsonWriter.endArray();
+
+        if (dtc != null) {
+            jsonWriter.name("stringValue").value(dtc);
+        }
+
+        jsonWriter.endObject();
+    }
+
+    public static final Parcelable.Creator<CarDiagnosticEvent> CREATOR =
+            new Parcelable.Creator<CarDiagnosticEvent>() {
+                public CarDiagnosticEvent createFromParcel(Parcel in) {
+                    return new CarDiagnosticEvent(in);
+                }
+
+                public CarDiagnosticEvent[] newArray(int size) {
+                    return new CarDiagnosticEvent[size];
+                }
+            };
+
+    private CarDiagnosticEvent(
+            int frameType,
+            long timestamp,
+            SparseArray<Float> floatValues,
+            SparseIntArray intValues,
+            String dtc) {
+        this.frameType = frameType;
+        this.timestamp = timestamp;
+        this.floatValues = floatValues;
+        this.intValues = intValues;
+        this.dtc = dtc;
+    }
+
+    /**
+     * This class can be used to incrementally construct a CarDiagnosticEvent.
+     * CarDiagnosticEvent instances are immutable once built.
+     */
+    public static class Builder {
+        private int mType = CarDiagnosticManager.FRAME_TYPE_LIVE;
+        private long mTimestamp = 0;
+        private SparseArray<Float> mFloatValues = new SparseArray<>();
+        private SparseIntArray mIntValues = new SparseIntArray();
+        private String mDtc = null;
+
+        private Builder(int type) {
+            mType = type;
+        }
+
+        /** Returns a new Builder for a live frame */
+        public static Builder newLiveFrameBuilder() {
+            return new Builder(CarDiagnosticManager.FRAME_TYPE_LIVE);
+        }
+
+        /** Returns a new Builder for a freeze frame */
+        public static Builder newFreezeFrameBuilder() {
+            return new Builder(CarDiagnosticManager.FRAME_TYPE_FREEZE);
+        }
+
+        /** Sets the timestamp for the frame being built */
+        public Builder atTimestamp(long timestamp) {
+            mTimestamp = timestamp;
+            return this;
+        }
+
+        /** Adds an integer-valued sensor to the frame being built */
+        public Builder withIntValue(int key, int value) {
+            mIntValues.put(key, value);
+            return this;
+        }
+
+        /** Adds a float-valued sensor to the frame being built */
+        public Builder withFloatValue(int key, float value) {
+            mFloatValues.put(key, value);
+            return this;
+        }
+
+        /** Sets the DTC for the frame being built */
+        public Builder withDtc(String dtc) {
+            mDtc = dtc;
+            return this;
+        }
+
+        /** Builds and returns the CarDiagnosticEvent */
+        public CarDiagnosticEvent build() {
+            return new CarDiagnosticEvent(mType, mTimestamp, mFloatValues, mIntValues, mDtc);
+        }
+    }
+
+    /**
+     * Returns a copy of this CarDiagnosticEvent with all vendor-specific sensors removed.
+     *
+     * @hide
+     */
+    public CarDiagnosticEvent withVendorSensorsRemoved() {
+        SparseIntArray newIntValues = intValues.clone();
+        SparseArray<Float> newFloatValues = floatValues.clone();
+        for (int i = 0; i < intValues.size(); ++i) {
+            int key = intValues.keyAt(i);
+            if (key >= android.car.diagnostic.IntegerSensorIndex.LAST_SYSTEM) {
+                newIntValues.delete(key);
+            }
+        }
+        for (int i = 0; i < floatValues.size(); ++i) {
+            int key = floatValues.keyAt(i);
+            if (key >= android.car.diagnostic.FloatSensorIndex.LAST_SYSTEM) {
+                newFloatValues.delete(key);
+            }
+        }
+        return new CarDiagnosticEvent(frameType, timestamp, newFloatValues, newIntValues, dtc);
+    }
+
+    /** Returns true if this object is a live frame, false otherwise */
+    public boolean isLiveFrame() {
+        return CarDiagnosticManager.FRAME_TYPE_LIVE == frameType;
+    }
+
+    /** Returns true if this object is a freeze frame, false otherwise */
+    public boolean isFreezeFrame() {
+        return CarDiagnosticManager.FRAME_TYPE_FREEZE == frameType;
+    }
+
+    /** @hide */
+    public boolean isEmptyFrame() {
+        boolean empty = (0 == intValues.size());
+        empty &= (0 == floatValues.size());
+        if (isFreezeFrame()) empty &= dtc.isEmpty();
+        return empty;
+    }
+
+    /** @hide */
+    public CarDiagnosticEvent checkLiveFrame() {
+        if (!isLiveFrame()) throw new IllegalStateException("frame is not a live frame");
+        return this;
+    }
+
+    /** @hide */
+    public CarDiagnosticEvent checkFreezeFrame() {
+        if (!isFreezeFrame()) throw new IllegalStateException("frame is not a freeze frame");
+        return this;
+    }
+
+    /** @hide */
+    public boolean isEarlierThan(CarDiagnosticEvent otherEvent) {
+        otherEvent = Objects.requireNonNull(otherEvent);
+        return (timestamp < otherEvent.timestamp);
+    }
+
+    @Override
+    public boolean equals(Object otherObject) {
+        if (this == otherObject) {
+            return true;
+        }
+        if (null == otherObject) {
+            return false;
+        }
+        if (!(otherObject instanceof CarDiagnosticEvent)) {
+            return false;
+        }
+        CarDiagnosticEvent otherEvent = (CarDiagnosticEvent)otherObject;
+        if (otherEvent.frameType != frameType)
+            return false;
+        if (otherEvent.timestamp != timestamp)
+            return false;
+        if (otherEvent.intValues.size() != intValues.size())
+            return false;
+        if (otherEvent.floatValues.size() != floatValues.size())
+            return false;
+        if (!Objects.equals(dtc, otherEvent.dtc))
+            return false;
+        for (int i = 0; i < intValues.size(); ++i) {
+            int key = intValues.keyAt(i);
+            int otherKey = otherEvent.intValues.keyAt(i);
+            if (key != otherKey) {
+                return false;
+            }
+            int value = intValues.valueAt(i);
+            int otherValue = otherEvent.intValues.valueAt(i);
+            if (value != otherValue) {
+                return false;
+            }
+        }
+        for (int i = 0; i < floatValues.size(); ++i) {
+            int key = floatValues.keyAt(i);
+            int otherKey = otherEvent.floatValues.keyAt(i);
+            if (key != otherKey) {
+                return false;
+            }
+            float value = floatValues.valueAt(i);
+            float otherValue = otherEvent.floatValues.valueAt(i);
+            if (value != otherValue) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        Integer[] intKeys = new Integer[intValues.size()];
+        Integer[] floatKeys = new Integer[floatValues.size()];
+        Integer[] intValues = new Integer[intKeys.length];
+        Float[] floatValues = new Float[floatKeys.length];
+        for (int i = 0; i < intKeys.length; ++i) {
+            intKeys[i] = this.intValues.keyAt(i);
+            intValues[i] = this.intValues.valueAt(i);
+        }
+        for (int i = 0; i < floatKeys.length; ++i) {
+            floatKeys[i] = this.floatValues.keyAt(i);
+            floatValues[i] = this.floatValues.valueAt(i);
+        }
+        int intKeysHash = Objects.hash((Object[])intKeys);
+        int intValuesHash = Objects.hash((Object[])intValues);
+        int floatKeysHash = Objects.hash((Object[])floatKeys);
+        int floatValuesHash = Objects.hash((Object[])floatValues);
+        return Objects.hash(frameType,
+                timestamp,
+                dtc,
+                intKeysHash,
+                intValuesHash,
+                floatKeysHash,
+                floatValuesHash);
+    }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "%s diagnostic frame {\n"
+                        + "\ttimestamp: %d, "
+                        + "DTC: %s\n"
+                        + "\tintValues: %s\n"
+                        + "\tfloatValues: %s\n}",
+                isLiveFrame() ? "live" : "freeze",
+                timestamp,
+                dtc,
+                intValues.toString(),
+                floatValues.toString());
+    }
+
+    /**
+     * Returns the value of the given integer sensor, if present in this frame.
+     * Returns defaultValue otherwise.
+     */
+    public int getSystemIntegerSensor(
+            @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue) {
+        return intValues.get(sensor, defaultValue);
+    }
+
+    /**
+     * Returns the value of the given float sensor, if present in this frame.
+     * Returns defaultValue otherwise.
+     */
+    public float getSystemFloatSensor(
+            @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue) {
+        return floatValues.get(sensor, defaultValue);
+    }
+
+    /**
+     * Returns the value of the given integer sensor, if present in this frame.
+     * Returns defaultValue otherwise.
+     */
+    public int getVendorIntegerSensor(int sensor, int defaultValue) {
+        return intValues.get(sensor, defaultValue);
+    }
+
+    /**
+     * Returns the value of the given float sensor, if present in this frame.
+     * Returns defaultValue otherwise.
+     */
+    public float getVendorFloatSensor(int sensor, float defaultValue) {
+        return floatValues.get(sensor, defaultValue);
+    }
+
+    /**
+     * Returns the value of the given integer sensor, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable Integer getSystemIntegerSensor(
+            @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor) {
+        int index = intValues.indexOfKey(sensor);
+        if (index < 0) return null;
+        return intValues.valueAt(index);
+    }
+
+    /**
+     * Returns the value of the given float sensor, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable Float getSystemFloatSensor(
+            @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor) {
+        int index = floatValues.indexOfKey(sensor);
+        if (index < 0) return null;
+        return floatValues.valueAt(index);
+    }
+
+    /**
+     * Returns the value of the given integer sensor, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable Integer getVendorIntegerSensor(int sensor) {
+        int index = intValues.indexOfKey(sensor);
+        if (index < 0) return null;
+        return intValues.valueAt(index);
+    }
+
+    /**
+     * Returns the value of the given float sensor, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable Float getVendorFloatSensor(int sensor) {
+        int index = floatValues.indexOfKey(sensor);
+        if (index < 0) return null;
+        return floatValues.valueAt(index);
+    }
+
+    /**
+     * Represents possible states of the fuel system; see {@link
+     * android.car.diagnostic.IntegerSensorIndex#FUEL_SYSTEM_STATUS}
+     */
+    public static final class FuelSystemStatus {
+        private FuelSystemStatus() {}
+
+        public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1;
+        public static final int CLOSED_LOOP = 2;
+        public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4;
+        public static final int OPEN_SYSTEM_FAILURE = 8;
+        public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16;
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({
+            OPEN_INSUFFICIENT_ENGINE_TEMPERATURE,
+            CLOSED_LOOP,
+            OPEN_ENGINE_LOAD_OR_DECELERATION,
+            OPEN_SYSTEM_FAILURE,
+            CLOSED_LOOP_BUT_FEEDBACK_FAULT
+        })
+        /** @hide */
+        public @interface Status {}
+    }
+
+    /**
+     * Represents possible states of the secondary air system; see {@link
+     * android.car.diagnostic.IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
+     */
+    public static final class SecondaryAirStatus {
+        private SecondaryAirStatus() {}
+
+        public static final int UPSTREAM = 1;
+        public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2;
+        public static final int FROM_OUTSIDE_OR_OFF = 4;
+        public static final int PUMP_ON_FOR_DIAGNOSTICS = 8;
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({
+            UPSTREAM,
+            DOWNSTREAM_OF_CATALYCIC_CONVERTER,
+            FROM_OUTSIDE_OR_OFF,
+            PUMP_ON_FOR_DIAGNOSTICS
+        })
+        /** @hide */
+        public @interface Status {}
+    }
+
+    /**
+     * Represents possible types of fuel; see {@link
+     * android.car.diagnostic.IntegerSensorIndex#FUEL_TYPE}
+     */
+    public static final class FuelType {
+        private FuelType() {}
+
+        public static final int NOT_AVAILABLE = 0;
+        public static final int GASOLINE = 1;
+        public static final int METHANOL = 2;
+        public static final int ETHANOL = 3;
+        public static final int DIESEL = 4;
+        public static final int LPG = 5;
+        public static final int CNG = 6;
+        public static final int PROPANE = 7;
+        public static final int ELECTRIC = 8;
+        public static final int BIFUEL_RUNNING_GASOLINE = 9;
+        public static final int BIFUEL_RUNNING_METHANOL = 10;
+        public static final int BIFUEL_RUNNING_ETHANOL = 11;
+        public static final int BIFUEL_RUNNING_LPG = 12;
+        public static final int BIFUEL_RUNNING_CNG = 13;
+        public static final int BIFUEL_RUNNING_PROPANE = 14;
+        public static final int BIFUEL_RUNNING_ELECTRIC = 15;
+        public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16;
+        public static final int HYBRID_GASOLINE = 17;
+        public static final int HYBRID_ETHANOL = 18;
+        public static final int HYBRID_DIESEL = 19;
+        public static final int HYBRID_ELECTRIC = 20;
+        public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21;
+        public static final int HYBRID_REGENERATIVE = 22;
+        public static final int BIFUEL_RUNNING_DIESEL = 23;
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({
+            NOT_AVAILABLE,
+            GASOLINE,
+            METHANOL,
+            ETHANOL,
+            DIESEL,
+            LPG,
+            CNG,
+            PROPANE,
+            ELECTRIC,
+            BIFUEL_RUNNING_GASOLINE,
+            BIFUEL_RUNNING_METHANOL,
+            BIFUEL_RUNNING_ETHANOL,
+            BIFUEL_RUNNING_LPG,
+            BIFUEL_RUNNING_CNG,
+            BIFUEL_RUNNING_PROPANE,
+            BIFUEL_RUNNING_ELECTRIC,
+            BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION,
+            HYBRID_GASOLINE,
+            HYBRID_ETHANOL,
+            HYBRID_DIESEL,
+            HYBRID_ELECTRIC,
+            HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION,
+            HYBRID_REGENERATIVE,
+            BIFUEL_RUNNING_DIESEL
+        })
+        /** @hide */
+        public @interface Type {}
+    }
+
+    /**
+     * Represents the state of an ignition monitor on a vehicle.
+     */
+    public static final class IgnitionMonitor {
+        public final boolean available;
+        public final boolean incomplete;
+
+        IgnitionMonitor(boolean available, boolean incomplete) {
+            this.available = available;
+            this.incomplete = incomplete;
+        }
+
+        /** @hide */
+        public static final class Decoder {
+            private final int mAvailableBitmask;
+            private final int mIncompleteBitmask;
+
+            Decoder(int availableBitmask, int incompleteBitmask) {
+                mAvailableBitmask = availableBitmask;
+                mIncompleteBitmask = incompleteBitmask;
+            }
+
+            public IgnitionMonitor fromValue(int value) {
+                boolean available = (0 != (value & mAvailableBitmask));
+                boolean incomplete = (0 != (value & mIncompleteBitmask));
+
+                return new IgnitionMonitor(available, incomplete);
+            }
+        }
+    }
+
+    /**
+     * Contains information about ignition monitors common to all vehicle types.
+     */
+    public static class CommonIgnitionMonitors {
+        public final IgnitionMonitor components;
+        public final IgnitionMonitor fuelSystem;
+        public final IgnitionMonitor misfire;
+
+        /** @hide */
+        public static final int COMPONENTS_AVAILABLE = 0x1 << 0;
+        /** @hide */
+        public static final int COMPONENTS_INCOMPLETE = 0x1 << 1;
+
+        /** @hide */
+        public static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2;
+        /** @hide */
+        public static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3;
+
+        /** @hide */
+        public static final int MISFIRE_AVAILABLE = 0x1 << 4;
+        /** @hide */
+        public static final int MISFIRE_INCOMPLETE = 0x1 << 5;
+
+        static final IgnitionMonitor.Decoder COMPONENTS_DECODER =
+                new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER =
+                new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder MISFIRE_DECODER =
+                new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE);
+
+        CommonIgnitionMonitors(int bitmask) {
+            components = COMPONENTS_DECODER.fromValue(bitmask);
+            fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask);
+            misfire = MISFIRE_DECODER.fromValue(bitmask);
+        }
+
+        /**
+         * Returns data about ignition monitors specific to spark vehicles, if this
+         * object represents ignition monitors for a spark vehicle.
+         * Returns null otherwise.
+         */
+        public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
+            if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this;
+            return null;
+        }
+
+        /**
+         * Returns data about ignition monitors specific to compression vehicles, if this
+         * object represents ignition monitors for a compression vehicle.
+         * Returns null otherwise.
+         */
+        public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
+            if (this instanceof CompressionIgnitionMonitors)
+                return (CompressionIgnitionMonitors) this;
+            return null;
+        }
+    }
+
+    /**
+     * Contains information about ignition monitors specific to spark vehicles.
+     */
+    public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors {
+        public final IgnitionMonitor EGR;
+        public final IgnitionMonitor oxygenSensorHeater;
+        public final IgnitionMonitor oxygenSensor;
+        public final IgnitionMonitor ACRefrigerant;
+        public final IgnitionMonitor secondaryAirSystem;
+        public final IgnitionMonitor evaporativeSystem;
+        public final IgnitionMonitor heatedCatalyst;
+        public final IgnitionMonitor catalyst;
+
+        /** @hide */
+        public static final int EGR_AVAILABLE = 0x1 << 6;
+        /** @hide */
+        public static final int EGR_INCOMPLETE = 0x1 << 7;
+
+        /** @hide */
+        public static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8;
+        /** @hide */
+        public static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9;
+
+        /** @hide */
+        public static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10;
+        /** @hide */
+        public static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11;
+
+        /** @hide */
+        public static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12;
+        /** @hide */
+        public static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13;
+
+        /** @hide */
+        public static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14;
+        /** @hide */
+        public static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15;
+
+        /** @hide */
+        public static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16;
+        /** @hide */
+        public static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17;
+
+        /** @hide */
+        public static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18;
+        /** @hide */
+        public static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19;
+
+        /** @hide */
+        public static final int CATALYST_AVAILABLE = 0x1 << 20;
+        /** @hide */
+        public static final int CATALYST_INCOMPLETE = 0x1 << 21;
+
+        static final IgnitionMonitor.Decoder EGR_DECODER =
+                new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER =
+                new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE,
+                        OXYGEN_SENSOR_HEATER_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER =
+                new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER =
+                new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE,
+                        AC_REFRIGERANT_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER =
+                new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE,
+                        SECONDARY_AIR_SYSTEM_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER =
+                new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE,
+                        EVAPORATIVE_SYSTEM_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER =
+                new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE,
+                        HEATED_CATALYST_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder CATALYST_DECODER =
+                new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE);
+
+        SparkIgnitionMonitors(int bitmask) {
+            super(bitmask);
+            EGR = EGR_DECODER.fromValue(bitmask);
+            oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask);
+            oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask);
+            ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask);
+            secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask);
+            evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask);
+            heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask);
+            catalyst = CATALYST_DECODER.fromValue(bitmask);
+        }
+    }
+
+    /**
+     * Contains information about ignition monitors specific to compression vehicles.
+     */
+    public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors {
+        public final IgnitionMonitor EGROrVVT;
+        public final IgnitionMonitor PMFilter;
+        public final IgnitionMonitor exhaustGasSensor;
+        public final IgnitionMonitor boostPressure;
+        public final IgnitionMonitor NOxSCR;
+        public final IgnitionMonitor NMHCCatalyst;
+
+        /** @hide */
+        public static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6;
+        /** @hide */
+        public static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7;
+
+        /** @hide */
+        public static final int PM_FILTER_AVAILABLE = 0x1 << 8;
+        /** @hide */
+        public static final int PM_FILTER_INCOMPLETE = 0x1 << 9;
+
+        /** @hide */
+        public static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10;
+        /** @hide */
+        public static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11;
+
+        /** @hide */
+        public static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12;
+        /** @hide */
+        public static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13;
+
+        /** @hide */
+        public static final int NOx_SCR_AVAILABLE = 0x1 << 14;
+        /** @hide */
+        public static final int NOx_SCR_INCOMPLETE = 0x1 << 15;
+
+        /** @hide */
+        public static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
+        /** @hide */
+        public static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
+
+        static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER =
+                new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder PM_FILTER_DECODER =
+                new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER =
+                new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE,
+                        EXHAUST_GAS_SENSOR_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER =
+                new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE,
+                        BOOST_PRESSURE_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder NOx_SCR_DECODER =
+                new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE);
+
+        static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER =
+                new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE);
+
+        CompressionIgnitionMonitors(int bitmask) {
+            super(bitmask);
+            EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask);
+            PMFilter = PM_FILTER_DECODER.fromValue(bitmask);
+            exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask);
+            boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask);
+            NOxSCR = NOx_SCR_DECODER.fromValue(bitmask);
+            NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask);
+        }
+    }
+
+    /**
+     * Returns the state of the fuel system, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
+        return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_SYSTEM_STATUS);
+    }
+
+    /**
+     * Returns the state of the secondary air system, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
+        return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
+    }
+
+    /**
+     * Returns data about the ignition monitors, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable CommonIgnitionMonitors getIgnitionMonitors() {
+        Integer ignitionMonitorsType =
+                getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED);
+        Integer ignitionMonitorsBitmask =
+                getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS);
+        if (null == ignitionMonitorsType) return null;
+        if (null == ignitionMonitorsBitmask) return null;
+        switch (ignitionMonitorsType) {
+            case 0:
+                return new SparkIgnitionMonitors(ignitionMonitorsBitmask);
+            case 1:
+                return new CompressionIgnitionMonitors(ignitionMonitorsBitmask);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Returns the fuel type, if present in this frame.
+     * Returns null otherwise.
+     */
+    public @Nullable @FuelType.Type Integer getFuelType() {
+        return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_TYPE);
+    }
+}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticManager.java b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
similarity index 81%
rename from car-lib/src/android/car/hardware/CarDiagnosticManager.java
rename to car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
index 0444c14..84cdd3e 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticManager.java
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
-package android.car.hardware;
+package android.car.diagnostic;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.car.Car;
 import android.car.CarApiUtil;
 import android.car.CarLibLog;
 import android.car.CarManagerBase;
 import android.car.CarNotConnectedException;
+import android.car.diagnostic.ICarDiagnosticEventListener.Stub;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
@@ -40,16 +43,22 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-/** API for monitoring car diagnostic data. */
-/** @hide */
+/**
+ * API for monitoring car diagnostic data.
+ *
+ * @hide
+ */
+@SystemApi
 public final class CarDiagnosticManager implements CarManagerBase {
     public static final int FRAME_TYPE_LIVE = 0;
     public static final int FRAME_TYPE_FREEZE = 1;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({FRAME_TYPE_LIVE, FRAME_TYPE_FREEZE})
+    /** @hide */
     public @interface FrameType {}
 
+    /** @hide */
     public static final @FrameType int FRAME_TYPES[] = {
         FRAME_TYPE_LIVE,
         FRAME_TYPE_FREEZE
@@ -67,6 +76,7 @@
 
     private final CarPermission mVendorExtensionPermission;
 
+    /** @hide */
     public CarDiagnosticManager(IBinder service, Context context, Handler handler) {
         mService = ICarDiagnostic.Stub.asInterface(service);
         mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(handler.getLooper(),
@@ -86,6 +96,7 @@
     }
 
     @Override
+    /** @hide */
     public void onCarDisconnected() {
         synchronized(mActiveListeners) {
             mActiveListeners.clear();
@@ -207,10 +218,12 @@
 
     /**
      * Retrieve the most-recently acquired live frame data from the car.
-     * @return
+     * @return A CarDiagnostic event for the most recently known live frame if one is present.
+     *         null if no live frame has been recorded by the vehicle.
      * @throws CarNotConnectedException
      */
-    public CarDiagnosticEvent getLatestLiveFrame() throws CarNotConnectedException {
+    public @Nullable
+    CarDiagnosticEvent getLatestLiveFrame() throws CarNotConnectedException {
         try {
             return mService.getLatestLiveFrame();
         } catch (IllegalStateException e) {
@@ -223,7 +236,12 @@
 
     /**
      * Return the list of the timestamps for which a freeze frame is currently stored.
-     * @return
+     * @return An array containing timestamps at which, at the current time, the vehicle has
+     *         a freeze frame stored. If no freeze frames are currently stored, an empty
+     *         array will be returned.
+     * Because vehicles might have a limited amount of storage for frames, clients cannot
+     * assume that a timestamp obtained via this call will be indefinitely valid for retrieval
+     * of the actual diagnostic data, and must be prepared to handle a missing frame.
      * @throws CarNotConnectedException
      */
     public long[] getFreezeFrameTimestamps() throws CarNotConnectedException {
@@ -240,10 +258,16 @@
     /**
      * Retrieve the freeze frame event data for a given timestamp, if available.
      * @param timestamp
-     * @return
+     * @return A CarDiagnostic event for the frame at the given timestamp, if one is
+     *         available. null is returned otherwise.
+     * Storage constraints might cause frames to be deleted from vehicle memory.
+     * For this reason it cannot be assumed that a timestamp will yield a valid frame,
+     * even if it was initially obtained via a call to getFreezeFrameTimestamps().
      * @throws CarNotConnectedException
      */
-    public CarDiagnosticEvent getFreezeFrame(long timestamp) throws CarNotConnectedException {
+    public @Nullable
+    CarDiagnosticEvent getFreezeFrame(long timestamp)
+        throws CarNotConnectedException {
         try {
             return mService.getFreezeFrame(timestamp);
         } catch (IllegalStateException e) {
@@ -256,8 +280,13 @@
 
     /**
      * Clear the freeze frame information from vehicle memory at the given timestamps.
-     * @param timestamps
-     * @return
+     * @param timestamps A list of timestamps to delete freeze frames at, or an empty array
+     *                   to delete all freeze frames from vehicle memory.
+     * @return true if all the required frames were deleted (including if no timestamps are
+     *         provided and all frames were cleared); false otherwise.
+     * Due to storage constraints, timestamps cannot be assumed to be indefinitely valid, and
+     * a false return from this method should be used by the client as cause for invalidating
+     * its local knowledge of the vehicle diagnostic state.
      * @throws CarNotConnectedException
      */
     public boolean clearFreezeFrames(long... timestamps) throws CarNotConnectedException {
@@ -288,13 +317,13 @@
     }
 
     /**
-     * Returns true if this vehicle supports sending freeze frame information.
-     * @return
+     * Returns true if this vehicle supports supports sending notifications to
+     * registered listeners when new freeze frames happen.
      * @throws CarNotConnectedException
      */
-    public boolean isFreezeFrameSupported() throws CarNotConnectedException {
+    public boolean isFreezeFrameNotificationSupported() throws CarNotConnectedException {
         try {
-            return mService.isFreezeFrameSupported();
+            return mService.isFreezeFrameNotificationSupported();
         } catch (IllegalStateException e) {
             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
         } catch (RemoteException e) {
@@ -304,14 +333,13 @@
     }
 
     /**
-     * Returns true if this vehicle supports retrieving freeze frame timestamps.
-     * This is only meaningful if freeze frame data is also supported.
-     * @return
+     * Returns whether the underlying HAL supports retrieving freeze frames
+     * stored in vehicle memory using timestamp.
      * @throws CarNotConnectedException
      */
-    public boolean isFreezeFrameTimestampSupported() throws CarNotConnectedException {
+    public boolean isGetFreezeFrameSupported() throws CarNotConnectedException {
         try {
-            return mService.isFreezeFrameTimestampSupported();
+            return mService.isGetFreezeFrameSupported();
         } catch (IllegalStateException e) {
             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
         } catch (RemoteException e) {
@@ -326,9 +354,9 @@
      * @return
      * @throws CarNotConnectedException
      */
-    public boolean isFreezeFrameClearSupported() throws CarNotConnectedException {
+    public boolean isClearFreezeFramesSupported() throws CarNotConnectedException {
         try {
-            return mService.isFreezeFrameClearSupported();
+            return mService.isClearFreezeFramesSupported();
         } catch (IllegalStateException e) {
             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
         } catch (RemoteException e) {
@@ -338,7 +366,7 @@
     }
 
     private static class CarDiagnosticEventListenerToService
-            extends ICarDiagnosticEventListener.Stub {
+            extends Stub {
         private final WeakReference<CarDiagnosticManager> mManager;
 
         public CarDiagnosticEventListenerToService(CarDiagnosticManager manager) {
diff --git a/car-lib/src/android/car/diagnostic/FloatSensorIndex.java b/car-lib/src/android/car/diagnostic/FloatSensorIndex.java
new file mode 100644
index 0000000..6ec7e8c
--- /dev/null
+++ b/car-lib/src/android/car/diagnostic/FloatSensorIndex.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.car.diagnostic;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class is a container for the indices of diagnostic sensors. The values are extracted by
+ * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.
+ *
+ * DO NOT EDIT MANUALLY
+ *
+ * @hide
+ */
+@SystemApi
+public final class FloatSensorIndex {
+    private FloatSensorIndex() {}
+
+    public static final int CALCULATED_ENGINE_LOAD = 0;
+    public static final int ENGINE_COOLANT_TEMPERATURE = 1;
+    public static final int SHORT_TERM_FUEL_TRIM_BANK1 = 2;
+    public static final int LONG_TERM_FUEL_TRIM_BANK1 = 3;
+    public static final int SHORT_TERM_FUEL_TRIM_BANK2 = 4;
+    public static final int LONG_TERM_FUEL_TRIM_BANK2 = 5;
+    public static final int FUEL_PRESSURE = 6;
+    public static final int INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7;
+    public static final int ENGINE_RPM = 8;
+    public static final int VEHICLE_SPEED = 9;
+    public static final int TIMING_ADVANCE = 10;
+    public static final int MAF_AIR_FLOW_RATE = 11;
+    public static final int THROTTLE_POSITION = 12;
+    public static final int OXYGEN_SENSOR1_VOLTAGE = 13;
+    public static final int OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14;
+    public static final int OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15;
+    public static final int OXYGEN_SENSOR2_VOLTAGE = 16;
+    public static final int OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17;
+    public static final int OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18;
+    public static final int OXYGEN_SENSOR3_VOLTAGE = 19;
+    public static final int OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20;
+    public static final int OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21;
+    public static final int OXYGEN_SENSOR4_VOLTAGE = 22;
+    public static final int OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23;
+    public static final int OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24;
+    public static final int OXYGEN_SENSOR5_VOLTAGE = 25;
+    public static final int OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26;
+    public static final int OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27;
+    public static final int OXYGEN_SENSOR6_VOLTAGE = 28;
+    public static final int OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29;
+    public static final int OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30;
+    public static final int OXYGEN_SENSOR7_VOLTAGE = 31;
+    public static final int OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32;
+    public static final int OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33;
+    public static final int OXYGEN_SENSOR8_VOLTAGE = 34;
+    public static final int OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35;
+    public static final int OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36;
+    public static final int FUEL_RAIL_PRESSURE = 37;
+    public static final int FUEL_RAIL_GAUGE_PRESSURE = 38;
+    public static final int COMMANDED_EXHAUST_GAS_RECIRCULATION = 39;
+    public static final int EXHAUST_GAS_RECIRCULATION_ERROR = 40;
+    public static final int COMMANDED_EVAPORATIVE_PURGE = 41;
+    public static final int FUEL_TANK_LEVEL_INPUT = 42;
+    public static final int EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43;
+    public static final int CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44;
+    public static final int CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45;
+    public static final int CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46;
+    public static final int CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47;
+    public static final int ABSOLUTE_LOAD_VALUE = 48;
+    public static final int FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49;
+    public static final int RELATIVE_THROTTLE_POSITION = 50;
+    public static final int ABSOLUTE_THROTTLE_POSITION_B = 51;
+    public static final int ABSOLUTE_THROTTLE_POSITION_C = 52;
+    public static final int ACCELERATOR_PEDAL_POSITION_D = 53;
+    public static final int ACCELERATOR_PEDAL_POSITION_E = 54;
+    public static final int ACCELERATOR_PEDAL_POSITION_F = 55;
+    public static final int COMMANDED_THROTTLE_ACTUATOR = 56;
+    public static final int ETHANOL_FUEL_PERCENTAGE = 57;
+    public static final int ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58;
+    public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59;
+    public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60;
+    public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61;
+    public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62;
+    public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63;
+    public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64;
+    public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65;
+    public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66;
+    public static final int RELATIVE_ACCELERATOR_PEDAL_POSITION = 67;
+    public static final int HYBRID_BATTERY_PACK_REMAINING_LIFE = 68;
+    public static final int FUEL_INJECTION_TIMING = 69;
+    public static final int ENGINE_FUEL_RATE = 70;
+    public static final int LAST_SYSTEM = ENGINE_FUEL_RATE;
+    public static final int VENDOR_START = LAST_SYSTEM + 1;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        FloatSensorIndex.CALCULATED_ENGINE_LOAD,
+        FloatSensorIndex.ENGINE_COOLANT_TEMPERATURE,
+        FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK1,
+        FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK1,
+        FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK2,
+        FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK2,
+        FloatSensorIndex.FUEL_PRESSURE,
+        FloatSensorIndex.INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
+        FloatSensorIndex.ENGINE_RPM,
+        FloatSensorIndex.VEHICLE_SPEED,
+        FloatSensorIndex.TIMING_ADVANCE,
+        FloatSensorIndex.MAF_AIR_FLOW_RATE,
+        FloatSensorIndex.THROTTLE_POSITION,
+        FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.OXYGEN_SENSOR2_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.OXYGEN_SENSOR3_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.OXYGEN_SENSOR4_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.OXYGEN_SENSOR7_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.OXYGEN_SENSOR8_VOLTAGE,
+        FloatSensorIndex.OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM,
+        FloatSensorIndex.OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO,
+        FloatSensorIndex.FUEL_RAIL_PRESSURE,
+        FloatSensorIndex.FUEL_RAIL_GAUGE_PRESSURE,
+        FloatSensorIndex.COMMANDED_EXHAUST_GAS_RECIRCULATION,
+        FloatSensorIndex.EXHAUST_GAS_RECIRCULATION_ERROR,
+        FloatSensorIndex.COMMANDED_EVAPORATIVE_PURGE,
+        FloatSensorIndex.FUEL_TANK_LEVEL_INPUT,
+        FloatSensorIndex.EVAPORATION_SYSTEM_VAPOR_PRESSURE,
+        FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR1,
+        FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR1,
+        FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR2,
+        FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR2,
+        FloatSensorIndex.ABSOLUTE_LOAD_VALUE,
+        FloatSensorIndex.FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO,
+        FloatSensorIndex.RELATIVE_THROTTLE_POSITION,
+        FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_B,
+        FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_C,
+        FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_D,
+        FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_E,
+        FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_F,
+        FloatSensorIndex.COMMANDED_THROTTLE_ACTUATOR,
+        FloatSensorIndex.ETHANOL_FUEL_PERCENTAGE,
+        FloatSensorIndex.ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE,
+        FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
+        FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
+        FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
+        FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
+        FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
+        FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
+        FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
+        FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
+        FloatSensorIndex.RELATIVE_ACCELERATOR_PEDAL_POSITION,
+        FloatSensorIndex.HYBRID_BATTERY_PACK_REMAINING_LIFE,
+        FloatSensorIndex.FUEL_INJECTION_TIMING,
+        FloatSensorIndex.ENGINE_FUEL_RATE,
+        FloatSensorIndex.LAST_SYSTEM,
+        FloatSensorIndex.VENDOR_START,
+    })
+    public @interface SensorIndex {}
+
+}
diff --git a/car-lib/src/android/car/hardware/ICarDiagnostic.aidl b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
similarity index 79%
rename from car-lib/src/android/car/hardware/ICarDiagnostic.aidl
rename to car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
index 3afffc5..3d1808f 100644
--- a/car-lib/src/android/car/hardware/ICarDiagnostic.aidl
+++ b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.car.hardware;
+package android.car.diagnostic;
 
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.ICarDiagnosticEventListener;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.ICarDiagnosticEventListener;
 
 /** @hide */
 interface ICarDiagnostic {
@@ -59,17 +59,19 @@
      boolean isLiveFrameSupported() = 7;
 
     /**
-     * Returns whether the underlying HAL supports freeze frames.
+     * Returns whether the underlying HAL supports sending notifications to
+     * registered listeners when new freeze frames happen.
      */
-     boolean isFreezeFrameSupported() = 8;
+     boolean isFreezeFrameNotificationSupported() = 8;
 
     /**
-     * Returns whether the underlying HAL supports retrieving freeze frame timestamps.
+     * Returns whether the underlying HAL supports retrieving freeze frames
+     * stored in vehicle memory using timestamp.
      */
-     boolean isFreezeFrameTimestampSupported() = 9;
+     boolean isGetFreezeFrameSupported() = 9;
 
     /**
      * Returns whether the underlying HAL supports clearing freeze frames.
      */
-     boolean isFreezeFrameClearSupported() = 10;
+     boolean isClearFreezeFramesSupported() = 10;
 }
diff --git a/car-lib/src/android/car/hardware/ICarDiagnosticEventListener.aidl b/car-lib/src/android/car/diagnostic/ICarDiagnosticEventListener.aidl
similarity index 89%
rename from car-lib/src/android/car/hardware/ICarDiagnosticEventListener.aidl
rename to car-lib/src/android/car/diagnostic/ICarDiagnosticEventListener.aidl
index 3c9a189..89070aa 100644
--- a/car-lib/src/android/car/hardware/ICarDiagnosticEventListener.aidl
+++ b/car-lib/src/android/car/diagnostic/ICarDiagnosticEventListener.aidl
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.car.hardware;
+package android.car.diagnostic;
 
-import android.car.hardware.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticEvent;
 
 /**
  * @hide
diff --git a/car-lib/src/android/car/diagnostic/IntegerSensorIndex.java b/car-lib/src/android/car/diagnostic/IntegerSensorIndex.java
new file mode 100644
index 0000000..15291f7
--- /dev/null
+++ b/car-lib/src/android/car/diagnostic/IntegerSensorIndex.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.car.diagnostic;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class is a container for the indices of diagnostic sensors. The values are extracted by
+ * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.
+ *
+ * DO NOT EDIT MANUALLY
+ *
+ * @hide
+ */
+@SystemApi
+public final class IntegerSensorIndex {
+    private IntegerSensorIndex() {}
+
+    public static final int FUEL_SYSTEM_STATUS = 0;
+    public static final int MALFUNCTION_INDICATOR_LIGHT_ON = 1;
+    public static final int IGNITION_MONITORS_SUPPORTED = 2;
+    public static final int IGNITION_SPECIFIC_MONITORS = 3;
+    public static final int INTAKE_AIR_TEMPERATURE = 4;
+    public static final int COMMANDED_SECONDARY_AIR_STATUS = 5;
+    public static final int NUM_OXYGEN_SENSORS_PRESENT = 6;
+    public static final int RUNTIME_SINCE_ENGINE_START = 7;
+    public static final int DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8;
+    public static final int WARMUPS_SINCE_CODES_CLEARED = 9;
+    public static final int DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10;
+    public static final int ABSOLUTE_BAROMETRIC_PRESSURE = 11;
+    public static final int CONTROL_MODULE_VOLTAGE = 12;
+    public static final int AMBIENT_AIR_TEMPERATURE = 13;
+    public static final int TIME_WITH_MALFUNCTION_LIGHT_ON = 14;
+    public static final int TIME_SINCE_TROUBLE_CODES_CLEARED = 15;
+    public static final int MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16;
+    public static final int MAX_OXYGEN_SENSOR_VOLTAGE = 17;
+    public static final int MAX_OXYGEN_SENSOR_CURRENT = 18;
+    public static final int MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19;
+    public static final int MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20;
+    public static final int FUEL_TYPE = 21;
+    public static final int FUEL_RAIL_ABSOLUTE_PRESSURE = 22;
+    public static final int ENGINE_OIL_TEMPERATURE = 23;
+    public static final int DRIVER_DEMAND_PERCENT_TORQUE = 24;
+    public static final int ENGINE_ACTUAL_PERCENT_TORQUE = 25;
+    public static final int ENGINE_REFERENCE_PERCENT_TORQUE = 26;
+    public static final int ENGINE_PERCENT_TORQUE_DATA_IDLE = 27;
+    public static final int ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28;
+    public static final int ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29;
+    public static final int ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30;
+    public static final int ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31;
+    public static final int LAST_SYSTEM = ENGINE_PERCENT_TORQUE_DATA_POINT4;
+    public static final int VENDOR_START = LAST_SYSTEM + 1;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        IntegerSensorIndex.FUEL_SYSTEM_STATUS,
+        IntegerSensorIndex.MALFUNCTION_INDICATOR_LIGHT_ON,
+        IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED,
+        IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS,
+        IntegerSensorIndex.INTAKE_AIR_TEMPERATURE,
+        IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
+        IntegerSensorIndex.NUM_OXYGEN_SENSORS_PRESENT,
+        IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START,
+        IntegerSensorIndex.DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON,
+        IntegerSensorIndex.WARMUPS_SINCE_CODES_CLEARED,
+        IntegerSensorIndex.DISTANCE_TRAVELED_SINCE_CODES_CLEARED,
+        IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE,
+        IntegerSensorIndex.CONTROL_MODULE_VOLTAGE,
+        IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE,
+        IntegerSensorIndex.TIME_WITH_MALFUNCTION_LIGHT_ON,
+        IntegerSensorIndex.TIME_SINCE_TROUBLE_CODES_CLEARED,
+        IntegerSensorIndex.MAX_FUEL_AIR_EQUIVALENCE_RATIO,
+        IntegerSensorIndex.MAX_OXYGEN_SENSOR_VOLTAGE,
+        IntegerSensorIndex.MAX_OXYGEN_SENSOR_CURRENT,
+        IntegerSensorIndex.MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
+        IntegerSensorIndex.MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR,
+        IntegerSensorIndex.FUEL_TYPE,
+        IntegerSensorIndex.FUEL_RAIL_ABSOLUTE_PRESSURE,
+        IntegerSensorIndex.ENGINE_OIL_TEMPERATURE,
+        IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE,
+        IntegerSensorIndex.ENGINE_ACTUAL_PERCENT_TORQUE,
+        IntegerSensorIndex.ENGINE_REFERENCE_PERCENT_TORQUE,
+        IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_IDLE,
+        IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT1,
+        IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT2,
+        IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT3,
+        IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT4,
+        IntegerSensorIndex.LAST_SYSTEM,
+        IntegerSensorIndex.VENDOR_START,
+    })
+    public @interface SensorIndex {}
+
+}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticEvent.java b/car-lib/src/android/car/hardware/CarDiagnosticEvent.java
deleted file mode 100644
index 1d82533..0000000
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.java
+++ /dev/null
@@ -1,762 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.hardware;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.car.annotation.FutureFeature;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.JsonWriter;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * A CarDiagnosticEvent object corresponds to a single diagnostic event frame coming from the car.
- *
- * @hide
- */
-@FutureFeature
-public class CarDiagnosticEvent implements Parcelable {
-    /** Whether this frame represents a live or a freeze frame */
-    public final int frameType;
-
-    /**
-     * When this data was acquired in car or received from car. It is elapsed real-time of data
-     * reception from car in nanoseconds since system boot.
-     */
-    public final long timestamp;
-
-    /**
-     * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
-     * integer valued properties
-     */
-    private final SparseIntArray intValues;
-
-    /**
-     * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
-     * float valued properties
-     */
-    private final SparseArray<Float> floatValues;
-
-    /**
-     * Diagnostic Troubleshooting Code (DTC) that was detected and caused this frame to be stored
-     * (if a freeze frame). Always null for a live frame.
-     */
-    public final String dtc;
-
-    public CarDiagnosticEvent(Parcel in) {
-        frameType = in.readInt();
-        timestamp = in.readLong();
-        int len = in.readInt();
-        floatValues = new SparseArray<>(len);
-        for (int i = 0; i < len; ++i) {
-            int key = in.readInt();
-            float value = in.readFloat();
-            floatValues.put(key, value);
-        }
-        len = in.readInt();
-        intValues = new SparseIntArray(len);
-        for (int i = 0; i < len; ++i) {
-            int key = in.readInt();
-            int value = in.readInt();
-            intValues.put(key, value);
-        }
-        dtc = (String) in.readValue(String.class.getClassLoader());
-        // version 1 up to here
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(frameType);
-        dest.writeLong(timestamp);
-        dest.writeInt(floatValues.size());
-        for (int i = 0; i < floatValues.size(); ++i) {
-            int key = floatValues.keyAt(i);
-            dest.writeInt(key);
-            dest.writeFloat(floatValues.get(key));
-        }
-        dest.writeInt(intValues.size());
-        for (int i = 0; i < intValues.size(); ++i) {
-            int key = intValues.keyAt(i);
-            dest.writeInt(key);
-            dest.writeInt(intValues.get(key));
-        }
-        dest.writeValue(dtc);
-    }
-
-    public void writeToJson(JsonWriter jsonWriter) throws IOException {
-        jsonWriter.beginObject();
-
-        jsonWriter.name("type");
-        switch (frameType) {
-            case CarDiagnosticManager.FRAME_TYPE_LIVE:
-                jsonWriter.value("live");
-                break;
-            case CarDiagnosticManager.FRAME_TYPE_FREEZE:
-                jsonWriter.value("freeze");
-                break;
-            default:
-                throw new IllegalStateException("unknown frameType " + frameType);
-        }
-
-        jsonWriter.name("timestamp").value(timestamp);
-
-        jsonWriter.name("intValues").beginArray();
-        for (int i = 0; i < intValues.size(); ++i) {
-            jsonWriter.beginObject();
-            jsonWriter.name("id").value(intValues.keyAt(i));
-            jsonWriter.name("value").value(intValues.valueAt(i));
-            jsonWriter.endObject();
-        }
-        jsonWriter.endArray();
-
-        jsonWriter.name("floatValues").beginArray();
-        for (int i = 0; i < floatValues.size(); ++i) {
-            jsonWriter.beginObject();
-            jsonWriter.name("id").value(floatValues.keyAt(i));
-            jsonWriter.name("value").value(floatValues.valueAt(i));
-            jsonWriter.endObject();
-        }
-        jsonWriter.endArray();
-
-        if (dtc != null) {
-            jsonWriter.name("stringValue").value(dtc);
-        }
-
-        jsonWriter.endObject();
-    }
-
-    public static final Parcelable.Creator<CarDiagnosticEvent> CREATOR =
-            new Parcelable.Creator<CarDiagnosticEvent>() {
-                public CarDiagnosticEvent createFromParcel(Parcel in) {
-                    return new CarDiagnosticEvent(in);
-                }
-
-                public CarDiagnosticEvent[] newArray(int size) {
-                    return new CarDiagnosticEvent[size];
-                }
-            };
-
-    private CarDiagnosticEvent(
-            int frameType,
-            long timestamp,
-            SparseArray<Float> floatValues,
-            SparseIntArray intValues,
-            String dtc) {
-        this.frameType = frameType;
-        this.timestamp = timestamp;
-        this.floatValues = floatValues;
-        this.intValues = intValues;
-        this.dtc = dtc;
-    }
-
-    public static class Builder {
-        private int mType = CarDiagnosticManager.FRAME_TYPE_LIVE;
-        private long mTimestamp = 0;
-        private SparseArray<Float> mFloatValues = new SparseArray<>();
-        private SparseIntArray mIntValues = new SparseIntArray();
-        private String mDtc = null;
-
-        private Builder(int type) {
-            mType = type;
-        }
-
-        public static Builder newLiveFrameBuilder() {
-            return new Builder(CarDiagnosticManager.FRAME_TYPE_LIVE);
-        }
-
-        public static Builder newFreezeFrameBuilder() {
-            return new Builder(CarDiagnosticManager.FRAME_TYPE_FREEZE);
-        }
-
-        public Builder atTimestamp(long timestamp) {
-            mTimestamp = timestamp;
-            return this;
-        }
-
-        public Builder withIntValue(int key, int value) {
-            mIntValues.put(key, value);
-            return this;
-        }
-
-        public Builder withFloatValue(int key, float value) {
-            mFloatValues.put(key, value);
-            return this;
-        }
-
-        public Builder withDTC(String dtc) {
-            mDtc = dtc;
-            return this;
-        }
-
-        public CarDiagnosticEvent build() {
-            return new CarDiagnosticEvent(mType, mTimestamp, mFloatValues, mIntValues, mDtc);
-        }
-    }
-
-    /**
-     * Returns a copy of this CarDiagnosticEvent with all vendor-specific sensors removed.
-     *
-     * @hide
-     */
-    public CarDiagnosticEvent withVendorSensorsRemoved() {
-        SparseIntArray newIntValues = intValues.clone();
-        SparseArray<Float> newFloatValues = floatValues.clone();
-        for (int i = 0; i < intValues.size(); ++i) {
-            int key = intValues.keyAt(i);
-            if (key >= CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.LAST_SYSTEM) {
-                newIntValues.delete(key);
-            }
-        }
-        for (int i = 0; i < floatValues.size(); ++i) {
-            int key = floatValues.keyAt(i);
-            if (key >= CarDiagnosticSensorIndices.Obd2FloatSensorIndex.LAST_SYSTEM) {
-                newFloatValues.delete(key);
-            }
-        }
-        return new CarDiagnosticEvent(frameType, timestamp, newFloatValues, newIntValues, dtc);
-    }
-
-    public boolean isLiveFrame() {
-        return CarDiagnosticManager.FRAME_TYPE_LIVE == frameType;
-    }
-
-    public boolean isFreezeFrame() {
-        return CarDiagnosticManager.FRAME_TYPE_FREEZE == frameType;
-    }
-
-    public boolean isEmptyFrame() {
-        boolean empty = (0 == intValues.size());
-        empty &= (0 == floatValues.size());
-        if (isFreezeFrame()) empty &= dtc.isEmpty();
-        return empty;
-    }
-
-    /** @hide */
-    public CarDiagnosticEvent checkLiveFrame() {
-        if (!isLiveFrame()) throw new IllegalStateException("frame is not a live frame");
-        return this;
-    }
-
-    /** @hide */
-    public CarDiagnosticEvent checkFreezeFrame() {
-        if (!isFreezeFrame()) throw new IllegalStateException("frame is not a freeze frame");
-        return this;
-    }
-
-    /** @hide */
-    public boolean isEarlierThan(CarDiagnosticEvent otherEvent) {
-        otherEvent = Objects.requireNonNull(otherEvent);
-        return (timestamp < otherEvent.timestamp);
-    }
-
-    @Override
-    public boolean equals(Object otherObject) {
-        if (this == otherObject) {
-            return true;
-        }
-        if (null == otherObject) {
-            return false;
-        }
-        if (!(otherObject instanceof CarDiagnosticEvent)) {
-            return false;
-        }
-        CarDiagnosticEvent otherEvent = (CarDiagnosticEvent)otherObject;
-        if (otherEvent.frameType != frameType)
-            return false;
-        if (otherEvent.timestamp != timestamp)
-            return false;
-        if (otherEvent.intValues.size() != intValues.size())
-            return false;
-        if (otherEvent.floatValues.size() != floatValues.size())
-            return false;
-        if (!Objects.equals(dtc, otherEvent.dtc))
-            return false;
-        for (int i = 0; i < intValues.size(); ++i) {
-            int key = intValues.keyAt(i);
-            int otherKey = otherEvent.intValues.keyAt(i);
-            if (key != otherKey) {
-                return false;
-            }
-            int value = intValues.valueAt(i);
-            int otherValue = otherEvent.intValues.valueAt(i);
-            if (value != otherValue) {
-                return false;
-            }
-        }
-        for (int i = 0; i < floatValues.size(); ++i) {
-            int key = floatValues.keyAt(i);
-            int otherKey = otherEvent.floatValues.keyAt(i);
-            if (key != otherKey) {
-                return false;
-            }
-            float value = floatValues.valueAt(i);
-            float otherValue = otherEvent.floatValues.valueAt(i);
-            if (value != otherValue) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        Integer[] intKeys = new Integer[intValues.size()];
-        Integer[] floatKeys = new Integer[floatValues.size()];
-        Integer[] intValues = new Integer[intKeys.length];
-        Float[] floatValues = new Float[floatKeys.length];
-        for (int i = 0; i < intKeys.length; ++i) {
-            intKeys[i] = this.intValues.keyAt(i);
-            intValues[i] = this.intValues.valueAt(i);
-        }
-        for (int i = 0; i < floatKeys.length; ++i) {
-            floatKeys[i] = this.floatValues.keyAt(i);
-            floatValues[i] = this.floatValues.valueAt(i);
-        }
-        int intKeysHash = Objects.hash((Object[])intKeys);
-        int intValuesHash = Objects.hash((Object[])intValues);
-        int floatKeysHash = Objects.hash((Object[])floatKeys);
-        int floatValuesHash = Objects.hash((Object[])floatValues);
-        return Objects.hash(frameType,
-                timestamp,
-                dtc,
-                intKeysHash,
-                intValuesHash,
-                floatKeysHash,
-                floatValuesHash);
-    }
-
-    @Override
-    public String toString() {
-        return String.format(
-                "%s diagnostic frame {\n"
-                        + "\ttimestamp: %d, "
-                        + "DTC: %s\n"
-                        + "\tintValues: %s\n"
-                        + "\tfloatValues: %s\n}",
-                isLiveFrame() ? "live" : "freeze",
-                timestamp,
-                dtc,
-                intValues.toString(),
-                floatValues.toString());
-    }
-
-    public int getSystemIntegerSensor(
-            @CarDiagnosticSensorIndices.IntegerSensorIndex int sensor, int defaultValue) {
-        return intValues.get(sensor, defaultValue);
-    }
-
-    public float getSystemFloatSensor(
-            @CarDiagnosticSensorIndices.FloatSensorIndex int sensor, float defaultValue) {
-        return floatValues.get(sensor, defaultValue);
-    }
-
-    public int getVendorIntegerSensor(int sensor, int defaultValue) {
-        return intValues.get(sensor, defaultValue);
-    }
-
-    public float getVendorFloatSensor(int sensor, float defaultValue) {
-        return floatValues.get(sensor, defaultValue);
-    }
-
-    public @Nullable Integer getSystemIntegerSensor(
-            @CarDiagnosticSensorIndices.IntegerSensorIndex int sensor) {
-        int index = intValues.indexOfKey(sensor);
-        if (index < 0) return null;
-        return intValues.valueAt(index);
-    }
-
-    public @Nullable Float getSystemFloatSensor(
-            @CarDiagnosticSensorIndices.FloatSensorIndex int sensor) {
-        int index = floatValues.indexOfKey(sensor);
-        if (index < 0) return null;
-        return floatValues.valueAt(index);
-    }
-
-    public @Nullable Integer getVendorIntegerSensor(int sensor) {
-        int index = intValues.indexOfKey(sensor);
-        if (index < 0) return null;
-        return intValues.valueAt(index);
-    }
-
-    public @Nullable Float getVendorFloatSensor(int sensor) {
-        int index = floatValues.indexOfKey(sensor);
-        if (index < 0) return null;
-        return floatValues.valueAt(index);
-    }
-
-    /**
-     * Represents possible states of the fuel system; see {@link
-     * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_SYSTEM_STATUS}
-     */
-    public static final class FuelSystemStatus {
-        private FuelSystemStatus() {}
-
-        public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1;
-        public static final int CLOSED_LOOP = 2;
-        public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4;
-        public static final int OPEN_SYSTEM_FAILURE = 8;
-        public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16;
-
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({
-            OPEN_INSUFFICIENT_ENGINE_TEMPERATURE,
-            CLOSED_LOOP,
-            OPEN_ENGINE_LOAD_OR_DECELERATION,
-            OPEN_SYSTEM_FAILURE,
-            CLOSED_LOOP_BUT_FEEDBACK_FAULT
-        })
-        public @interface Status {}
-    }
-
-    /**
-     * Represents possible states of the secondary air system; see {@link
-     * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
-     */
-    public static final class SecondaryAirStatus {
-        private SecondaryAirStatus() {}
-
-        public static final int UPSTREAM = 1;
-        public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2;
-        public static final int FROM_OUTSIDE_OR_OFF = 4;
-        public static final int PUMP_ON_FOR_DIAGNOSTICS = 8;
-
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({
-            UPSTREAM,
-            DOWNSTREAM_OF_CATALYCIC_CONVERTER,
-            FROM_OUTSIDE_OR_OFF,
-            PUMP_ON_FOR_DIAGNOSTICS
-        })
-        public @interface Status {}
-    }
-
-    /**
-     * Represents possible types of fuel; see {@link
-     * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_TYPE}
-     */
-    public static final class FuelType {
-        private FuelType() {}
-
-        public static final int NOT_AVAILABLE = 0;
-        public static final int GASOLINE = 1;
-        public static final int METHANOL = 2;
-        public static final int ETHANOL = 3;
-        public static final int DIESEL = 4;
-        public static final int LPG = 5;
-        public static final int CNG = 6;
-        public static final int PROPANE = 7;
-        public static final int ELECTRIC = 8;
-        public static final int BIFUEL_RUNNING_GASOLINE = 9;
-        public static final int BIFUEL_RUNNING_METHANOL = 10;
-        public static final int BIFUEL_RUNNING_ETHANOL = 11;
-        public static final int BIFUEL_RUNNING_LPG = 12;
-        public static final int BIFUEL_RUNNING_CNG = 13;
-        public static final int BIFUEL_RUNNING_PROPANE = 14;
-        public static final int BIFUEL_RUNNING_ELECTRIC = 15;
-        public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16;
-        public static final int HYBRID_GASOLINE = 17;
-        public static final int HYBRID_ETHANOL = 18;
-        public static final int HYBRID_DIESEL = 19;
-        public static final int HYBRID_ELECTRIC = 20;
-        public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21;
-        public static final int HYBRID_REGENERATIVE = 22;
-        public static final int BIFUEL_RUNNING_DIESEL = 23;
-
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({
-            NOT_AVAILABLE,
-            GASOLINE,
-            METHANOL,
-            ETHANOL,
-            DIESEL,
-            LPG,
-            CNG,
-            PROPANE,
-            ELECTRIC,
-            BIFUEL_RUNNING_GASOLINE,
-            BIFUEL_RUNNING_METHANOL,
-            BIFUEL_RUNNING_ETHANOL,
-            BIFUEL_RUNNING_LPG,
-            BIFUEL_RUNNING_CNG,
-            BIFUEL_RUNNING_PROPANE,
-            BIFUEL_RUNNING_ELECTRIC,
-            BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION,
-            HYBRID_GASOLINE,
-            HYBRID_ETHANOL,
-            HYBRID_DIESEL,
-            HYBRID_ELECTRIC,
-            HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION,
-            HYBRID_REGENERATIVE,
-            BIFUEL_RUNNING_DIESEL
-        })
-        public @interface Type {}
-    }
-
-    /**
-     * Represents possible states of the ignition monitors on the vehicle; see {@link
-     * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_MONITORS_SUPPORTED} see {@link
-     * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_SPECIFIC_MONITORS}
-     */
-    public static final class IgnitionMonitors {
-        public static final class IgnitionMonitor {
-            public final boolean available;
-            public final boolean incomplete;
-
-            IgnitionMonitor(boolean available, boolean incomplete) {
-                this.available = available;
-                this.incomplete = incomplete;
-            }
-
-            public static final class Decoder {
-                private final int mAvailableBitmask;
-                private final int mIncompleteBitmask;
-
-                Decoder(int availableBitmask, int incompleteBitmask) {
-                    mAvailableBitmask = availableBitmask;
-                    mIncompleteBitmask = incompleteBitmask;
-                }
-
-                public IgnitionMonitor fromValue(int value) {
-                    boolean available = (0 != (value & mAvailableBitmask));
-                    boolean incomplete = (0 != (value & mIncompleteBitmask));
-
-                    return new IgnitionMonitor(available, incomplete);
-                }
-            }
-        }
-
-        public static class CommonIgnitionMonitors {
-            public final IgnitionMonitor components;
-            public final IgnitionMonitor fuelSystem;
-            public final IgnitionMonitor misfire;
-
-            static final int COMPONENTS_AVAILABLE = 0x1 << 0;
-            static final int COMPONENTS_INCOMPLETE = 0x1 << 1;
-
-            static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2;
-            static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3;
-
-            static final int MISFIRE_AVAILABLE = 0x1 << 4;
-            static final int MISFIRE_INCOMPLETE = 0x1 << 5;
-
-            static final IgnitionMonitor.Decoder COMPONENTS_DECODER =
-                    new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER =
-                    new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder MISFIRE_DECODER =
-                    new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE);
-
-            CommonIgnitionMonitors(int bitmask) {
-                components = COMPONENTS_DECODER.fromValue(bitmask);
-                fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask);
-                misfire = MISFIRE_DECODER.fromValue(bitmask);
-            }
-
-            public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
-                if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this;
-                return null;
-            }
-
-            public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
-                if (this instanceof CompressionIgnitionMonitors)
-                    return (CompressionIgnitionMonitors) this;
-                return null;
-            }
-        }
-
-        public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors {
-            public final IgnitionMonitor EGR;
-            public final IgnitionMonitor oxygenSensorHeater;
-            public final IgnitionMonitor oxygenSensor;
-            public final IgnitionMonitor ACRefrigerant;
-            public final IgnitionMonitor secondaryAirSystem;
-            public final IgnitionMonitor evaporativeSystem;
-            public final IgnitionMonitor heatedCatalyst;
-            public final IgnitionMonitor catalyst;
-
-            static final int EGR_AVAILABLE = 0x1 << 6;
-            static final int EGR_INCOMPLETE = 0x1 << 7;
-
-            static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8;
-            static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9;
-
-            static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10;
-            static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11;
-
-            static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12;
-            static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13;
-
-            static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14;
-            static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15;
-
-            static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16;
-            static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17;
-
-            static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18;
-            static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19;
-
-            static final int CATALYST_AVAILABLE = 0x1 << 20;
-            static final int CATALYST_INCOMPLETE = 0x1 << 21;
-
-            static final IgnitionMonitor.Decoder EGR_DECODER =
-                    new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER =
-                    new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE,
-                            OXYGEN_SENSOR_HEATER_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER =
-                    new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER =
-                    new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE,
-                            AC_REFRIGERANT_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER =
-                    new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE,
-                            SECONDARY_AIR_SYSTEM_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER =
-                    new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE,
-                            EVAPORATIVE_SYSTEM_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER =
-                    new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE,
-                            HEATED_CATALYST_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder CATALYST_DECODER =
-                    new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE);
-
-            SparkIgnitionMonitors(int bitmask) {
-                super(bitmask);
-                EGR = EGR_DECODER.fromValue(bitmask);
-                oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask);
-                oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask);
-                ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask);
-                secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask);
-                evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask);
-                heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask);
-                catalyst = CATALYST_DECODER.fromValue(bitmask);
-            }
-        }
-
-        public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors {
-            public final IgnitionMonitor EGROrVVT;
-            public final IgnitionMonitor PMFilter;
-            public final IgnitionMonitor exhaustGasSensor;
-            public final IgnitionMonitor boostPressure;
-            public final IgnitionMonitor NOxSCR;
-            public final IgnitionMonitor NMHCCatalyst;
-
-            static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6;
-            static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7;
-
-            static final int PM_FILTER_AVAILABLE = 0x1 << 8;
-            static final int PM_FILTER_INCOMPLETE = 0x1 << 9;
-
-            static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10;
-            static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11;
-
-            static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12;
-            static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13;
-
-            static final int NOx_SCR_AVAILABLE = 0x1 << 14;
-            static final int NOx_SCR_INCOMPLETE = 0x1 << 15;
-
-            static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
-            static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
-
-            static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER =
-                    new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder PM_FILTER_DECODER =
-                    new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER =
-                    new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE,
-                            EXHAUST_GAS_SENSOR_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER =
-                    new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE,
-                            BOOST_PRESSURE_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder NOx_SCR_DECODER =
-                    new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE);
-
-            static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER =
-                    new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE);
-
-            CompressionIgnitionMonitors(int bitmask) {
-                super(bitmask);
-                EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask);
-                PMFilter = PM_FILTER_DECODER.fromValue(bitmask);
-                exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask);
-                boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask);
-                NOxSCR = NOx_SCR_DECODER.fromValue(bitmask);
-                NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask);
-            }
-        }
-    }
-
-    public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
-        return getSystemIntegerSensor(
-                CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS);
-    }
-
-    public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
-        return getSystemIntegerSensor(
-                CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
-    }
-
-    public @Nullable IgnitionMonitors.CommonIgnitionMonitors getIgnitionMonitors() {
-        Integer ignitionMonitorsType =
-                getSystemIntegerSensor(
-                        CarDiagnosticSensorIndices.Obd2IntegerSensorIndex
-                                .IGNITION_MONITORS_SUPPORTED);
-        Integer ignitionMonitorsBitmask =
-                getSystemIntegerSensor(
-                        CarDiagnosticSensorIndices.Obd2IntegerSensorIndex
-                                .IGNITION_SPECIFIC_MONITORS);
-        if (null == ignitionMonitorsType) return null;
-        if (null == ignitionMonitorsBitmask) return null;
-        switch (ignitionMonitorsType) {
-            case 0:
-                return new IgnitionMonitors.SparkIgnitionMonitors(ignitionMonitorsBitmask);
-            case 1:
-                return new IgnitionMonitors.CompressionIgnitionMonitors(ignitionMonitorsBitmask);
-            default:
-                return null;
-        }
-    }
-
-    public @Nullable @FuelType.Type Integer getFuelType() {
-        return getSystemIntegerSensor(CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_TYPE);
-    }
-}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticSensorIndices.java b/car-lib/src/android/car/hardware/CarDiagnosticSensorIndices.java
deleted file mode 100644
index d19b232..0000000
--- a/car-lib/src/android/car/hardware/CarDiagnosticSensorIndices.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-package android.car.hardware;
-
-import android.annotation.IntDef;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This class is a container for the indices of integer and float diagnostic sensors.
- * These values are extracted from types.hal by packages/services/Car/tools/update-obd2-sensors.py
- *
- * DO NOT EDIT MANUALLY
- *
- * @hide
- */
-public final class CarDiagnosticSensorIndices {
-    public static final class Obd2IntegerSensorIndex {
-        private Obd2IntegerSensorIndex() {}
-
-        public static final int FUEL_SYSTEM_STATUS = 0;
-        public static final int MALFUNCTION_INDICATOR_LIGHT_ON = 1;
-        public static final int IGNITION_MONITORS_SUPPORTED = 2;
-        public static final int IGNITION_SPECIFIC_MONITORS = 3;
-        public static final int INTAKE_AIR_TEMPERATURE = 4;
-        public static final int COMMANDED_SECONDARY_AIR_STATUS = 5;
-        public static final int NUM_OXYGEN_SENSORS_PRESENT = 6;
-        public static final int RUNTIME_SINCE_ENGINE_START = 7;
-        public static final int DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8;
-        public static final int WARMUPS_SINCE_CODES_CLEARED = 9;
-        public static final int DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10;
-        public static final int ABSOLUTE_BAROMETRIC_PRESSURE = 11;
-        public static final int CONTROL_MODULE_VOLTAGE = 12;
-        public static final int AMBIENT_AIR_TEMPERATURE = 13;
-        public static final int TIME_WITH_MALFUNCTION_LIGHT_ON = 14;
-        public static final int TIME_SINCE_TROUBLE_CODES_CLEARED = 15;
-        public static final int MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16;
-        public static final int MAX_OXYGEN_SENSOR_VOLTAGE = 17;
-        public static final int MAX_OXYGEN_SENSOR_CURRENT = 18;
-        public static final int MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19;
-        public static final int MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20;
-        public static final int FUEL_TYPE = 21;
-        public static final int FUEL_RAIL_ABSOLUTE_PRESSURE = 22;
-        public static final int ENGINE_OIL_TEMPERATURE = 23;
-        public static final int DRIVER_DEMAND_PERCENT_TORQUE = 24;
-        public static final int ENGINE_ACTUAL_PERCENT_TORQUE = 25;
-        public static final int ENGINE_REFERENCE_PERCENT_TORQUE = 26;
-        public static final int ENGINE_PERCENT_TORQUE_DATA_IDLE = 27;
-        public static final int ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28;
-        public static final int ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29;
-        public static final int ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30;
-        public static final int ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31;
-        public static final int LAST_SYSTEM = ENGINE_PERCENT_TORQUE_DATA_POINT4;
-        public static final int VENDOR_START = LAST_SYSTEM + 1;
-    }
-
-    public static final class Obd2FloatSensorIndex {
-        private Obd2FloatSensorIndex() {}
-
-        public static final int CALCULATED_ENGINE_LOAD = 0;
-        public static final int ENGINE_COOLANT_TEMPERATURE = 1;
-        public static final int SHORT_TERM_FUEL_TRIM_BANK1 = 2;
-        public static final int LONG_TERM_FUEL_TRIM_BANK1 = 3;
-        public static final int SHORT_TERM_FUEL_TRIM_BANK2 = 4;
-        public static final int LONG_TERM_FUEL_TRIM_BANK2 = 5;
-        public static final int FUEL_PRESSURE = 6;
-        public static final int INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7;
-        public static final int ENGINE_RPM = 8;
-        public static final int VEHICLE_SPEED = 9;
-        public static final int TIMING_ADVANCE = 10;
-        public static final int MAF_AIR_FLOW_RATE = 11;
-        public static final int THROTTLE_POSITION = 12;
-        public static final int OXYGEN_SENSOR1_VOLTAGE = 13;
-        public static final int OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14;
-        public static final int OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15;
-        public static final int OXYGEN_SENSOR2_VOLTAGE = 16;
-        public static final int OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17;
-        public static final int OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18;
-        public static final int OXYGEN_SENSOR3_VOLTAGE = 19;
-        public static final int OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20;
-        public static final int OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21;
-        public static final int OXYGEN_SENSOR4_VOLTAGE = 22;
-        public static final int OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23;
-        public static final int OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24;
-        public static final int OXYGEN_SENSOR5_VOLTAGE = 25;
-        public static final int OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26;
-        public static final int OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27;
-        public static final int OXYGEN_SENSOR6_VOLTAGE = 28;
-        public static final int OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29;
-        public static final int OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30;
-        public static final int OXYGEN_SENSOR7_VOLTAGE = 31;
-        public static final int OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32;
-        public static final int OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33;
-        public static final int OXYGEN_SENSOR8_VOLTAGE = 34;
-        public static final int OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35;
-        public static final int OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36;
-        public static final int FUEL_RAIL_PRESSURE = 37;
-        public static final int FUEL_RAIL_GAUGE_PRESSURE = 38;
-        public static final int COMMANDED_EXHAUST_GAS_RECIRCULATION = 39;
-        public static final int EXHAUST_GAS_RECIRCULATION_ERROR = 40;
-        public static final int COMMANDED_EVAPORATIVE_PURGE = 41;
-        public static final int FUEL_TANK_LEVEL_INPUT = 42;
-        public static final int EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43;
-        public static final int CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44;
-        public static final int CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45;
-        public static final int CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46;
-        public static final int CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47;
-        public static final int ABSOLUTE_LOAD_VALUE = 48;
-        public static final int FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49;
-        public static final int RELATIVE_THROTTLE_POSITION = 50;
-        public static final int ABSOLUTE_THROTTLE_POSITION_B = 51;
-        public static final int ABSOLUTE_THROTTLE_POSITION_C = 52;
-        public static final int ACCELERATOR_PEDAL_POSITION_D = 53;
-        public static final int ACCELERATOR_PEDAL_POSITION_E = 54;
-        public static final int ACCELERATOR_PEDAL_POSITION_F = 55;
-        public static final int COMMANDED_THROTTLE_ACTUATOR = 56;
-        public static final int ETHANOL_FUEL_PERCENTAGE = 57;
-        public static final int ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58;
-        public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59;
-        public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60;
-        public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61;
-        public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62;
-        public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63;
-        public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64;
-        public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65;
-        public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66;
-        public static final int RELATIVE_ACCELERATOR_PEDAL_POSITION = 67;
-        public static final int HYBRID_BATTERY_PACK_REMAINING_LIFE = 68;
-        public static final int FUEL_INJECTION_TIMING = 69;
-        public static final int ENGINE_FUEL_RATE = 70;
-        public static final int LAST_SYSTEM = ENGINE_FUEL_RATE;
-        public static final int VENDOR_START = LAST_SYSTEM + 1;
-    }
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-        Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS,
-        Obd2IntegerSensorIndex.MALFUNCTION_INDICATOR_LIGHT_ON,
-        Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED,
-        Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS,
-        Obd2IntegerSensorIndex.INTAKE_AIR_TEMPERATURE,
-        Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
-        Obd2IntegerSensorIndex.NUM_OXYGEN_SENSORS_PRESENT,
-        Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START,
-        Obd2IntegerSensorIndex.DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON,
-        Obd2IntegerSensorIndex.WARMUPS_SINCE_CODES_CLEARED,
-        Obd2IntegerSensorIndex.DISTANCE_TRAVELED_SINCE_CODES_CLEARED,
-        Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE,
-        Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE,
-        Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE,
-        Obd2IntegerSensorIndex.TIME_WITH_MALFUNCTION_LIGHT_ON,
-        Obd2IntegerSensorIndex.TIME_SINCE_TROUBLE_CODES_CLEARED,
-        Obd2IntegerSensorIndex.MAX_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2IntegerSensorIndex.MAX_OXYGEN_SENSOR_VOLTAGE,
-        Obd2IntegerSensorIndex.MAX_OXYGEN_SENSOR_CURRENT,
-        Obd2IntegerSensorIndex.MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
-        Obd2IntegerSensorIndex.MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR,
-        Obd2IntegerSensorIndex.FUEL_TYPE,
-        Obd2IntegerSensorIndex.FUEL_RAIL_ABSOLUTE_PRESSURE,
-        Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE,
-        Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE,
-        Obd2IntegerSensorIndex.ENGINE_ACTUAL_PERCENT_TORQUE,
-        Obd2IntegerSensorIndex.ENGINE_REFERENCE_PERCENT_TORQUE,
-        Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_IDLE,
-        Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT1,
-        Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT2,
-        Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT3,
-        Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT4,
-        Obd2IntegerSensorIndex.LAST_SYSTEM,
-        Obd2IntegerSensorIndex.VENDOR_START,
-    })
-    public @interface IntegerSensorIndex {}
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-        Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD,
-        Obd2FloatSensorIndex.ENGINE_COOLANT_TEMPERATURE,
-        Obd2FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK1,
-        Obd2FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK1,
-        Obd2FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK2,
-        Obd2FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK2,
-        Obd2FloatSensorIndex.FUEL_PRESSURE,
-        Obd2FloatSensorIndex.INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
-        Obd2FloatSensorIndex.ENGINE_RPM,
-        Obd2FloatSensorIndex.VEHICLE_SPEED,
-        Obd2FloatSensorIndex.TIMING_ADVANCE,
-        Obd2FloatSensorIndex.MAF_AIR_FLOW_RATE,
-        Obd2FloatSensorIndex.THROTTLE_POSITION,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR2_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR3_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR4_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR7_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR8_VOLTAGE,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM,
-        Obd2FloatSensorIndex.OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.FUEL_RAIL_PRESSURE,
-        Obd2FloatSensorIndex.FUEL_RAIL_GAUGE_PRESSURE,
-        Obd2FloatSensorIndex.COMMANDED_EXHAUST_GAS_RECIRCULATION,
-        Obd2FloatSensorIndex.EXHAUST_GAS_RECIRCULATION_ERROR,
-        Obd2FloatSensorIndex.COMMANDED_EVAPORATIVE_PURGE,
-        Obd2FloatSensorIndex.FUEL_TANK_LEVEL_INPUT,
-        Obd2FloatSensorIndex.EVAPORATION_SYSTEM_VAPOR_PRESSURE,
-        Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR1,
-        Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR1,
-        Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR2,
-        Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR2,
-        Obd2FloatSensorIndex.ABSOLUTE_LOAD_VALUE,
-        Obd2FloatSensorIndex.FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO,
-        Obd2FloatSensorIndex.RELATIVE_THROTTLE_POSITION,
-        Obd2FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_B,
-        Obd2FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_C,
-        Obd2FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_D,
-        Obd2FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_E,
-        Obd2FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_F,
-        Obd2FloatSensorIndex.COMMANDED_THROTTLE_ACTUATOR,
-        Obd2FloatSensorIndex.ETHANOL_FUEL_PERCENTAGE,
-        Obd2FloatSensorIndex.ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE,
-        Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
-        Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
-        Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
-        Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
-        Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
-        Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
-        Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
-        Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
-        Obd2FloatSensorIndex.RELATIVE_ACCELERATOR_PEDAL_POSITION,
-        Obd2FloatSensorIndex.HYBRID_BATTERY_PACK_REMAINING_LIFE,
-        Obd2FloatSensorIndex.FUEL_INJECTION_TIMING,
-        Obd2FloatSensorIndex.ENGINE_FUEL_RATE,
-        Obd2FloatSensorIndex.LAST_SYSTEM,
-        Obd2FloatSensorIndex.VENDOR_START,
-    })
-    public @interface FloatSensorIndex {}
-
-}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl b/car-lib/src/android/car/hardware/CarSensorConfig.aidl
similarity index 95%
rename from car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
rename to car-lib/src/android/car/hardware/CarSensorConfig.aidl
index 73b184e..480f751 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
+++ b/car-lib/src/android/car/hardware/CarSensorConfig.aidl
@@ -16,4 +16,4 @@
 
 package android.car.hardware;
 
-parcelable CarDiagnosticEvent;
+parcelable CarSensorConfig;
diff --git a/car-lib/src/android/car/hardware/CarSensorConfig.java b/car-lib/src/android/car/hardware/CarSensorConfig.java
new file mode 100644
index 0000000..8ba456b
--- /dev/null
+++ b/car-lib/src/android/car/hardware/CarSensorConfig.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.hardware;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.ArrayList;
+
+/**
+ * A CarSensorConfig object corresponds to a single sensor type coming from the car.
+ * @hide
+ */
+public class CarSensorConfig implements Parcelable {
+    /** List of property specific mapped elements in bundle for WHEEL_TICK_DISTANCE sensor*/
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS =
+        "android.car.wheelTickDistanceSupportedWhheels";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK =
+        "android.car.wheelTickDistanceFrontLeftUmPerTick";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK =
+        "android.car.wheelTickDistanceFrontRightUmPerTick";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK =
+        "android.car.wheelTickDistanceRearRightUmPerTick";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK =
+        "android.car.wheelTickDistanceRearLeftUmPerTick";
+
+    /** Config data stored in Bundle */
+    private final Bundle mConfig;
+    private final int mType;
+
+    /** @hide */
+    public CarSensorConfig(Parcel in) {
+        mType = in.readInt();
+        mConfig = in.readBundle();
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeBundle(mConfig);
+    }
+
+    /** @hide */
+    public static final Parcelable.Creator<CarSensorConfig> CREATOR
+    = new Parcelable.Creator<CarSensorConfig>() {
+        public CarSensorConfig createFromParcel(Parcel in) {
+            return new CarSensorConfig(in);
+        }
+
+        public CarSensorConfig[] newArray(int size) {
+            return new CarSensorConfig[size];
+        }
+    };
+
+    /** @hide */
+    public CarSensorConfig(int type, Bundle b) {
+        mType = type;
+        mConfig = b.deepCopy();
+    }
+
+    private void checkType(int type) {
+        if (mType == type) {
+            return;
+        }
+        throw new UnsupportedOperationException(String.format(
+                "Invalid sensor type: expected %d, got %d", type, mType));
+    }
+
+    /** @hide */
+    public Bundle getBundle() {
+        return mConfig;
+    }
+
+    /** @hide */
+    public int getInt(String key) {
+        if (mConfig.containsKey(key)) {
+            return mConfig.getInt(key);
+        } else {
+            throw new IllegalArgumentException("SensorType " + mType +
+                " does not contain key: " + key);
+        }
+    }
+
+    /** @hide */
+    public int getType() {
+        return mType;
+    }
+
+    /** @hide */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getName() + "[");
+        sb.append("mType: " + mType);
+        sb.append("mConfig: " + mConfig.toString());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/car-lib/src/android/car/hardware/CarSensorEvent.java b/car-lib/src/android/car/hardware/CarSensorEvent.java
index 680b9ab..52f363d 100644
--- a/car-lib/src/android/car/hardware/CarSensorEvent.java
+++ b/car-lib/src/android/car/hardware/CarSensorEvent.java
@@ -137,6 +137,17 @@
      * Pressure in kPa.
      */
     public static final int INDEX_ENVIRONMENT_PRESSURE = 1;
+    /**
+     * Index for {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE} in longValues. RESET_COUNT
+     * is incremented whenever the HAL detects that a sensor reset has occurred.  It represents to
+     * the upper layer that the WHEEL_DISTANCE values will not be contiguous with other values
+     * reported with a different RESET_COUNT.
+     */
+    public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0;
+    public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
+    public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
+    public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
+    public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
 
     private static final long MILLI_IN_NANOS = 1000000L;
 
@@ -144,8 +155,8 @@
     public int sensorType;
 
     /**
-     * When this data was acquired in car or received from car. It is elapsed real-time of data
-     * reception from car in nanoseconds since system boot.
+     * When this data was received from car. It is elapsed real-time of data reception from car in
+     * nanoseconds since system boot.
      */
     public long timestamp;
     /**
@@ -154,6 +165,8 @@
     public final float[] floatValues;
     /** array holding int type of sensor data */
     public final int[] intValues;
+    /** array holding long int type of sensor data */
+    public final long[] longValues;
 
     /** @hide */
     public CarSensorEvent(Parcel in) {
@@ -166,6 +179,9 @@
         intValues = new int[len];
         in.readIntArray(intValues);
         // version 1 up to here
+        len = in.readInt();
+        longValues = new long[len];
+        in.readLongArray(longValues);
     }
 
     @Override
@@ -181,6 +197,8 @@
         dest.writeFloatArray(floatValues);
         dest.writeInt(intValues.length);
         dest.writeIntArray(intValues);
+        dest.writeInt(longValues.length);
+        dest.writeLongArray(longValues);
     }
 
     public static final Parcelable.Creator<CarSensorEvent> CREATOR
@@ -195,19 +213,23 @@
     };
 
     /** @hide */
-    public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize) {
+    public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize,
+                          int longValueSize) {
         this.sensorType = sensorType;
         this.timestamp = timestamp;
         floatValues = new float[floatValueSize];
         intValues = new int[intValueSize];
+        longValues = new long[longValueSize];
     }
 
     /** @hide */
-    CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues) {
+    CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues,
+                   long[] longValues) {
         this.sensorType = sensorType;
         this.timestamp = timestamp;
         this.floatValues = floatValues;
         this.intValues = intValues;
+        this.longValues = longValues;
     }
 
     private void checkType(int type) {
@@ -493,6 +515,100 @@
     }
 
     /** @hide */
+    public static class CarWheelTickDistanceData {
+        public long timestamp;
+        public long sensorResetCount;
+        public long frontLeftWheelDistanceMm;
+        public long frontRightWheelDistanceMm;
+        public long rearRightWheelDistanceMm;
+        public long rearLeftWheelDistanceMm;
+
+        /** @hide */
+        private CarWheelTickDistanceData() {};
+    }
+
+    /**
+     * Convenience method for obtaining a {@link CarWheelTickDistanceData} object from a
+     * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE}.
+     *
+     * @param data an optional output parameter which, if non-null, will be used by this method
+     *     instead of a newly created object.
+     * @return CarWheelTickDistanceData object corresponding to data contained in the CarSensorEvent
+     * @hide
+     */
+    public CarWheelTickDistanceData getCarWheelTickDistanceData(CarWheelTickDistanceData data) {
+        checkType(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
+        if (data == null) {
+            data = new CarWheelTickDistanceData();
+        }
+        data.timestamp = timestamp;
+        data.sensorResetCount = longValues[INDEX_WHEEL_DISTANCE_RESET_COUNT];
+        data.frontLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_LEFT];
+        data.frontRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_RIGHT];
+        data.rearRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_RIGHT];
+        data.rearLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_LEFT];
+        return data;
+    }
+
+    /** @hide */
+    public static class CarAbsActiveData {
+        public long timestamp;
+        public boolean absIsActive;
+
+        /** @hide */
+        private CarAbsActiveData() {};
+    }
+
+    /**
+     * Convenience method for obtaining a {@link CarAbsActiveData} object from a CarSensorEvent
+     * object with type {@link CarSensorManager#SENSOR_TYPE_ABS_ACTIVE}.
+     *
+     * @param data an optional output parameter which, if non-null, will be used by this method
+     *     instead of a newly created object.
+     * @return a CarAbsActiveData object corresponding to data contained in the CarSensorEvent.
+     * @hide
+     */
+    public CarAbsActiveData getCarAbsActiveData(CarAbsActiveData data) {
+        checkType(CarSensorManager.SENSOR_TYPE_ABS_ACTIVE);
+        if (data == null) {
+            data = new CarAbsActiveData();
+        }
+        data.timestamp = timestamp;
+        data.absIsActive = intValues[0] == 1;
+        return data;
+    }
+
+    /** @hide */
+    public static class CarTractionControlActiveData {
+        public long timestamp;
+        public boolean tractionControlIsActive;
+
+        /** @hide */
+        private CarTractionControlActiveData() {};
+    }
+
+    /**
+     * Convenience method for obtaining a {@link CarTractionControlActiveData} object from a
+     * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_TRACTION_CONTROL_ACTIVE}.
+     *
+     * @param data an optional output parameter which, if non-null, will be used by this method
+     *     instead of a newly created object.
+     * @return a CarTractionControlActiveData object corresponding to data contained in the
+     *     CarSensorEvent.
+     * @hide
+     */
+    public CarTractionControlActiveData getCarTractionControlActiveData(
+            CarTractionControlActiveData data) {
+        checkType(CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE);
+        if (data == null) {
+            data = new CarTractionControlActiveData();
+        }
+        data.timestamp = timestamp;
+        data.tractionControlIsActive = intValues[0] == 1;
+        return data;
+    }
+
+    /** @hide */
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -510,6 +626,12 @@
                 sb.append(" " + v);
             }
         }
+        if (longValues != null && longValues.length > 0) {
+            sb.append(" long values:");
+            for (long v: longValues) {
+                sb.append(" " + v);
+            }
+        }
         sb.append("]");
         return sb.toString();
     }
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index 4fbc2cd..e2bce11 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -25,6 +25,7 @@
 import android.car.CarManagerBase;
 import android.car.CarNotConnectedException;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -48,22 +49,22 @@
  */
 public final class CarSensorManager implements CarManagerBase {
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED1           = 1;
+    public static final int SENSOR_TYPE_RESERVED1                   = 1;
     /**
      * This sensor represents vehicle speed in m/s.
      * Sensor data in {@link CarSensorEvent} is a float which will be >= 0.
      * This requires {@link Car#PERMISSION_SPEED} permission.
      */
-    public static final int SENSOR_TYPE_CAR_SPEED         = 2;
+    public static final int SENSOR_TYPE_CAR_SPEED                   = 2;
     /**
      * Represents engine RPM of the car. Sensor data in {@link CarSensorEvent} is a float.
      */
-    public static final int SENSOR_TYPE_RPM               = 3;
+    public static final int SENSOR_TYPE_RPM                         = 3;
     /**
      * Total travel distance of the car in Kilometer. Sensor data is a float.
      * This requires {@link Car#PERMISSION_MILEAGE} permission.
      */
-    public static final int SENSOR_TYPE_ODOMETER          = 4;
+    public static final int SENSOR_TYPE_ODOMETER                    = 4;
     /**
      * Indicates fuel level of the car.
      * In {@link CarSensorEvent}, floatValues[{@link CarSensorEvent#INDEX_FUEL_LEVEL_IN_PERCENTILE}]
@@ -74,67 +75,84 @@
      * condition.
      * This requires {@link Car#PERMISSION_FUEL} permission.
      */
-    public static final int SENSOR_TYPE_FUEL_LEVEL        = 5;
+    public static final int SENSOR_TYPE_FUEL_LEVEL                  = 5;
     /**
      * Represents the current status of parking brake. Sensor data in {@link CarSensorEvent} is an
      * intValues[0]. Value of 1 represents parking brake applied while 0 means the other way
      * around. For this sensor, rate in {@link #registerListener(OnSensorChangedListener, int, int)}
      * will be ignored and all changes will be notified.
      */
-    public static final int SENSOR_TYPE_PARKING_BRAKE     = 6;
+    public static final int SENSOR_TYPE_PARKING_BRAKE               = 6;
     /**
      * This represents the current position of transmission gear. Sensor data in
      * {@link CarSensorEvent} is an intValues[0]. For the meaning of the value, check
      * {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
      */
-    public static final int SENSOR_TYPE_GEAR              = 7;
+    public static final int SENSOR_TYPE_GEAR                        = 7;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED8         = 8;
+    public static final int SENSOR_TYPE_RESERVED8                   = 8;
     /**
      * Day/night sensor. Sensor data is intValues[0].
      */
-    public static final int SENSOR_TYPE_NIGHT             = 9;
+    public static final int SENSOR_TYPE_NIGHT                       = 9;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED10        = 10;
+    public static final int SENSOR_TYPE_RESERVED10                  = 10;
     /**
      * Represents the current driving status of car. Different user interaction should be used
      * depending on the current driving status. Driving status is intValues[0].
      */
-    public static final int SENSOR_TYPE_DRIVING_STATUS    = 11;
+    public static final int SENSOR_TYPE_DRIVING_STATUS              = 11;
     /**
      * Environment like temperature and pressure.
      */
-    public static final int SENSOR_TYPE_ENVIRONMENT       = 12;
+    public static final int SENSOR_TYPE_ENVIRONMENT                 = 12;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED13        = 13;
+    public static final int SENSOR_TYPE_RESERVED13                  = 13;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED14        = 14;
+    public static final int SENSOR_TYPE_RESERVED14                  = 14;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED15        = 15;
+    public static final int SENSOR_TYPE_RESERVED15                  = 15;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED16        = 16;
+    public static final int SENSOR_TYPE_RESERVED16                  = 16;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED17        = 17;
+    public static final int SENSOR_TYPE_RESERVED17                  = 17;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED18        = 18;
+    public static final int SENSOR_TYPE_RESERVED18                  = 18;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED19        = 19;
+    public static final int SENSOR_TYPE_RESERVED19                  = 19;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED20        = 20;
+    public static final int SENSOR_TYPE_RESERVED20                  = 20;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED21        = 21;
-
+    public static final int SENSOR_TYPE_RESERVED21                  = 21;
     /**
      * Represents ignition state. The value should be one of the constants that starts with
      * IGNITION_STATE_* in {@link CarSensorEvent}.
      */
-    public static final int SENSOR_TYPE_IGNITION_STATE    = 22;
+    public static final int SENSOR_TYPE_IGNITION_STATE              = 22;
+    /**
+     * Represents wheel distance in millimeters.  Some cars may not have individual sensors on each
+     * wheel.  If a value is not available, Long.MAX_VALUE will be reported.  The wheel distance
+     * accumulates over time.  It increments on forward movement, and decrements on reverse.  Wheel
+     * distance shall be reset to zero each time a vehicle is started by the user.
+     * This requires {@link Car#PERMISSION_SPEED} permission.
+     */
+    public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE         = 23;
+    /**
+     * Set to true when ABS is active.  This sensor is event driven.
+     * This requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+     */
+    public static final int SENSOR_TYPE_ABS_ACTIVE                  = 24;
+    /**
+     * Set to true when traction control is active.  This sensor is event driven.
+     * This requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+     */
+    public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE     = 25;
 
     /**
      * Sensor type bigger than this is invalid. Always update this after adding a new sensor.
      * @hide
      */
-    private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_IGNITION_STATE;
+    private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_TRACTION_CONTROL_ACTIVE;
 
     /**
      * Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
@@ -162,6 +180,9 @@
         SENSOR_TYPE_DRIVING_STATUS,
         SENSOR_TYPE_ENVIRONMENT,
         SENSOR_TYPE_IGNITION_STATE,
+        SENSOR_TYPE_WHEEL_TICK_DISTANCE,
+        SENSOR_TYPE_ABS_ACTIVE,
+        SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SensorType {}
@@ -291,9 +312,11 @@
      * If the same listener is registered again for the same sensor, it will be either ignored or
      * updated depending on the rate.
      * <p>
-     * Requires {@link Car#PERMISSION_SPEED} for {@link #SENSOR_TYPE_CAR_SPEED},
-     *  {@link Car#PERMISSION_MILEAGE} for {@link #SENSOR_TYPE_ODOMETER},
-     *  or {@link Car#PERMISSION_FUEL} for {@link #SENSOR_TYPE_FUEL_LEVEL}.
+     * Requires {@link Car#PERMISSION_SPEED} for {@link #SENSOR_TYPE_CAR_SPEED} and
+     *  {@link #SENSOR_TYPE_WHEEL_TICK_DISTANCE}, {@link Car#PERMISSION_MILEAGE} for
+     *  {@link #SENSOR_TYPE_ODOMETER}, {@link Car#PERMISSION_FUEL} for
+     *  {@link #SENSOR_TYPE_FUEL_LEVEL}, or {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} for
+     *  {@link #SENSOR_TYPE_ABS_ACTIVE} and {@link #SENSOR_TYPE_TRACTION_CONTROL_ACTIVE}
      *
      * @param listener
      * @param sensorType sensor type to subscribe.
@@ -310,7 +333,8 @@
      * @throws SecurityException if missing the appropriate permission
      */
     @RequiresPermission(anyOf={Manifest.permission.ACCESS_FINE_LOCATION, Car.PERMISSION_SPEED,
-            Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL}, conditional=true)
+            Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL, Car.PERMISSION_VEHICLE_DYNAMICS_STATE},
+            conditional=true)
     public boolean registerListener(OnSensorChangedListener listener, @SensorType int sensorType,
             @SensorRate int rate) throws CarNotConnectedException, IllegalArgumentException {
         assertSensorType(sensorType);
@@ -490,4 +514,28 @@
             });
         }
     }
+
+    /**
+     * Get the config data for the given type.
+     *
+     * A CarSensorConfig object is returned for every sensor type.  However, if there is no
+     * config, the data will be empty.
+     *
+     * @param sensor type to request
+     * @return CarSensorConfig object
+     * @throws CarNotConnectedException if the connection to the car service has been lost.
+     * @hide
+     */
+    public CarSensorConfig getSensorConfig(@SensorType int type)
+        throws CarNotConnectedException {
+        assertSensorType(type);
+        try {
+            return mService.getSensorConfig(type);
+        } catch (IllegalStateException e) {
+            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+        } catch(RemoteException e) {
+            handleCarServiceRemoteExceptionAndThrow(e);
+        }
+        return new CarSensorConfig(0, Bundle.EMPTY);
+    }
 }
diff --git a/car-lib/src/android/car/hardware/ICarSensor.aidl b/car-lib/src/android/car/hardware/ICarSensor.aidl
index 6f9e315..e943e0f 100644
--- a/car-lib/src/android/car/hardware/ICarSensor.aidl
+++ b/car-lib/src/android/car/hardware/ICarSensor.aidl
@@ -16,6 +16,7 @@
 
 package android.car.hardware;
 
+import android.car.hardware.CarSensorConfig;
 import android.car.hardware.CarSensorEvent;
 import android.car.hardware.ICarSensorEventListener;
 
@@ -44,4 +45,9 @@
      * be affected.
      */
     void unregisterSensorListener(int sensorType, in ICarSensorEventListener callback) = 3;
+
+    /**
+     * get config flags and config array for the sensor type
+     */
+    CarSensorConfig getSensorConfig(int sensorType) = 4;
 }
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
index bebdd99..a2f6e6d 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
+++ b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
@@ -161,6 +161,11 @@
      */
     public static final int ID_ZONED_MAX_DEFROST_ON = 0x400E;
     /**
+     * Automatic recirculation mode ON
+     * true indicates recirculation is in automatic mode
+     */
+    public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 0x400F;
+    /**
      * Defroster ON, bool type
      * Defroster controls are based on window position.
      * True indicates the defroster is ON.
@@ -187,6 +192,7 @@
             ID_ZONED_DUAL_ZONE_ON,
             ID_ZONED_MAX_DEFROST_ON,
             ID_ZONED_HVAC_POWER_ON,
+            ID_ZONED_HVAC_AUTO_RECIRC_ON,
             ID_WINDOW_DEFROSTER_ON,
     })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index cf7ce1f..49a7ca4 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -36,7 +36,7 @@
 import java.lang.ref.WeakReference;
 
 /**
- * APIs for handling car specific audio stuffs.
+ * APIs for handling car specific audio stuff.
  */
 public final class CarAudioManager implements CarManagerBase {
 
@@ -81,10 +81,14 @@
      */
     public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9;
     /**
+     * Audio usage for the ringing of a phone call.
+     */
+    public static final int CAR_AUDIO_USAGE_RINGTONE = 10;
+    /**
      * Audio usage for external audio usage.
      * @hide
      */
-    public static final int CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE = 10;
+    public static final int CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE = 11;
 
     /** @hide */
     public static final int CAR_AUDIO_USAGE_MAX = CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
diff --git a/car-lib/src/android/car/media/ICarAudio.aidl b/car-lib/src/android/car/media/ICarAudio.aidl
index f43a378..6d9b3ce 100644
--- a/car-lib/src/android/car/media/ICarAudio.aidl
+++ b/car-lib/src/android/car/media/ICarAudio.aidl
@@ -22,7 +22,7 @@
 
 /**
  * Binder interface for {@link android.car.media.CarAudioManager}.
- * Check {@link android.car.media.CarAudioManager} APIs for expected behavior of each calls.
+ * Check {@link android.car.media.CarAudioManager} APIs for expected behavior of each call.
  *
  * @hide
  */
diff --git a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
index fe48504..cfa05f8 100644
--- a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
+++ b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
@@ -17,9 +17,9 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -46,13 +46,15 @@
     private int mMinIntervalMillis;
 
     @ClusterType
-    private int mType;
+    private final int mType;
 
-    private int mImageWidth;
+    private final int mImageWidth;
 
-    private int mImageHeight;
+    private final int mImageHeight;
 
-    private int mImageColorDepthBits;
+    private final int mImageColorDepthBits;
+
+    private final Bundle mExtra;
 
     public static final Parcelable.Creator<CarNavigationInstrumentCluster> CREATOR
             = new Parcelable.Creator<CarNavigationInstrumentCluster>() {
@@ -102,6 +104,12 @@
     }
 
     /**
+     * Contains extra information about instrument cluster.
+     * @hide
+     */
+    public Bundle getExtra() { return mExtra; }
+
+    /**
      * If instrument cluster is image, number of bits of colour depth it supports (8, 16, or 32).
      */
     public int getImageColorDepthBits() {
@@ -130,11 +138,12 @@
             int imageWidth,
             int imageHeight,
             int imageColorDepthBits) {
-        this.mMinIntervalMillis = minIntervalMillis;
-        this.mType = type;
-        this.mImageWidth = imageWidth;
-        this.mImageHeight = imageHeight;
-        this.mImageColorDepthBits = imageColorDepthBits;
+        mMinIntervalMillis = minIntervalMillis;
+        mType = type;
+        mImageWidth = imageWidth;
+        mImageHeight = imageHeight;
+        mImageColorDepthBits = imageColorDepthBits;
+        mExtra = new Bundle();
     }
 
     @Override
@@ -149,6 +158,7 @@
         dest.writeInt(mImageWidth);
         dest.writeInt(mImageHeight);
         dest.writeInt(mImageColorDepthBits);
+        dest.writeBundle(mExtra);
     }
 
     private CarNavigationInstrumentCluster(Parcel in) {
@@ -157,6 +167,7 @@
         mImageWidth = in.readInt();
         mImageHeight = in.readInt();
         mImageColorDepthBits = in.readInt();
+        mExtra = in.readBundle(getClass().getClassLoader());
     }
 
     /** Converts to string for debug purpose */
@@ -167,6 +178,7 @@
                 "type: " + mType + ", " +
                 "imageWidth: " + mImageWidth + ", " +
                 "imageHeight: " + mImageHeight + ", " +
-                "imageColourDepthBits: " + mImageColorDepthBits + " }";
+                "imageColourDepthBits: " + mImageColorDepthBits +
+                "extra: " + mExtra + " }";
     }
 }
diff --git a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
index f24ff30..e15a17d 100644
--- a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
+++ b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
@@ -22,10 +22,10 @@
 import android.car.CarNotConnectedException;
 import android.car.cluster.renderer.IInstrumentClusterNavigation;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -114,6 +114,22 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TurnEvent {}
 
+    /**
+     * Event type that holds information about next maneuver.
+     * @hide
+     */
+    public static final int EVENT_TYPE_NEXT_MANEUVER_INFO = 1;
+    /**
+     * Event type that holds information regarding distance/time to the next maneuver.
+     * @hide
+     */
+    public static final int EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN = 2;
+    /**
+     * All custom (vendor-specific) event types should be equal or greater than this constant.
+     * @hide
+     */
+    public static final int EVENT_TYPE_VENDOR_FIRST = 1024;
+
     /* Turn Side */
     /** Turn is on the left side of the vehicle. */
     public static final int TURN_SIDE_LEFT = 1;
@@ -198,7 +214,7 @@
      * drives on the left-hand side of the road, such as Australia; anti-clockwise for roads where
      * the car drives on the right, such as the USA).
      *
-     * @param event event type ({@link #TURN_TURN}, {@link #TURN_U_TURN},
+     * @param turnEvent turn event like ({@link #TURN_TURN}, {@link #TURN_U_TURN},
      *        {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}, etc).
      * @param eventName Name of the turn event like road name to turn. For example "Charleston road"
      *        in "Turn right to Charleston road"
@@ -212,6 +228,7 @@
      *        {@link #TURN_SIDE_UNSPECIFIED}).
      * @throws CarNotConnectedException if the connection to the car service has been lost.
      *
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public void sendNavigationTurnEvent(@TurnEvent int turnEvent, CharSequence eventName,
             int turnAngle, int turnNumber, Bitmap image, @TurnSide int turnSide)
@@ -238,6 +255,8 @@
      * @param displayDistanceUnit units for {@param displayDistanceMillis} param.
      * See {@link DistanceUnit} for acceptable values.
      * @throws CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public void sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds,
             int displayDistanceMillis, @DistanceUnit int displayDistanceUnit)
@@ -252,6 +271,28 @@
         }
     }
 
+    /**
+     * Sends events from navigation app to instrument cluster.
+     *
+     * @param eventType event type
+     * @param bundle object that holds data about the event
+     * @throws CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @see #EVENT_TYPE_NEXT_MANEUVER_INFO
+     * @see #EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN
+     *
+     * @hide
+     */
+    public void sendEvent(int eventType, Bundle bundle) throws CarNotConnectedException {
+        try {
+            mService.onEvent(eventType, bundle);
+        } catch (IllegalStateException e) {
+            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+        } catch (RemoteException e) {
+            handleCarServiceRemoteExceptionAndThrow(e);
+        }
+    }
+
     @Override
     public void onCarDisconnected() {
         Log.d(TAG, "onCarDisconnected");
diff --git a/car-lib/src/android/car/settings/CarSettings.java b/car-lib/src/android/car/settings/CarSettings.java
index 69e9a1e..6e2a8f4 100644
--- a/car-lib/src/android/car/settings/CarSettings.java
+++ b/car-lib/src/android/car/settings/CarSettings.java
@@ -74,6 +74,13 @@
          */
         public static final String KEY_VOLUME_CALL = "android.car.VOLUME_CALL";
         /**
+         * Key for phone ring volume. This is used internally, changing this value will not change
+         * the volume.
+         *
+         * @hide
+         */
+        public static final String KEY_VOLUME_RINGTONE = "android.car.VOLUME_RINGTONE";
+        /**
          * Key for alarm volume. This is used internally, changing this value will not change the
          * volume.
          *
@@ -145,25 +152,81 @@
 
         /**
          * Key for a list of devices to automatically connect on Bluetooth A2dp/Avrcp profiles
-         *
+         * Written to and read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
          * @hide
          */
         public static final String KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES =
                 "android.car.BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES";
         /**
          * Key for a list of devices to automatically connect on Bluetooth HFP & PBAP profiles
+         * Written to and read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
          *
          * @hide
          */
         public static final String KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES =
                 "android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICES";
+
         /**
          * Key for a list of devices to automatically connect on Bluetooth MAP profile
-         *
+         * Written to and read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
          * @hide
          */
         public static final String KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES =
                 "android.car.BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES";
 
+        /**
+         * Key for setting primary Music Device
+         * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+         * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+         * @hide
+         */
+        public static final String KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0 =
+                "android.car.BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0";
+
+        /**
+         * Key for setting secondary Music Device
+         * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+         * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+         * @hide
+         */
+        public static final String KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1 =
+                "android.car.BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1";
+
+        /**
+         * Key for setting Primary Phone Device
+         * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+         * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+         * @hide
+         */
+        public static final String KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0 =
+                "android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0";
+
+        /**
+         * Key for setting Secondary Phone Device
+         * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+         * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+         * @hide
+         */
+        public static final String KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1 =
+                "android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1";
+
+        /**
+         * Key for setting Primary Messaging Device
+         * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+         * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+         * @hide
+         */
+        public static final String KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0 =
+                "android.car.BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0";
+
+        /**
+         * Key for setting Secondary Messaging Device
+         * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+         * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+         * @hide
+         */
+        public static final String KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1 =
+                "android.car.BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1";
+
     }
 }
diff --git a/car-lib/src/android/car/vms/IVmsPublisherClient.aidl b/car-lib/src/android/car/vms/IVmsPublisherClient.aidl
deleted file mode 100644
index 96b993b..0000000
--- a/car-lib/src/android/car/vms/IVmsPublisherClient.aidl
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.vms.IVmsPublisherService;
-import android.car.vms.VmsSubscriptionState;
-
-/**
- * @hide
- */
-interface IVmsPublisherClient {
-    /**
-    * Once the VmsPublisherService is bound to the client, this callback is used to set the
-    * binder that the client can use to invoke publisher services. This also gives the client
-    * the token it should use when calling the service.
-    */
-    oneway void setVmsPublisherService(in IBinder token, IVmsPublisherService service) = 0;
-
-    /**
-     * The VmsPublisherService uses this callback to notify about subscription changes.
-     * @param subscriptionState all the layers that have subscribers and a sequence number,
-     *                          clients should ignore any packet with a sequence number that is less
-     *                          than the highest sequence number they have seen thus far.
-     */
-    oneway void onVmsSubscriptionChange(in VmsSubscriptionState subscriptionState) = 1;
-}
diff --git a/car-lib/src/android/car/vms/IVmsPublisherService.aidl b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
deleted file mode 100644
index 3312794..0000000
--- a/car-lib/src/android/car/vms/IVmsPublisherService.aidl
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsLayersOffering;
-import android.car.vms.VmsSubscriptionState;
-
-/**
- * Exposes publisher services to VMS clients.
- *
- * @hide
- */
-interface IVmsPublisherService {
-    /**
-     * Client call to publish a message.
-     */
-    oneway void publish(in IBinder token, in VmsLayer layer, in byte[] message) = 0;
-
-    /**
-     * Returns the list of VmsLayers that has any clients subscribed to it.
-     */
-    VmsSubscriptionState getSubscriptions() = 1;
-
-    /**
-     * Sets which layers the publisher can publish under which dependencties.
-     */
-    oneway void setLayersOffering(in IBinder token, in VmsLayersOffering offering) = 2;
-
-    /**
-     * The first time a publisher calls this API it will store the publisher info and assigns the
-     * publisher a static ID. Between reboots, subsequent calls with the same publisher info will
-      * return the same ID so that a restarting process can obtain the same ID as it had before.
-     */
-    int getPublisherStaticId(in byte[] publisherInfo) = 3;
-}
diff --git a/car-lib/src/android/car/vms/IVmsSubscriberClient.aidl b/car-lib/src/android/car/vms/IVmsSubscriberClient.aidl
deleted file mode 100644
index 79c88ac..0000000
--- a/car-lib/src/android/car/vms/IVmsSubscriberClient.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.vms.VmsLayer;
-
-/**
- * @hide
- */
-oneway interface IVmsSubscriberClient {
-    /**
-     * A VmsService uses this callback to pass messages to subscribers.
-     */
-    void onVmsMessageReceived(in VmsLayer layer, in byte[] payload) = 0;
-
-    void onLayersAvailabilityChange(in List<VmsLayer> availableLayers) = 1;
-}
diff --git a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
deleted file mode 100644
index 9234134..0000000
--- a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.vms.IVmsSubscriberClient;
-import android.car.vms.VmsLayer;
-
-/**
- * @hide
- */
-interface IVmsSubscriberService {
-    /**
-     * Subscribes the listener to receive messages from layer/version.
-     */
-    void addVmsSubscriberClientListener(
-            in IVmsSubscriberClient listener,
-            in VmsLayer layer) = 0;
-
-    /**
-     * Subscribes the listener to receive messages from all published layer/version. The
-     * service will not send any subscription notifications to publishers (i.e. this is a passive
-     * subscriber).
-     */
-    void addVmsSubscriberClientPassiveListener(in IVmsSubscriberClient listener) = 1;
-
-    /**
-     * Tells the VmsSubscriberService a client unsubscribes to layer messages.
-     */
-    void removeVmsSubscriberClientListener(
-            in IVmsSubscriberClient listener,
-            in VmsLayer layer) = 2;
-
-    /**
-     * Tells the VmsSubscriberService a passive client unsubscribes. This will not unsubscribe
-     * the listener from any specific layer it has subscribed to.
-     */
-    void removeVmsSubscriberClientPassiveListener(
-            in IVmsSubscriberClient listener) = 3;
-
-    /**
-     * Returns a list of available layers from the closure of the publishers offerings.
-     */
-    List<VmsLayer> getAvailableLayers() = 4;
-
-    /**
-     *  Returns a the publisher information for a publisher ID.
-     */
-    byte[] getPublisherInfo(in int publisherId) = 5;
-}
diff --git a/car-lib/src/android/car/vms/VmsLayer.aidl b/car-lib/src/android/car/vms/VmsLayer.aidl
deleted file mode 100644
index ff0768a..0000000
--- a/car-lib/src/android/car/vms/VmsLayer.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-parcelable VmsLayer;
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayer.java b/car-lib/src/android/car/vms/VmsLayer.java
deleted file mode 100644
index afd0ae7..0000000
--- a/car-lib/src/android/car/vms/VmsLayer.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.annotation.FutureFeature;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * A VMS Layer which can be subscribed to by VMS clients.
- * Consists of the layer ID and the layer major version.
- *
- * This class does not contain the minor version since all minor version are backward and forward
- * compatible and should not be used for routing messages.
- * @hide
- */
-@FutureFeature
-public final class VmsLayer implements Parcelable {
-
-    // The layer ID.
-    private int mId;
-
-    // The layer version.
-    private int mVersion;
-
-    public VmsLayer(int id, int version) {
-        mId = id;
-        mVersion = version;
-    }
-
-    public int getId() {
-        return mId;
-    }
-
-    public int getVersion() {
-        return mVersion;
-    }
-
-    /**
-     * Checks the two objects for equality by comparing their IDs and Versions.
-     *
-     * @param o the {@link VmsLayer} to which this one is to be checked for equality
-     * @return true if the underlying objects of the VmsLayer are both considered equal
-     */
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof VmsLayer)) {
-            return false;
-        }
-        VmsLayer p = (VmsLayer) o;
-        return Objects.equals(p.mId, mId) && Objects.equals(p.mVersion, mVersion);
-    }
-
-    /**
-     * Compute a hash code similarly tp {@link android.util.Pair}
-     *
-     * @return a hashcode of the Pair
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mId, mVersion);
-    }
-
-    @Override
-    public String toString() {
-        return "VmsLayer{" + mId + " " + mVersion + "}";
-    }
-
-
-    // Parcelable related methods.
-    public static final Parcelable.Creator<VmsLayer> CREATOR = new
-            Parcelable.Creator<VmsLayer>() {
-                public VmsLayer createFromParcel(Parcel in) {
-                    return new VmsLayer(in);
-                }
-
-                public VmsLayer[] newArray(int size) {
-                    return new VmsLayer[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mId);
-        out.writeInt(mVersion);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    private VmsLayer(Parcel in) {
-        readFromParcel(in);
-    }
-
-    private void readFromParcel(Parcel in) {
-        mId = in.readInt();
-        mVersion = in.readInt();
-    }
-}
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayerDependency.aidl b/car-lib/src/android/car/vms/VmsLayerDependency.aidl
deleted file mode 100644
index 3e64001..0000000
--- a/car-lib/src/android/car/vms/VmsLayerDependency.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-parcelable VmsLayerDependency;
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayerDependency.java b/car-lib/src/android/car/vms/VmsLayerDependency.java
deleted file mode 100644
index e14c7ec..0000000
--- a/car-lib/src/android/car/vms/VmsLayerDependency.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.annotation.FutureFeature;
-import android.os.Parcel;
-import android.os.Parcelable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A dependency for a VMS layer on other VMS layers.
- *
- * @hide
- */
-@FutureFeature
-public final class VmsLayerDependency implements Parcelable {
-    private final VmsLayer mLayer;
-    private final Set<VmsLayer> mDependency;
-
-    /**
-     * Construct a dependency for layer on other layers.
-     */
-    public VmsLayerDependency(VmsLayer layer, Set<VmsLayer> dependencies) {
-        mLayer = layer;
-        mDependency = Collections.unmodifiableSet(dependencies);
-    }
-
-    /**
-     * Constructs a layer without a dependency.
-     */
-    public VmsLayerDependency(VmsLayer layer) {
-        mLayer = layer;
-        mDependency = Collections.emptySet();
-    }
-
-    /**
-     * Checks if a layer has a dependency.
-     */
-    public boolean hasDependencies() {
-        return (!mDependency.isEmpty());
-    }
-
-    public VmsLayer getLayer() {
-        return mLayer;
-    }
-
-    /**
-     * Returns the dependencies.
-     */
-    public Set<VmsLayer> getDependencies() {
-        return mDependency;
-    }
-
-    public static final Parcelable.Creator<VmsLayerDependency> CREATOR = new
-        Parcelable.Creator<VmsLayerDependency>() {
-            public VmsLayerDependency createFromParcel(Parcel in) {
-                return new VmsLayerDependency(in);
-            }
-            public VmsLayerDependency[] newArray(int size) {
-                return new VmsLayerDependency[size];
-            }
-        };
-
-    public String toString() {
-        return "VmsLayerDependency{ Layer: " + mLayer + " Dependency: " + mDependency + "}";
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mLayer, flags);
-        out.writeParcelableList(new ArrayList<VmsLayer>(mDependency), flags);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    private VmsLayerDependency(Parcel in) {
-        mLayer = in.readParcelable(VmsLayer.class.getClassLoader());
-        List<VmsLayer> dependency = new ArrayList<>();
-        in.readParcelableList(dependency, VmsLayer.class.getClassLoader());
-        mDependency = Collections.unmodifiableSet(new HashSet<VmsLayer>(dependency));
-    }
-}
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayersOffering.aidl b/car-lib/src/android/car/vms/VmsLayersOffering.aidl
deleted file mode 100644
index 4231f2d..0000000
--- a/car-lib/src/android/car/vms/VmsLayersOffering.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-parcelable VmsLayersOffering;
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayersOffering.java b/car-lib/src/android/car/vms/VmsLayersOffering.java
deleted file mode 100644
index 51a0b99..0000000
--- a/car-lib/src/android/car/vms/VmsLayersOffering.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.annotation.FutureFeature;
-import android.os.Parcel;
-import android.os.Parcelable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The state of dependencies for a single publisher.
- *
- * @hide
- */
-@FutureFeature
-public final class VmsLayersOffering implements Parcelable {
-
-    private final List<VmsLayerDependency> mDependencies;
-
-    public VmsLayersOffering(List<VmsLayerDependency> dependencies) {
-        mDependencies = Collections.unmodifiableList(dependencies);
-    }
-
-    /**
-     * Returns the dependencies.
-     */
-    public List<VmsLayerDependency> getDependencies() {
-        return mDependencies;
-    }
-
-    public static final Parcelable.Creator<VmsLayersOffering> CREATOR = new
-        Parcelable.Creator<VmsLayersOffering>() {
-            public VmsLayersOffering createFromParcel(Parcel in) {
-                return new VmsLayersOffering(in);
-            }
-            public VmsLayersOffering[] newArray(int size) {
-                return new VmsLayersOffering[size];
-            }
-        };
-
-    @Override
-    public String toString() {
-        return "VmsLayersOffering{" + mDependencies+ "}";
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelableList(mDependencies, flags);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    private VmsLayersOffering(Parcel in) {
-        List<VmsLayerDependency> dependencies = new ArrayList<>();
-        in.readParcelableList(dependencies, VmsLayerDependency.class.getClassLoader());
-        mDependencies = Collections.unmodifiableList(dependencies);
-    }
-}
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java
deleted file mode 100644
index ea265df..0000000
--- a/car-lib/src/android/car/vms/VmsPublisherClientService.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-
-import android.app.Service;
-import android.car.annotation.FutureFeature;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.annotation.Nullable;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-/**
- * Services that need VMS publisher services need to inherit from this class and also need to be
- * declared in the array vmsPublisherClients located in
- * packages/services/Car/service/res/values/config.xml (most likely, this file will be in an overlay
- * of the target product.
- *
- * The {@link com.android.car.VmsPublisherService} will start this service. The callback
- * {@link #onVmsPublisherServiceReady()} notifies when VMS publisher services (i.e.
- * {@link #publish(int, int, byte[])} and {@link #getSubscribers()}) can be used.
- *
- * SystemApi candidate.
- *
- * @hide
- */
-@FutureFeature
-public abstract class VmsPublisherClientService extends Service {
-    private static final boolean DBG = true;
-    private static final String TAG = "VmsPublisherClient";
-
-    private final Object mLock = new Object();
-
-    private Handler mHandler = new VmsEventHandler(this);
-    private final VmsPublisherClientBinder mVmsPublisherClient = new VmsPublisherClientBinder(this);
-    private volatile IVmsPublisherService mVmsPublisherService = null;
-    @GuardedBy("mLock")
-    private IBinder mToken = null;
-
-    @Override
-    public final IBinder onBind(Intent intent) {
-        if (DBG) {
-            Log.d(TAG, "onBind, intent: " + intent);
-        }
-        return mVmsPublisherClient.asBinder();
-    }
-
-    @Override
-    public final boolean onUnbind(Intent intent) {
-        if (DBG) {
-            Log.d(TAG, "onUnbind, intent: " + intent);
-        }
-        stopSelf();
-        return super.onUnbind(intent);
-    }
-
-    public void setToken(IBinder token) {
-        synchronized (mLock) {
-            mToken = token;
-        }
-    }
-
-    /**
-     * Notifies that the publisher services are ready.
-     */
-    public abstract void onVmsPublisherServiceReady();
-
-    /**
-     * Publishers need to implement this method to receive notifications of subscription changes.
-     *
-     * @param subscriptionState  layers with subscribers and a sequence number.
-     */
-    public abstract void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState);
-
-    /**
-     * Uses the VmsPublisherService binder to publish messages.
-     *
-     * @param layer   the layer to publish to.
-     * @param payload the message to be sent.
-     * @return if the call to the method VmsPublisherService.publish was successful.
-     */
-    public final boolean publish(VmsLayer layer, byte[] payload) {
-        if (DBG) {
-            Log.d(TAG, "Publishing for layer : " + layer);
-        }
-
-        IBinder token = getTokenForPublisherServiceThreadSafe();
-
-        try {
-            mVmsPublisherService.publish(token, layer, payload);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "unable to publish message: " + payload, e);
-        }
-        return false;
-    }
-
-    /**
-     * Uses the VmsPublisherService binder to set the layers offering.
-     *
-     * @param offering the layers that the publisher may publish.
-     * @return if the call to VmsPublisherService.setLayersOffering was successful.
-     */
-    public final boolean setLayersOffering(VmsLayersOffering offering) {
-        if (DBG) {
-            Log.d(TAG, "Setting layers offering : " + offering);
-        }
-
-        IBinder token = getTokenForPublisherServiceThreadSafe();
-
-        try {
-            mVmsPublisherService.setLayersOffering(token, offering);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "unable to set layers offering: " + offering, e);
-        }
-        return false;
-    }
-
-    private IBinder getTokenForPublisherServiceThreadSafe() {
-        if (mVmsPublisherService == null) {
-            throw new IllegalStateException("VmsPublisherService not set.");
-        }
-
-        IBinder token;
-        synchronized (mLock) {
-            token = mToken;
-        }
-        if (token == null) {
-            throw new IllegalStateException("VmsPublisherService does not have a valid token.");
-        }
-        return token;
-    }
-
-    public final int getPublisherStaticId(byte[] publisherInfo) {
-        if (mVmsPublisherService == null) {
-            throw new IllegalStateException("VmsPublisherService not set.");
-        }
-        Integer publisherStaticId = null;
-        try {
-            Log.i(TAG, "Getting publisher static ID");
-            publisherStaticId = mVmsPublisherService.getPublisherStaticId(publisherInfo);
-        } catch (RemoteException e) {
-            Log.e(TAG, "unable to invoke binder method.", e);
-        }
-        if (publisherStaticId == null) {
-            throw new IllegalStateException("VmsPublisherService cannot get a publisher static ID.");
-        }
-        return publisherStaticId;
-    }
-
-    /**
-     * Uses the VmsPublisherService binder to get the list of layer/version that have any
-     * subscribers.
-     *
-     * @return list of layer/version or null in case of error.
-     */
-    public final @Nullable VmsSubscriptionState getSubscriptions() {
-        if (mVmsPublisherService == null) {
-            throw new IllegalStateException("VmsPublisherService not set.");
-        }
-        try {
-            return mVmsPublisherService.getSubscriptions();
-        } catch (RemoteException e) {
-            Log.e(TAG, "unable to invoke binder method.", e);
-        }
-        return null;
-    }
-
-    private void setVmsPublisherService(IVmsPublisherService service) {
-        mVmsPublisherService = service;
-        onVmsPublisherServiceReady();
-    }
-
-    /**
-     * Implements the interface that the VMS service uses to communicate with this client.
-     */
-    private static class VmsPublisherClientBinder extends IVmsPublisherClient.Stub {
-        private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService;
-        @GuardedBy("mSequenceLock")
-        private long mSequence = -1;
-        private final Object mSequenceLock = new Object();
-
-        public VmsPublisherClientBinder(VmsPublisherClientService vmsPublisherClientService) {
-            mVmsPublisherClientService = new WeakReference<>(vmsPublisherClientService);
-        }
-
-        @Override
-        public void setVmsPublisherService(IBinder token, IVmsPublisherService service)
-                throws RemoteException {
-            VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get();
-            if (vmsPublisherClientService == null) return;
-            if (DBG) {
-                Log.d(TAG, "setting VmsPublisherService.");
-            }
-            Handler handler = vmsPublisherClientService.mHandler;
-            handler.sendMessage(
-                    handler.obtainMessage(VmsEventHandler.SET_SERVICE_CALLBACK, service));
-            vmsPublisherClientService.setToken(token);
-        }
-
-        @Override
-        public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState)
-                throws RemoteException {
-            VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get();
-            if (vmsPublisherClientService == null) return;
-            if (DBG) {
-                Log.d(TAG, "subscription event: " + subscriptionState);
-            }
-            synchronized (mSequenceLock) {
-                if (subscriptionState.getSequenceNumber() <= mSequence) {
-                    Log.w(TAG, "Sequence out of order. Current sequence = " + mSequence
-                            + "; expected new sequence = " + subscriptionState.getSequenceNumber());
-                    // Do not propagate old notifications.
-                    return;
-                } else {
-                    mSequence = subscriptionState.getSequenceNumber();
-                }
-            }
-            Handler handler = vmsPublisherClientService.mHandler;
-            handler.sendMessage(
-                    handler.obtainMessage(VmsEventHandler.ON_SUBSCRIPTION_CHANGE_EVENT,
-                            subscriptionState));
-        }
-    }
-
-    /**
-     * Receives events from the binder thread and dispatches them.
-     */
-    private final static class VmsEventHandler extends Handler {
-        /** Constants handled in the handler */
-        private static final int ON_SUBSCRIPTION_CHANGE_EVENT = 0;
-        private static final int SET_SERVICE_CALLBACK = 1;
-
-        private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService;
-
-        VmsEventHandler(VmsPublisherClientService service) {
-            super(Looper.getMainLooper());
-            mVmsPublisherClientService = new WeakReference<>(service);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            VmsPublisherClientService service = mVmsPublisherClientService.get();
-            if (service == null) return;
-            switch (msg.what) {
-                case ON_SUBSCRIPTION_CHANGE_EVENT:
-                    VmsSubscriptionState subscriptionState = (VmsSubscriptionState) msg.obj;
-                    service.onVmsSubscriptionChange(subscriptionState);
-                    break;
-                case SET_SERVICE_CALLBACK:
-                    service.setVmsPublisherService((IVmsPublisherService) msg.obj);
-                    break;
-                default:
-                    Log.e(TAG, "Event type not handled:  " + msg.what);
-                    break;
-            }
-        }
-    }
-}
diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java
deleted file mode 100644
index 84405f4..0000000
--- a/car-lib/src/android/car/vms/VmsSubscriberManager.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.Car;
-import android.car.CarManagerBase;
-import android.car.CarNotConnectedException;
-import android.car.annotation.FutureFeature;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-/**
- * API for interfacing with the VmsSubscriberService. It supports a single listener that can
- * (un)subscribe to different layers. After getting an instance of this manager, the first step
- * must be to call #setListener. After that, #subscribe and #unsubscribe methods can be invoked.
- * SystemApi candidate
- *
- * @hide
- */
-@FutureFeature
-public final class VmsSubscriberManager implements CarManagerBase {
-    private static final boolean DBG = true;
-    private static final String TAG = "VmsSubscriberManager";
-
-    private final Handler mHandler;
-    private final IVmsSubscriberService mVmsSubscriberService;
-    private final IVmsSubscriberClient mIListener;
-    private final Object mListenerLock = new Object();
-    @GuardedBy("mListenerLock")
-    private VmsSubscriberClientListener mListener;
-
-    /** Interface exposed to VMS subscribers: it is a wrapper of IVmsSubscriberClient. */
-    public interface VmsSubscriberClientListener {
-        /** Called when the property is updated */
-        void onVmsMessageReceived(VmsLayer layer, byte[] payload);
-
-        /** Called when layers availability change */
-        void onLayersAvailabilityChange(List<VmsLayer> availableLayers);
-
-        /** Notifies the client of the disconnect event */
-        void onCarDisconnected();
-    }
-
-    /**
-     * Allows to asynchronously dispatch onVmsMessageReceived events.
-     */
-    private final static class VmsEventHandler extends Handler {
-        /** Constants handled in the handler */
-        private static final int ON_RECEIVE_MESSAGE_EVENT = 0;
-        private static final int ON_AVAILABILITY_CHANGE_EVENT = 1;
-
-        private final WeakReference<VmsSubscriberManager> mMgr;
-
-        VmsEventHandler(VmsSubscriberManager mgr, Looper looper) {
-            super(looper);
-            mMgr = new WeakReference<>(mgr);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            VmsSubscriberManager mgr = mMgr.get();
-            switch (msg.what) {
-                case ON_RECEIVE_MESSAGE_EVENT:
-                    if (mgr != null) {
-                        // Parse the message
-                        VmsDataMessage vmsDataMessage = (VmsDataMessage) msg.obj;
-
-                        // Dispatch the parsed message
-                        mgr.dispatchOnReceiveMessage(vmsDataMessage.getLayer(),
-                                                     vmsDataMessage.getPayload());
-                    }
-                    break;
-                case ON_AVAILABILITY_CHANGE_EVENT:
-                    if (mgr != null) {
-                        // Parse the message
-                        List<VmsLayer> vmsAvailabilityChangeMessage = (List<VmsLayer>) msg.obj;
-
-                        // Dispatch the parsed message
-                        mgr.dispatchOnAvailabilityChangeMessage(vmsAvailabilityChangeMessage);
-                    }
-                    break;
-
-                default:
-                    Log.e(VmsSubscriberManager.TAG, "Event type not handled:  " + msg.what);
-                    break;
-            }
-        }
-    }
-
-    public VmsSubscriberManager(IBinder service, Handler handler) {
-        mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
-        mHandler = new VmsEventHandler(this, handler.getLooper());
-        mIListener = new IVmsSubscriberClient.Stub() {
-            @Override
-            public void onVmsMessageReceived(VmsLayer layer, byte[] payload)
-                throws RemoteException {
-                // Create the data message
-                VmsDataMessage vmsDataMessage = new VmsDataMessage(layer, payload);
-                mHandler.sendMessage(
-                        mHandler.obtainMessage(
-                            VmsEventHandler.ON_RECEIVE_MESSAGE_EVENT,
-                            vmsDataMessage));
-            }
-
-            @Override
-            public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
-                mHandler.sendMessage(
-                    mHandler.obtainMessage(
-                        VmsEventHandler.ON_AVAILABILITY_CHANGE_EVENT,
-                        availableLayers));
-            }
-        };
-    }
-
-    /**
-     * Sets the listener ({@link #mListener}) this manager is linked to. Subscriptions to the
-     * {@link com.android.car.VmsSubscriberService} are done through the {@link #mIListener}.
-     * Therefore, notifications from the {@link com.android.car.VmsSubscriberService} are received
-     * by the {@link #mIListener} and then forwarded to the {@link #mListener}.
-     *
-     * It is expected that this method is invoked just once during the lifetime of the object.
-     *
-     * @param listener subscriber listener that will handle onVmsMessageReceived events.
-     * @throws IllegalStateException if the listener was already set.
-     */
-    public void setListener(VmsSubscriberClientListener listener) {
-        if (DBG) {
-            Log.d(TAG, "Setting listener.");
-        }
-        synchronized (mListenerLock) {
-            if (mListener != null) {
-                throw new IllegalStateException("Listener is already configured.");
-            }
-            mListener = listener;
-        }
-    }
-
-    /**
-     * Returns a serialized publisher information for a publisher ID.
-     */
-    public byte[] getPublisherInfo(int publisherId) throws CarNotConnectedException, IllegalStateException {
-        if (DBG) {
-            Log.d(TAG, "Getting all publishers info.");
-        }
-        try {
-            return mVmsSubscriberService.getPublisherInfo(publisherId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not connect: ", e);
-            throw new CarNotConnectedException(e);
-        } catch (IllegalStateException ex) {
-            Car.checkCarNotConnectedExceptionFromCarService(ex);
-            throw new IllegalStateException(ex);
-        }
-    }
-
-    /**
-     * Subscribes to listen to the layer specified.
-     *
-     * @param layer the layer to subscribe to.
-     * @throws IllegalStateException if the listener was not set via {@link #setListener}.
-     */
-    public void subscribe(VmsLayer layer) throws CarNotConnectedException {
-        if (DBG) {
-            Log.d(TAG, "Subscribing to layer: " + layer);
-        }
-        VmsSubscriberClientListener listener;
-        synchronized (mListenerLock) {
-            listener = mListener;
-        }
-        if (listener == null) {
-            Log.w(TAG, "subscribe: listener was not set, " +
-                    "setListener must be called first.");
-            throw new IllegalStateException("Listener was not set.");
-        }
-        try {
-            mVmsSubscriberService.addVmsSubscriberClientListener(mIListener, layer);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not connect: ", e);
-            throw new CarNotConnectedException(e);
-        } catch (IllegalStateException ex) {
-            Car.checkCarNotConnectedExceptionFromCarService(ex);
-        }
-    }
-
-    public void subscribeAll() throws CarNotConnectedException {
-        if (DBG) {
-            Log.d(TAG, "Subscribing passively to all data messages");
-        }
-        VmsSubscriberClientListener listener;
-        synchronized (mListenerLock) {
-            listener = mListener;
-        }
-        if (listener == null) {
-            Log.w(TAG, "subscribe: listener was not set, " +
-                "setListener must be called first.");
-            throw new IllegalStateException("Listener was not set.");
-        }
-        try {
-            mVmsSubscriberService.addVmsSubscriberClientPassiveListener(mIListener);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not connect: ", e);
-            throw new CarNotConnectedException(e);
-        } catch (IllegalStateException ex) {
-            Car.checkCarNotConnectedExceptionFromCarService(ex);
-        }
-    }
-
-    /**
-     * Unsubscribes from the layer/version specified.
-     *
-     * @param layer   the layer to unsubscribe from.
-     * @throws IllegalStateException if the listener was not set via {@link #setListener}.
-     */
-    public void unsubscribe(VmsLayer layer) {
-        if (DBG) {
-            Log.d(TAG, "Unsubscribing from layer: " + layer);
-        }
-        VmsSubscriberClientListener listener;
-        synchronized (mListenerLock) {
-            listener = mListener;
-        }
-        if (listener == null) {
-            Log.w(TAG, "unsubscribe: listener was not set, " +
-                    "setListener must be called first.");
-            throw new IllegalStateException("Listener was not set.");
-        }
-        try {
-            mVmsSubscriberService.removeVmsSubscriberClientListener(mIListener, layer);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to unregister subscriber", e);
-            // ignore
-        } catch (IllegalStateException ex) {
-            Car.hideCarNotConnectedExceptionFromCarService(ex);
-        }
-    }
-
-    public void unsubscribeAll() {
-        if (DBG) {
-            Log.d(TAG, "Unsubscribing passively from all data messages");
-        }
-        VmsSubscriberClientListener listener;
-        synchronized (mListenerLock) {
-            listener = mListener;
-        }
-        if (listener == null) {
-            Log.w(TAG, "unsubscribeAll: listener was not set, " +
-                    "setListener must be called first.");
-            throw new IllegalStateException("Listener was not set.");
-        }
-        try {
-            mVmsSubscriberService.removeVmsSubscriberClientPassiveListener(mIListener);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to unregister subscriber ", e);
-            // ignore
-        } catch (IllegalStateException ex) {
-            Car.hideCarNotConnectedExceptionFromCarService(ex);
-        }
-    }
-
-    private void dispatchOnReceiveMessage(VmsLayer layer, byte[] payload) {
-        VmsSubscriberClientListener listener;
-        synchronized (mListenerLock) {
-            listener = mListener;
-        }
-        if (listener == null) {
-            Log.e(TAG, "Listener died, not dispatching event.");
-            return;
-        }
-        listener.onVmsMessageReceived(layer, payload);
-    }
-
-    private void dispatchOnAvailabilityChangeMessage(List<VmsLayer> availableLayers) {
-        VmsSubscriberClientListener listener;
-        synchronized (mListenerLock) {
-            listener = mListener;
-        }
-        if (listener == null) {
-            Log.e(TAG, "Listener died, not dispatching event.");
-            return;
-        }
-        listener.onLayersAvailabilityChange(availableLayers);
-    }
-
-    /** @hide */
-    @Override
-    public void onCarDisconnected() {
-        VmsSubscriberClientListener listener;
-        synchronized (mListenerLock) {
-            listener = mListener;
-        }
-        if (listener == null) {
-            Log.e(TAG, "Listener died, not dispatching event.");
-            return;
-        }
-        listener.onCarDisconnected();
-    }
-
-    private static final class VmsDataMessage {
-        private final VmsLayer mLayer;
-        private final byte[] mPayload;
-
-        public VmsDataMessage(VmsLayer layer, byte[] payload) {
-            mLayer = layer;
-            mPayload = payload;
-        }
-
-        public VmsLayer getLayer() {
-            return mLayer;
-        }
-        public byte[] getPayload() {
-            return mPayload;
-        }
-    }
-}
diff --git a/car-lib/src/android/car/vms/VmsSubscriptionState.aidl b/car-lib/src/android/car/vms/VmsSubscriptionState.aidl
deleted file mode 100644
index b5ce8ff..0000000
--- a/car-lib/src/android/car/vms/VmsSubscriptionState.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-parcelable VmsSubscriptionState;
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsSubscriptionState.java b/car-lib/src/android/car/vms/VmsSubscriptionState.java
deleted file mode 100644
index 0e36fb1..0000000
--- a/car-lib/src/android/car/vms/VmsSubscriptionState.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.vms;
-
-import android.car.annotation.FutureFeature;
-import android.os.Parcel;
-import android.os.Parcelable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The list of layers with subscribers.
- *
- * @hide
- */
-@FutureFeature
-public final class VmsSubscriptionState implements Parcelable {
-    private final int mSequenceNumber;
-    private final List<VmsLayer> mLayers;
-
-    /**
-     * Construct a dependency for layer on other layers.
-     */
-    public VmsSubscriptionState(int sequenceNumber, List<VmsLayer> dependencies) {
-        mSequenceNumber = sequenceNumber;
-        mLayers = Collections.unmodifiableList(dependencies);
-    }
-
-    public int getSequenceNumber() {
-        return mSequenceNumber;
-    }
-
-    public List<VmsLayer> getLayers() {
-        return mLayers;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("sequence number=").append(mSequenceNumber);
-        sb.append("; layers={");
-        for(VmsLayer layer : mLayers) {
-            sb.append(layer).append(",");
-        }
-        sb.append("}");
-        return sb.toString();
-    }
-
-    public static final Parcelable.Creator<VmsSubscriptionState> CREATOR = new
-        Parcelable.Creator<VmsSubscriptionState>() {
-            public VmsSubscriptionState createFromParcel(Parcel in) {
-                return new VmsSubscriptionState(in);
-            }
-            public VmsSubscriptionState[] newArray(int size) {
-                return new VmsSubscriptionState[size];
-            }
-        };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mSequenceNumber);
-        out.writeParcelableList(mLayers, flags);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    private VmsSubscriptionState(Parcel in) {
-        mSequenceNumber = in.readInt();
-        List<VmsLayer> layers = new ArrayList<>();
-        in.readParcelableList(layers, VmsLayer.class.getClassLoader());
-        mLayers = Collections.unmodifiableList(layers);
-    }
-}
\ No newline at end of file
diff --git a/car-maps-placeholder/Android.mk b/car-maps-placeholder/Android.mk
index 5f0b93a..53cdbf8 100644
--- a/car-maps-placeholder/Android.mk
+++ b/car-maps-placeholder/Android.mk
@@ -14,6 +14,8 @@
 # limitations under the License.
 #
 
+ifneq ($(TARGET_BUILD_PDK), true)
+
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
@@ -35,3 +37,5 @@
 include packages/apps/Car/libs/car-stream-ui-lib/car-stream-ui-lib.mk
 
 include $(BUILD_PACKAGE)
+
+endif
diff --git a/car-support-lib/Android.mk b/car-support-lib/Android.mk
index d0052e4..76f2587 100644
--- a/car-support-lib/Android.mk
+++ b/car-support-lib/Android.mk
@@ -19,7 +19,8 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-#Build prebuilt android.support.car library
+# Build prebuilt android.support.car library
+# ---------------------------------------------
 include $(CLEAR_VARS)
 
 LOCAL_AAPT_FLAGS := --auto-add-overlay
@@ -47,7 +48,36 @@
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 ifeq ($(BOARD_IS_AUTOMOTIVE), true)
- $(call dist-for-goals,dist_files,$(built_aar):android.support.car.aar)
+    $(call dist-for-goals, dist_files, $(built_aar):android.support.car.aar)
+endif
+
+# Same as above, except without proguard.
+# ---------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay
+LOCAL_MODULE := android.support.car-1p-prebuilt
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+# Build against the current public APIs of the SDK
+LOCAL_SDK_VERSION := current
+
+LOCAL_MANIFEST_FILE := AndroidManifest.xml
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+LOCAL_JAVA_LIBRARIES += android.car\
+                        android-support-annotations
+# Specify 1.7 for backwards compatibility.
+# Otherwise the lib won't be usable on pre-N devices
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+
+LOCAL_PROGUARD_ENABLED := disabled
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+ifeq ($(BOARD_IS_AUTOMOTIVE), true)
+    $(call dist-for-goals, dist_files, $(built_aar):android.support.car-1p.aar)
 endif
 
 # Build support library.
diff --git a/car-support-lib/api/current.txt b/car-support-lib/api/current.txt
index 8912265..3d9c32b 100644
--- a/car-support-lib/api/current.txt
+++ b/car-support-lib/api/current.txt
@@ -100,8 +100,14 @@
     field public static final int INDEX_COMPASS_BEARING = 0; // 0x0
     field public static final int INDEX_COMPASS_PITCH = 1; // 0x1
     field public static final int INDEX_COMPASS_ROLL = 2; // 0x2
+    field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1
+    field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2
+    field public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4; // 0x4
+    field public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3; // 0x3
+    field public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0; // 0x0
     field public final float[] floatValues;
     field public final int[] intValues;
+    field public final long[] longValues;
     field public final int sensorType;
     field public final long timestamp;
   }
@@ -144,10 +150,13 @@
     method public abstract void removeListener(android.support.car.hardware.CarSensorManager.OnSensorChangedListener, int);
     field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
     field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
+    field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
     field public static final int SENSOR_TYPE_COMPASS = 1; // 0x1
     field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
     field public static final int SENSOR_TYPE_NIGHT = 9; // 0x9
     field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
+    field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25; // 0x19
+    field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23; // 0x17
   }
 
   public static abstract interface CarSensorManager.OnSensorChangedListener {
@@ -211,9 +220,9 @@
     ctor public CarNavigationStatusManager();
     method public abstract void addListener(android.support.car.navigation.CarNavigationStatusManager.CarNavigationCallback) throws android.support.car.CarNotConnectedException;
     method public abstract void removeListener();
-    method public abstract void sendNavigationStatus(int) throws android.support.car.CarNotConnectedException;
-    method public abstract void sendNavigationTurnDistanceEvent(int, int, int, int) throws android.support.car.CarNotConnectedException;
-    method public abstract void sendNavigationTurnEvent(int, java.lang.CharSequence, int, int, int) throws android.support.car.CarNotConnectedException;
+    method public abstract deprecated void sendNavigationStatus(int) throws android.support.car.CarNotConnectedException;
+    method public abstract deprecated void sendNavigationTurnDistanceEvent(int, int, int, int) throws android.support.car.CarNotConnectedException;
+    method public abstract deprecated void sendNavigationTurnEvent(int, java.lang.CharSequence, int, int, int) throws android.support.car.CarNotConnectedException;
     field public static final int DISTANCE_FEET = 4; // 0x4
     field public static final int DISTANCE_KILOMETERS = 2; // 0x2
     field public static final int DISTANCE_METERS = 1; // 0x1
diff --git a/car-support-lib/proguard-release.flags b/car-support-lib/proguard-release.flags
index daccb0d..6ec8b3c 100644
--- a/car-support-lib/proguard-release.flags
+++ b/car-support-lib/proguard-release.flags
@@ -5227,8 +5227,14 @@
     public static int INDEX_COMPASS_BEARING;
     public static int INDEX_COMPASS_PITCH;
     public static int INDEX_COMPASS_ROLL;
+    public static int INDEX_WHEEL_DISTANCE_FRONT_LEFT;
+    public static int INDEX_WHEEL_DISTANCE_FRONT_RIGHT;
+    public static int INDEX_WHEEL_DISTANCE_REAR_LEFT;
+    public static int INDEX_WHEEL_DISTANCE_REAR_RIGHT;
+    public static int INDEX_WHEEL_DISTANCE_RESET_COUNT;
     public float[] floatValues;
     public int[] intValues;
+    public long[] longValues;
     public int sensorType;
     public long timestamp;
 }
@@ -5286,10 +5292,13 @@
 
     public static int SENSOR_RATE_FASTEST;
     public static int SENSOR_RATE_NORMAL;
+    public static int SENSOR_TYPE_ABS_ACTIVE;
     public static int SENSOR_TYPE_COMPASS;
     public static int SENSOR_TYPE_DRIVING_STATUS;
     public static int SENSOR_TYPE_NIGHT;
     public static int SENSOR_TYPE_PARKING_BRAKE;
+    public static int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE;
+    public static int SENSOR_TYPE_WHEEL_TICK_DISTANCE;
 }
 
 -keep class android.support.car.hardware.CarSensorManager$OnSensorChangedListener {
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index c47b650..a374d87 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -178,6 +178,12 @@
      */
     public static final String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
     /**
+     * Permission necessary to access car dynamics state.
+     * @hide
+     */
+    public static final String PERMISSION_VEHICLE_DYNAMICS_STATE =
+            "android.car.permission.VEHICLE_DYNAMICS_STATE";
+    /**
      * Permission necessary to access a car-specific communication channel.
      */
     public static final String PERMISSION_VENDOR_EXTENSION =
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorConfig.java b/car-support-lib/src/android/support/car/hardware/CarSensorConfig.java
new file mode 100644
index 0000000..3c71e04
--- /dev/null
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorConfig.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.car.hardware;
+
+import android.os.Bundle;
+import android.support.annotation.RestrictTo;
+import java.util.ArrayList;
+
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+/**
+ * A CarSensorConfig object corresponds to a single sensor type coming from the car.
+ * @hide
+ */
+public class CarSensorConfig {
+    /** List of property specific mapped elements in bundle for WHEEL_TICK_DISTANCE sensor*/
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS =
+        "android.car.wheelTickDistanceSupportedWhheels";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK =
+        "android.car.wheelTickDistanceFrontLeftUmPerTick";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK =
+        "android.car.wheelTickDistanceFrontRightUmPerTick";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK =
+        "android.car.wheelTickDistanceRearRightUmPerTick";
+    /** @hide */
+    public final static String WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK =
+        "android.car.wheelTickDistanceRearLeftUmPerTick";
+
+    /** Config data stored in Bundle */
+    private final Bundle mConfig;
+    private final int mType;
+
+    private final static int RAW_BUNDLE_SIZE = 4;
+    private final static int WHEEL_TICK_DISTANCE_BUNDLE_SIZE = 6;
+
+    /**
+     * Constructs a {@link CarSensorConfig}. Handled by CarSensorManager implementations.
+     * App developers need not worry about constructing these objects.
+     * @hide
+     */
+    @RestrictTo(GROUP_ID)
+    public CarSensorConfig(int type, Bundle in) {
+        mType = type;
+        mConfig = in.deepCopy();
+    }
+
+    private void checkType(int type) {
+        if (mType == type) {
+            return;
+        }
+        throw new UnsupportedOperationException(String.format(
+            "Invalid sensor type: expected %d, got %d", type, mType));
+    }
+
+    /** @hide */
+    public int getInt(String key) {
+        if (mConfig.containsKey(key)) {
+            return mConfig.getInt(key);
+        } else {
+            throw new IllegalArgumentException("SensorType " + mType +
+                " does not contain key: " + key);
+        }
+    }
+
+    /** @hide */
+    public int getType() {
+        return mType;
+    }
+
+    /** @hide */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getName() + "[");
+        sb.append("mConfig: " + mConfig.toString());
+        sb.append("mType: " + mType);
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
index 215cddf..f7b4dba 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
@@ -88,6 +88,15 @@
     public static final int INDEX_COMPASS_ROLL    = 2;
 
 
+    /**
+     * Index for {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE} in longValues.
+     */
+    public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0;
+    public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
+    public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
+    public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
+    public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
+
     private static final long MILLI_IN_NANOS = 1000000L;
 
     /** Sensor type for this event, such as {@link CarSensorManager#SENSOR_TYPE_COMPASS}. */
@@ -104,9 +113,12 @@
     public final float[] floatValues;
     /** Array holding int type of sensor data. */
     public final int[] intValues;
+    /** array holding long int type of sensor data */
+    public final long[] longValues;
 
     private static final float[] EMPTY_FLOAT_ARRAY = {};
     private static final int[] EMPTY_INT_ARRAY = {};
+    private static final long[] EMPTY_LONG_ARRAY = {};
 
     /**
      * Constructs a {@link CarSensorEvent} from integer values. Handled by
@@ -115,11 +127,13 @@
      * @hide
      */
     @RestrictTo(GROUP_ID)
-    public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize) {
+    public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize,
+                          int longValueSize) {
         this.sensorType = sensorType;
         this.timestamp = timestamp;
         floatValues = new float[floatValueSize];
         intValues = new int[intValueSize];
+        longValues = new long[longValueSize];
     }
 
     /**
@@ -128,14 +142,17 @@
      * @param timestamp time since system start in nanoseconds
      * @param floatValues {@code null} will be converted to an empty array
      * @param intValues {@code null} will be converted to an empty array
+     * @param longValues {@code null} will be converted to an empty array
      * @hide
      */
     @RestrictTo(GROUP_ID)
-    public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues) {
+    public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues,
+                          long[] longValues) {
         this.sensorType = sensorType;
         this.timestamp = timestamp;
         this.floatValues = (floatValues == null) ? EMPTY_FLOAT_ARRAY : floatValues;
         this.intValues = (intValues == null) ? EMPTY_INT_ARRAY : intValues;
+        this.longValues = (longValues == null) ? EMPTY_LONG_ARRAY : longValues;
     }
 
     /**
@@ -145,10 +162,12 @@
      * @param floatValues {@code null} will be converted to an empty array
      * @param byteValues bytes will be converted into the intValues array. {@code null} will be
      * converted to an empty array.
+     * @param longValues {@code null} will be converted to an empty array
      * @hide
      */
     @RestrictTo(GROUP_ID)
-    public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, byte[] byteValues) {
+    public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, byte[] byteValues,
+                          long[] longValues) {
         this.sensorType = sensorType;
         this.timestamp = timestamp;
         this.floatValues = (floatValues == null) ? EMPTY_FLOAT_ARRAY : floatValues;
@@ -160,6 +179,7 @@
                 this.intValues[i] = byteValues[i];
             }
         }
+        this.longValues = (longValues == null) ? EMPTY_LONG_ARRAY : longValues;
     }
 
     private void checkType(int type) {
@@ -920,6 +940,104 @@
     }
 
     /** @hide */
+    public static class CarWheelTickDistanceData {
+        public final long timestamp;
+        public final long sensorResetCount;
+        public final long frontLeftWheelDistanceMm;
+        public final long frontRightWheelDistanceMm;
+        public final long rearRightWheelDistanceMm;
+        public final long rearLeftWheelDistanceMm;
+
+        /** @hide */
+        @RestrictTo(GROUP_ID)
+        public CarWheelTickDistanceData(long timestamp, long sensorResetCount,
+                                    long frontLeftWheelDistanceMm, long frontRightWheelDistanceMm,
+                                    long rearRightWheelDistanceMm, long rearLeftWheelDistanceMm) {
+            this.timestamp = timestamp;
+            this.sensorResetCount = sensorResetCount;
+            this.frontLeftWheelDistanceMm = frontLeftWheelDistanceMm;
+            this.frontRightWheelDistanceMm = frontRightWheelDistanceMm;
+            this.rearRightWheelDistanceMm = rearRightWheelDistanceMm;
+            this.rearLeftWheelDistanceMm = rearLeftWheelDistanceMm;
+        }
+    }
+
+    /**
+     * Convenience method for obtaining a {@link CarWheelTickDistanceData} object from a
+     * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE}.
+     *
+     * @return CarWheelTickDistanceData object corresponding to data contained in the CarSensorEvent
+     * @hide
+     */
+    public CarWheelTickDistanceData getCarWheelTickDistanceData() {
+        checkType(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
+        long sensorResetCount = longValues[INDEX_WHEEL_DISTANCE_RESET_COUNT];
+        long frontLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_LEFT];
+        long frontRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_RIGHT];
+        long rearRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_RIGHT];
+        long rearLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_LEFT];
+        return new CarWheelTickDistanceData(timestamp, sensorResetCount, frontLeftWheelDistanceMm,
+            frontRightWheelDistanceMm, rearRightWheelDistanceMm, rearLeftWheelDistanceMm);
+    }
+
+    /** @hide */
+    public static class CarAbsActiveData {
+        public final long timestamp;
+        public final boolean absIsActive;
+
+        /** @hide */
+        @RestrictTo(GROUP_ID)
+        public CarAbsActiveData(long timestamp, boolean absIsActive) {
+            this.timestamp = timestamp;
+            this.absIsActive = absIsActive;
+        };
+    }
+
+    /**
+     * Convenience method for obtaining a {@link CarAbsActiveData} object from a CarSensorEvent
+     * object with type {@link CarSensorManager#SENSOR_TYPE_ABS_ACTIVE}.
+     *
+     * @param data an optional output parameter which, if non-null, will be used by this method
+     *     instead of a newly created object.
+     * @return a CarAbsActiveData object corresponding to data contained in the CarSensorEvent.
+     * @hide
+     */
+    public CarAbsActiveData getCarAbsActiveData() {
+        checkType(CarSensorManager.SENSOR_TYPE_ABS_ACTIVE);
+        boolean absIsActive = intValues[0] == 1;
+        return new CarAbsActiveData(timestamp, absIsActive);
+    }
+
+    /** @hide */
+    public static class CarTractionControlActiveData {
+        public final long timestamp;
+        public final boolean tractionControlIsActive;
+
+        /** @hide */
+        @RestrictTo(GROUP_ID)
+        public CarTractionControlActiveData(long timestamp, boolean tractionControlIsActive) {
+            this.timestamp = timestamp;
+            this.tractionControlIsActive = tractionControlIsActive;
+        };
+    }
+
+    /**
+     * Convenience method for obtaining a {@link CarTractionControlActiveData} object from a
+     * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_TRACTION_CONTROL_ACTIVE}.
+     *
+     * @param data an optional output parameter which, if non-null, will be used by this method
+     *     instead of a newly created object.
+     * @return a CarTractionControlActiveData object corresponding to data contained in the
+     *     CarSensorEvent.
+     * @hide
+     */
+    public CarTractionControlActiveData getCarTractionControlActiveData() {
+        checkType(CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE);
+        boolean tractionControlIsActive = intValues[0] == 1;
+        return new CarTractionControlActiveData(timestamp, tractionControlIsActive);
+    }
+
+    /** @hide */
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -937,6 +1055,12 @@
                 sb.append(" " + v);
             }
         }
+        if (longValues != null && longValues.length > 0) {
+            sb.append(" long values:");
+            for (long v: longValues) {
+                sb.append(" " + v);
+            }
+        }
         sb.append("]");
         return sb.toString();
     }
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
index adc976c..8bddce1 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
@@ -124,6 +124,23 @@
     public static final int SENSOR_TYPE_RESERVED21 = 21;
     /** @hide */
     public static final int SENSOR_TYPE_RESERVED22 = 22;
+    /**
+     * Represents wheel distance in millimeters.  Some cars may not have individual sensors on each
+     * wheel.  If a value is not available, -1 will be reported.  The wheel distance accumulates
+     * over time.
+     * Requires {@link Car#PERMISSION_MILEAGE} permission.
+     */
+    public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE         = 23;
+    /**
+     * Set to true when ABS is active.  This sensor is event driven.
+     * Requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+     */
+    public static final int SENSOR_TYPE_ABS_ACTIVE                  = 24;
+    /**
+     * Set to true when traction control is active.  This sensor is event driven.
+     * Requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+     */
+    public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE     = 25;
 
     /**
      * Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
@@ -155,7 +172,10 @@
         SENSOR_TYPE_ENVIRONMENT,
         SENSOR_TYPE_ACCELEROMETER,
         SENSOR_TYPE_GPS_SATELLITE,
-        SENSOR_TYPE_GYROSCOPE
+        SENSOR_TYPE_GYROSCOPE,
+        SENSOR_TYPE_WHEEL_TICK_DISTANCE,
+        SENSOR_TYPE_ABS_ACTIVE,
+        SENSOR_TYPE_TRACTION_CONTROL_ACTIVE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SensorType {}
@@ -230,7 +250,8 @@
      * @throws SecurityException if missing the appropriate permission.
      */
     @RequiresPermission(anyOf={Manifest.permission.ACCESS_FINE_LOCATION, Car.PERMISSION_SPEED,
-            Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL}, conditional=true)
+            Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL, Car.PERMISSION_VEHICLE_DYNAMICS_STATE},
+            conditional=true)
     public abstract boolean addListener(OnSensorChangedListener listener,
             @SensorType int sensorType, @SensorRate int rate)
                     throws CarNotConnectedException, IllegalArgumentException;
@@ -259,4 +280,15 @@
      */
     public abstract CarSensorEvent getLatestSensorEvent(@SensorType int type)
             throws CarNotConnectedException;
+
+    /**
+     * Get the config data for the given type.
+     * @param sensor type to request
+     * @return CarSensorConfig object
+     * @throws CarNotConnectedException if the connection to the car service has been lost.
+     * @hide
+     */
+    public abstract CarSensorConfig getSensorConfig(@SensorType int type)
+            throws CarNotConnectedException;
+
 }
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java b/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java
index 2692267..0fde10f 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java
@@ -155,6 +155,16 @@
     }
 
     @Override
+    public CarSensorConfig getSensorConfig(@SensorType int type)
+        throws CarNotConnectedException {
+        try {
+            return convert(mManager.getSensorConfig(type));
+        } catch (android.car.CarNotConnectedException e) {
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    @Override
     public void onCarDisconnected() {
         //nothing to do
     }
@@ -173,7 +183,14 @@
             return null;
         }
         return new CarSensorEvent(event.sensorType, event.timestamp, event.floatValues,
-                event.intValues);
+                event.intValues, event.longValues);
+    }
+
+    private static CarSensorConfig convert(android.car.hardware.CarSensorConfig cfg) {
+        if (cfg == null) {
+            return null;
+        }
+        return new CarSensorConfig(cfg.getType(), cfg.getBundle());
     }
 
     private static class OnSensorChangedListenerProxy
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java b/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
index a0eb482..e110d07 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
@@ -322,7 +322,7 @@
                 case CarSensorManager.SENSOR_TYPE_COMPASS:
                     if (mLastMagneticFieldDataTime != 0 && mLastAccelerometerDataTime != 0) {
                         event = new CarSensorEvent(sensorType, Math.max(mLastMagneticFieldDataTime,
-                                mLastAccelerometerDataTime), 3, 0);
+                                mLastAccelerometerDataTime), 3, 0, 0);
                         SensorManager.getRotationMatrix(mR, mI, mLastAccelerometerData,
                                 mLastMagneticFieldData);
                         SensorManager.getOrientation(mR, mOrientation);
@@ -336,13 +336,13 @@
                     break;
                 case CarSensorManager.SENSOR_TYPE_LOCATION:
                     if (mLastLocationTime != 0) {
-                        event = new CarSensorEvent(sensorType, mLastLocationTime, 6, 3);
+                        event = new CarSensorEvent(sensorType, mLastLocationTime, 6, 3, 0);
                         populateLocationCarSensorEvent(event, mLastLocation);
                     }
                     break;
                 case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
                     if (mLastAccelerometerDataTime != 0) {
-                        event = new CarSensorEvent(sensorType, mLastAccelerometerDataTime, 3, 0);
+                        event = new CarSensorEvent(sensorType, mLastAccelerometerDataTime, 3, 0, 0);
                         event.floatValues[CarSensorEvent.INDEX_ACCELEROMETER_X] =
                                 mLastAccelerometerData[0];
                         event.floatValues[CarSensorEvent.INDEX_ACCELEROMETER_Y] =
@@ -358,7 +358,7 @@
                     break;
                 case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
                     if (mLastGyroscopeDataTime != 0) {
-                        event = new CarSensorEvent(sensorType, mLastGyroscopeDataTime, 3, 0);
+                        event = new CarSensorEvent(sensorType, mLastGyroscopeDataTime, 3, 0, 0);
                         event.floatValues[CarSensorEvent.INDEX_GYROSCOPE_X] = mLastGyroscopeData[0];
                         event.floatValues[CarSensorEvent.INDEX_GYROSCOPE_Y] = mLastGyroscopeData[1];
                         event.floatValues[CarSensorEvent.INDEX_GYROSCOPE_Z] = mLastGyroscopeData[2];
@@ -430,7 +430,7 @@
         int intValuesSize = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL * numberInView
                 + CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET;
         event = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_GPS_SATELLITE, mLastGpsStatusTime,
-                floatValuesSize, intValuesSize);
+                floatValuesSize, intValuesSize, 0);
         event.intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_USE] = numberInUse;
         event.intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_VIEW] = numberInView;
         int i = 0;
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java b/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java
index f814b74..9d2175c 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java
@@ -15,14 +15,15 @@
  */
 package android.support.car.navigation;
 
-import android.support.annotation.IntDef;
-import android.support.annotation.RestrictTo;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
+import android.os.Bundle;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Holds options related to navigation for the car's instrument cluster.
  */
@@ -44,13 +45,15 @@
     private int mMinIntervalMillis;
 
     @ClusterType
-    private int mType;
+    private final int mType;
 
-    private int mImageWidth;
+    private final int mImageWidth;
 
-    private int mImageHeight;
+    private final int mImageHeight;
 
-    private int mImageColorDepthBits;
+    private final int mImageColorDepthBits;
+
+    private final Bundle mExtra;
 
     /** @hide */
     @RestrictTo(GROUP_ID)
@@ -99,6 +102,12 @@
     }
 
     /**
+     * Contains extra information about instrument cluster.
+     * @hide
+     */
+    public Bundle getExtra() { return mExtra; }
+
+    /**
      * @return If instrument cluster is image, number of bits of colour depth it supports (8, 16,
      * or  32), 0 otherwise.
      */
@@ -133,11 +142,24 @@
             int imageWidth,
             int imageHeight,
             int imageColorDepthBits) {
-        this.mMinIntervalMillis = minIntervalMillis;
-        this.mType = type;
-        this.mImageWidth = imageWidth;
-        this.mImageHeight = imageHeight;
-        this.mImageColorDepthBits = imageColorDepthBits;
+        this(minIntervalMillis, type, imageWidth, imageHeight, imageColorDepthBits, null);
+    }
+
+    /** @hide */
+    @RestrictTo(GROUP_ID)
+    CarNavigationInstrumentCluster(
+            int minIntervalMillis,
+            @ClusterType int type,
+            int imageWidth,
+            int imageHeight,
+            int imageColorDepthBits,
+            @Nullable Bundle extra) {
+        mMinIntervalMillis = minIntervalMillis;
+        mType = type;
+        mImageWidth = imageWidth;
+        mImageHeight = imageHeight;
+        mImageColorDepthBits = imageColorDepthBits;
+        mExtra = extra == null ? new Bundle() : new Bundle(extra);
     }
 
 
@@ -149,6 +171,7 @@
                 "type: " + mType + ", " +
                 "imageWidth: " + mImageWidth + ", " +
                 "imageHeight: " + mImageHeight + ", " +
-                "imageColourDepthBits: " + mImageColorDepthBits + " }";
+                "imageColourDepthBits: " + mImageColorDepthBits + ", " +
+                "extra: " + mExtra + " }";
     }
 }
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
index efaf7da..adf9208 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
@@ -16,6 +16,7 @@
 package android.support.car.navigation;
 
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.support.annotation.IntDef;
 import android.support.car.CarManagerBase;
 import android.support.car.CarNotConnectedException;
@@ -188,10 +189,28 @@
     public @interface DistanceUnit {}
 
     /**
+     * Event type that holds information about next maneuver.
+     * @hide
+     */
+    public static final int EVENT_TYPE_NEXT_MANEUVER_INFO = 1;
+    /**
+     * Event type that holds information regarding distance/time to the next maneuver.
+     * @hide
+     */
+    public static final int EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN = 2;
+    /**
+     * All custom (vendor-specific) event types should be equal or greater than this constant.
+     * @hide
+     */
+    public static final int EVENT_TYPE_VENDOR_FIRST = 1024;
+
+    /**
      * Inform the instrument cluster if navigation is active or not.
      * @param status New instrument cluster navigation status, one of the STATUS_* constants in
      * this class.
      * @throws CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public abstract void sendNavigationStatus(@Status int status) throws CarNotConnectedException;
 
@@ -216,6 +235,8 @@
      * @param turnSide Turn side ({@link #TURN_SIDE_LEFT}, {@link #TURN_SIDE_RIGHT} or {@link
      * #TURN_SIDE_UNSPECIFIED}).
      * @throws CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public abstract void sendNavigationTurnEvent(@TurnEvent int turnEvent, CharSequence eventName,
             int turnAngle, int turnNumber, @TurnSide int turnSide) throws CarNotConnectedException;
@@ -231,6 +252,7 @@
      * imageColorDepthBits) in the initial NavigationStatusService call.
      *
      * @hide only first party applications may send a custom image to the cluster.
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public abstract void sendNavigationTurnEvent(@TurnEvent int turnEvent, CharSequence eventName,
             int turnAngle, int turnNumber, Bitmap image, @TurnSide int turnSide)
@@ -247,11 +269,31 @@
      * file.
      * @return Returns {@code true} if successful.
      * @throws CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public abstract void sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds,
             int displayDistanceMillis, int displayDistanceUnit) throws CarNotConnectedException;
 
     /**
+     * Sends events from navigation app to instrument cluster.
+     *
+     * @param eventType event type, the value could be either
+     * {@link #EVENT_TYPE_NEXT_MANEUVER_INFO}, {@link EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN}, or
+     * vendor-specific code.
+     *
+     * @param bundle object that holds data about the event
+     * @throws android.car.CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @see #EVENT_TYPE_NEXT_MANEUVER_INFO
+     * @see #EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN
+     *
+     * @hide
+     */
+    public abstract void sendEvent(int eventType, Bundle bundle)
+            throws CarNotConnectedException;
+
+    /**
      * @param callback {@link CarNavigationCallback} to be registered, replacing any existing
      * listeners.
      * @throws CarNotConnectedException if the connection to the car service has been lost.
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
index f96952b..a72e20e 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
@@ -16,6 +16,7 @@
 package android.support.car.navigation;
 
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.support.car.CarNotConnectedException;
 
 /**
@@ -71,6 +72,15 @@
     }
 
     @Override
+    public void sendEvent(int eventType, Bundle bundle) throws CarNotConnectedException {
+        try {
+            mManager.sendEvent(eventType, bundle);
+        } catch (android.car.CarNotConnectedException e) {
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    @Override
     public void onCarDisconnected() {
         //nothing to do
     }
@@ -103,6 +113,7 @@
             return null;
         }
         return new CarNavigationInstrumentCluster(ic.getMinIntervalMillis(), ic.getType(),
-                ic.getImageWidth(), ic.getImageHeight(), ic.getImageColorDepthBits());
+                ic.getImageWidth(), ic.getImageHeight(), ic.getImageColorDepthBits(),
+                ic.getExtra());
     }
 }
diff --git a/car-usb-handler/Android.mk b/car-usb-handler/Android.mk
index c050854..3194250 100644
--- a/car-usb-handler/Android.mk
+++ b/car-usb-handler/Android.mk
@@ -14,6 +14,8 @@
 #
 #
 
+ifneq ($(TARGET_BUILD_PDK), true)
+
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
@@ -33,3 +35,5 @@
 LOCAL_JAVA_LIBRARIES += android.car
 
 include $(BUILD_PACKAGE)
+
+endif
diff --git a/car_product/bootanimations/bootanimation-832.zip b/car_product/bootanimations/bootanimation-832.zip
new file mode 100644
index 0000000..76b9c82
--- /dev/null
+++ b/car_product/bootanimations/bootanimation-832.zip
Binary files differ
diff --git a/car_product/bootanimations/square_280/bootanimation.zip b/car_product/bootanimations/square_280/bootanimation.zip
deleted file mode 100644
index ab4d79f..0000000
--- a/car_product/bootanimations/square_280/bootanimation.zip
+++ /dev/null
Binary files differ
diff --git a/car_product/bootanimations/square_320/bootanimation.zip b/car_product/bootanimations/square_320/bootanimation.zip
deleted file mode 100644
index f621ad8..0000000
--- a/car_product/bootanimations/square_320/bootanimation.zip
+++ /dev/null
Binary files differ
diff --git a/car_product/bootanimations/square_360/bootanimation.zip b/car_product/bootanimations/square_360/bootanimation.zip
deleted file mode 100644
index 3cb32f4..0000000
--- a/car_product/bootanimations/square_360/bootanimation.zip
+++ /dev/null
Binary files differ
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 1c2e7ee..9984726 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -33,7 +33,9 @@
 PRODUCT_PACKAGES += \
     EmbeddedKitchenSinkApp \
     VmsPublisherClientSample \
-    VmsSubscriberClientSample
+    VmsSubscriberClientSample \
+    android.car.cluster.loggingrenderer \
+    DirectRenderingClusterSample \
 
 PRODUCT_COPY_FILES := \
     frameworks/av/media/libeffects/data/audio_effects.conf:system/etc/audio_effects.conf \
@@ -82,7 +84,6 @@
     LocalMediaPlayer \
     CarMediaApp \
     CarMessengerApp \
-    Stream \
     CarHvacApp \
     CarMapsPlaceholder \
     CarLatinIME \
@@ -92,7 +93,7 @@
 
 # Boot animation
 PRODUCT_COPY_FILES += \
-    packages/services/Car/car_product/bootanimations/square_280/bootanimation.zip:system/media/bootanimation.zip
+    packages/services/Car/car_product/bootanimations/bootanimation-832.zip:system/media/bootanimation.zip
 
 PRODUCT_PROPERTY_OVERRIDES += \
     fmas.spkr_6ch=35,20,110 \
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index cbc461c..815b9ba 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -17,7 +17,7 @@
 # Base platform for car builds
 # car packages should be added to car.mk instead of here
 
-PRODUCT_PACKAGE_OVERLAYS := packages/services/Car/car_product/overlay
+PRODUCT_PACKAGE_OVERLAYS += packages/services/Car/car_product/overlay
 
 PRODUCT_PACKAGES += \
     ContactsProvider \
@@ -94,7 +94,9 @@
 PRODUCT_COPY_FILES += \
     frameworks/native/data/etc/android.hardware.type.automotive.xml:system/etc/permissions/android.hardware.type.automotive.xml
 
-PRODUCT_PACKAGES += android.hardware.automotive.vehicle@2.1-service
+# Default permission grant exceptions
+PRODUCT_COPY_FILES += \
+    packages/services/Car/car_product/build/default-car-permissions.xml:system/etc/default-permissions/default-car-permissions.xml
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
 
diff --git a/car_product/build/default-car-permissions.xml b/car_product/build/default-car-permissions.xml
new file mode 100644
index 0000000..c36f78c
--- /dev/null
+++ b/car_product/build/default-car-permissions.xml
@@ -0,0 +1,54 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+    This file contains permissions to be granted by default. Default
+    permissions are granted to special platform components and to apps
+    that are approved to get default grants. The special components
+    are apps that are expected tto work out-of-the-box as they provide
+    core use cases such as default dialer, default email, etc. These
+    grants are managed by the platform. The apps that are additionally
+    approved for default grants are ones that provide carrier specific
+    functionality, ones legally required at some location, ones providing
+    alternative disclosure and opt-out UI, ones providing highlight features
+    of a dedicated device, etc. This file contains only the latter exceptions.
+    Fixed permissions cannot be controlled by the user and need a special
+    approval. Typically these are to ensure either legally mandated functions
+    or the app is considered a part of the OS.
+-->
+
+<exceptions>
+
+    <!-- This is an example of an exception:
+    <exception
+        package="foo.bar.permission"
+      <permission name="android.permission.READ_CONTACTS" fixed="true"/>
+      <permission name="android.permission.READ_CALENDAR" fixed="false"/>
+    </exception>
+    -->
+
+    <exception
+            package="com.android.car.messenger">
+        <!-- Contacts -->
+        <permission name="android.permission.READ_CONTACTS" fixed="false"/>
+
+        <!-- SMS -->
+        <permission name="android.permission.SEND_SMS" fixed="false"/>
+        <permission name="android.permission.READ_SMS" fixed="false"/>
+    </exception>
+
+</exceptions>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml b/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
index 2774c9e..9865fe7 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
@@ -19,4 +19,19 @@
 <resources>
     <!-- The background color for the container of notification actions. -->
     <color name="notification_action_list">#ff11181d</color>   <!-- Dark Blue Grey 800 -->
+
+    <!-- The primary text color if the text is on top of a dark background. -->
+    <color name="notification_primary_text_color_light">#fff5f5f5</color>  <!-- Grey 100 -->
+
+    <!-- The primary text color if the text is on top of a light background. -->
+    <color name="notification_primary_text_color_dark">#ff212121</color>  <!-- Grey 900 -->
+
+    <!-- The secondary text color if the text is on top of a dark background. -->
+    <color name="notification_secondary_text_color_light">#ffe0e0e0</color>  <!-- Grey 300 -->
+
+    <!-- The background color of a notification card. -->
+    <color name="notification_material_background_color">#ff172026</color>   <!-- Dark Blue Grey 700 -->
+
+    <!-- The secondary text color if the text is on top of a dark background. -->
+    <color name="notification_secondary_text_color_dark">#ff6B6B6B</color>  <!-- Grey 650 -->
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/colors.xml b/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
index f7a7a12..1efd0eb 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
@@ -19,4 +19,23 @@
 <resources>
     <!-- The background color for the container of notification actions. -->
     <color name="notification_action_list">#ffeeeeee</color>  <!-- Grey 200 -->
+
+    <!-- The primary text color if the text is on top of a light background. -->
+    <color name="notification_primary_text_color_light">#ff212121</color>  <!-- Grey 900 -->
+
+    <!-- The primary text color if the text is on top of a dark background. -->
+    <color name="notification_primary_text_color_dark">#fff5f5f5</color>  <!-- Grey 100 -->
+
+    <!-- The secondary text color if the text is on top of a dark background. -->
+    <color name="notification_secondary_text_color_light">#ff6B6B6B</color>  <!-- Grey 650 -->
+
+    <!-- The secondary text color if the text is on top of a dark background. -->
+    <color name="notification_secondary_text_color_dark">#ffe0e0e0</color>  <!-- Grey 300 -->
+
+    <!-- The background color of a notification card. -->
+    <color name="notification_material_background_color">#fffafafa</color>  <!-- Grey 50 -->
+
+    <!-- The default color for text in a notification. This color is also the default color for
+         icons. -->
+    <color name="notification_default_color">@color/notification_primary_text_color_light</color>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index a9fb0e7..ca8a8d8 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -60,4 +60,13 @@
          notification. If false, then the expand icon has to be clicked in order for the expand
          to occur. -->
     <bool name="config_notificationHeaderClickableForExpand">true</bool>
+
+    <!-- Night mode should be enabled. -->
+    <bool name="config_enableNightMode">true</bool>
+
+    <!-- The action buttons should always take the default color. -->
+    <bool name="config_tintNotificationActionButtons">false</bool>
+
+    <!-- Home screen(Launcher) app presence -->
+    <bool name="config_noHomeScreen">true</bool>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
index 125e1fb..314d757 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
@@ -18,7 +18,9 @@
 -->
 <resources>
     <dimen name="status_bar_height">56dp</dimen>
+    <!-- Both of these are used in separate positions so make sure that they remain the same. -->
     <dimen name="navigation_bar_height_car_mode">112dp</dimen>
+    <dimen name="navigation_bar_height_landscape">112dp</dimen>
     <dimen name="status_bar_icon_size">40dp</dimen>
 
     <!-- The height of the header of a notification. -->
@@ -27,7 +29,7 @@
     <!-- The absolute size of the notification expand icon. -->
     <dimen name="notification_header_expand_icon_size">55dp</dimen>
 
-        <!-- The top padding for the notification expand button. -->
+    <!-- The top padding for the notification expand button. -->
     <dimen name="notification_expand_button_padding_top">0dp</dimen>
 
     <!-- The end margin after the application icon in the notification header -->
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/drawable/ic_done_wht.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/drawable/ic_done_wht.xml
new file mode 100644
index 0000000..9e265d4
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/drawable/ic_done_wht.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="64dp"
+    android:height="64dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:pathData="M0 0h48v48H0z" />
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M18 32.34L9.66 24l-2.83 2.83L18 38l24-24-2.83-2.83z" />
+</vector>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..08fe6d0
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<com.android.keyguard.KeyguardPINView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/keyguard_pin_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="@dimen/num_pad_margin_left"
+        android:layout_marginRight="@dimen/num_pad_margin_right">
+
+
+        <GridLayout
+            android:id="@+id/container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|left"
+            android:columnCount="3">
+
+            <!-- Row 1 -->
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key1"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/one" />
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key2"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/two" />
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key3"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/three" />
+
+            <!-- Row 2 -->
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key4"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/four" />
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key5"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/five" />
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key6"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/six" />
+
+            <!-- Row 3 -->
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key7"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/seven" />
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key8"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/eight" />
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key9"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/nine" />
+
+            <!-- Row 4 -->
+            <Space
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            <com.android.keyguard.NumPadKey
+                android:id="@+id/key0"
+                style="@style/NumPadKeyButton"
+                app:digit="@string/zero" />
+            <ImageButton
+                android:id="@+id/delete_button"
+                style="@style/NumPadKeyButton"
+                android:gravity="center_vertical"
+                android:src="@drawable/ic_backspace_black_24dp"
+                android:clickable="true"
+                android:background="@drawable/ripple_drawable"
+                android:contentDescription="@string/keyboardview_keycode_delete"
+                android:layout_alignParentRight="true" />
+        </GridLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|right"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <com.android.keyguard.PasswordTextView
+                android:id="@+id/pinEntry"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/pin_entry_height"
+                android:gravity="center"
+                app:scaledTextSize="@integer/password_text_view_scale"
+                android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+            <View
+                android:id="@+id/divider"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/divider_height"
+                android:background="@android:color/white" />
+
+            <com.android.keyguard.AlphaOptimizedImageButton
+                android:id="@+id/key_enter"
+                android:layout_width="@dimen/num_pad_key_width"
+                android:layout_height="@dimen/num_pad_key_height"
+                android:src="@drawable/ic_done_wht"
+                android:background="@drawable/ripple_drawable"
+                android:layout_marginTop="@dimen/key_enter_margin_top"
+                android:contentDescription="@string/keyboardview_keycode_enter" />
+
+            <include layout="@layout/keyguard_message_area"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"/>
+        </LinearLayout>
+    </FrameLayout>
+
+    <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+         keyguard to crash. Instead put them down here where they are out of the way and set their
+         visibility to gone. -->
+    <com.android.keyguard.AlphaOptimizedRelativeLayout
+        android:id="@+id/row0"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row3"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row4"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+
+    <include layout="@layout/keyguard_eca"
+             android:id="@+id/keyguard_selector_fade_container"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:orientation="vertical"
+             android:layout_gravity="bottom|center_horizontal"
+             android:gravity="center_horizontal"
+             android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml
new file mode 100644
index 0000000..f1dbe75
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources>
+    <dimen name="num_pad_margin_left">112dp</dimen>
+    <dimen name="num_pad_margin_right">144dp</dimen>
+    <dimen name="num_pad_key_width">160dp</dimen>
+    <dimen name="num_pad_key_height">128dp</dimen>
+    <dimen name="num_pad_key_margin">32dp</dimen>
+    <dimen name="num_pad_key_digit_size">48sp</dimen>
+    <dimen name="num_pad_key_text_size">24sp</dimen>
+    <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
+    <dimen name="divider_height">1dp</dimen>
+    <dimen name="key_enter_margin_top">128dp</dimen>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/integers.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/integers.xml
new file mode 100644
index 0000000..0408ca4
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/integers.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources>
+    <integer name="password_text_view_scale">72</integer>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/strings.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/strings.xml
new file mode 100644
index 0000000..cabeec2
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="zero">0</string>
+    <string name="one">1</string>
+    <string name="two">2</string>
+    <string name="three">3</string>
+    <string name="four">4</string>
+    <string name="five">5</string>
+    <string name="six">6</string>
+    <string name="seven">7</string>
+    <string name="eight">8</string>
+    <string name="nine">9</string>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml
new file mode 100644
index 0000000..c1e9622
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- The style for the volume icons in the volume dialog. This style makes the icon scale to
+         fit its container since auto wants the icon to be larger. The padding is added to make it
+         so the icon does not press along the edges of the dialog. -->
+    <style name="NumPadKeyButton">
+        <item name="android:layout_width">@dimen/num_pad_key_width</item>
+        <item name="android:layout_height">@dimen/num_pad_key_height</item>
+        <item name="android:layout_margin">@dimen/num_pad_key_margin</item>
+        <item name="textView">@id/pinEntry</item>
+    </style>
+
+    <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
+        <!-- Only replaces the text size. -->
+        <item name="android:textSize">@dimen/num_pad_key_digit_size</item>
+    </style>
+
+    <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey">
+        <!-- Only replaces the text size. -->
+        <item name="android:textSize">@dimen/num_pad_key_text_size</item>
+    </style>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_thumb.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_thumb.xml
new file mode 100644
index 0000000..a29ddc8
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_thumb.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="@color/car_teal_700" />
+    <size
+        android:width="30dp"
+        android:height="30dp" />
+    <stroke
+        android:width="1dp"
+        android:color="@color/car_teal_700" />
+</shape>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_track.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_track.xml
new file mode 100644
index 0000000..2b42d9d
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_track.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Use android provided id, as seekbar is expecting that -->
+    <item android:id="@android:id/background">
+        <shape android:shape="line">
+            <corners
+                android:radius="3dp"/>
+            <stroke
+                android:width="3dp"
+                android:color="@color/car_grey_300" />
+        </shape>
+    </item>
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape android:shape="line">
+                <stroke
+                    android:width="6dp"
+                    android:color="@color/car_teal_700" />
+            </shape>
+        </clip>
+    </item>
+</layer-list>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml
new file mode 100644
index 0000000..039833b
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/notification_ripple_untinted_color">
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android">
+          <solid android:color="@color/notification_material_background_color" />
+          <corners
+              android:radius="@dimen/notification_shadow_radius"/>
+      </shape>
+    </item>
+</ripple>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
new file mode 100644
index 0000000..90c793f
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/notification_ripple_untinted_color">
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android">
+          <solid android:color="@color/notification_material_background_color" />
+          <corners
+              android:radius="@dimen/notification_shadow_radius"/>
+      </shape>
+    </item>
+</ripple>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/remote_input_bg.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/remote_input_bg.xml
new file mode 100644
index 0000000..3120679
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/remote_input_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#ff6c6c6c" />
+    <corners
+        android:bottomRightRadius="16dp"
+        android:bottomLeftRadius="16dp"/>
+</shape>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/volume_dialog_background.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/volume_dialog_background.xml
new file mode 100644
index 0000000..345692f
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <padding
+        android:bottom="5dp"
+        android:left="5dp"
+        android:right="5dp"
+        android:top="5dp" />
+    <corners android:bottomLeftRadius="20dp"
+             android:bottomRightRadius="20dp"/>
+</shape>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/layout/volume_dialog_row.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/layout/volume_dialog_row.xml
new file mode 100644
index 0000000..069936c
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/volume_row_height"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/volume_row_padding_bottom" >
+
+    <!-- don't need this view in car, the visibility is overriden by system UI, so set the
+    layout_height to 0dp.
+    TODO: figure out a better way to switch UI based on FEATURE_AUTOMOTIVE -->
+    <TextView
+        android:id="@+id/volume_row_header"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:ellipsize="end"
+        android:visibility="gone"
+        android:maxLines="1"
+        android:textSize="40sp"
+        android:textColor="?android:attr/textColorSecondary"
+        android:paddingStart="@dimen/volume_row_header_padding_start" />
+
+    <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/line_item_height">
+        <com.android.keyguard.AlphaOptimizedImageButton
+                android:id="@+id/volume_row_icon"
+                style="@style/VolumeButtons"
+                android:tint="@color/car_grey_900"
+                android:gravity="center_vertical"
+                android:layout_gravity="center_vertical"
+                android:layout_width="@dimen/volume_icon_size"
+                android:layout_height="@dimen/volume_icon_size"
+                android:layout_marginStart="@dimen/car_keyline_1"
+                android:soundEffectsEnabled="false" />
+
+        <SeekBar
+                android:id="@+id/volume_row_slider"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="@dimen/car_keyline_3"
+                android:progressDrawable="@drawable/car_ic_seekbar_track"
+                android:thumb="@drawable/car_ic_seekbar_thumb"
+                android:focusable="true"
+                android:focusableInTouchMode="true"/>
+    </FrameLayout>
+</LinearLayout>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
index be66b5b..26479a5 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
@@ -19,4 +19,7 @@
 <resources>
     <color name="status_bar_background_color">#ff000000</color>
     <color name="system_bar_background_opaque">#ff0c1013</color>
+
+    <!-- The color of the ripples on the untinted notifications -->
+    <color name="notification_ripple_untinted_color">@color/ripple_material_dark</color>
 </resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml
index 9208135..94a6d45 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml
@@ -33,7 +33,7 @@
         <!-- Launch the lenspicker for all the facets. The lens picker will trampoline into the last run app or display a list of valid apps -->
         <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
         <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
-        <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;S.system_command=show_notifications;end</item>
+        <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;S.system_command=toggle_notifications;end</item>
         <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
         <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
     </array>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
index b66ff92..54d926f 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2016, The Android Open Source Project
+** Copyright 2017, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -31,4 +31,11 @@
 
     <!-- The color of the dividing line between grouped notifications. -->
     <color name="notification_divider_color">@*android:color/notification_action_list</color>
+
+    <!-- The color of the ripples on the untinted notifications -->
+    <color name="notification_ripple_untinted_color">@color/ripple_material_light</color>
+
+    <color name="car_teal_700">#ff00796b</color>
+    <color name="car_grey_300">#ffe0e0e0</color>
+    <color name="car_grey_900">#ff212121</color>
 </resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
index a9f231c..e48a06a 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
@@ -30,22 +30,6 @@
     <!-- No need to draw a background around a notification because there is no gear icon. -->
     <bool name="config_drawNotificationBackground">false</bool>
 
-    <!-- No quick settings the quick settings row should not be shown.-->
-    <bool name="config_showQuickSettingsRow">false</bool>
-
-    <!-- The quick settings are not available on the car and should not be editable. -->
-    <bool name="config_showQuickSettingsEditingIcon">false</bool>
-
-    <!-- The multi-user switcher should always be visible because quick settings cannot be
-         expanded. Thus, there is no other way to access this. -->
-    <bool name="config_alwaysShowMultiUserSwitcher">true</bool>
-
-    <!-- The quick settings should not be available for expansion in the car. -->
-    <bool name="config_showQuickSettingsExpandIndicator">false</bool>
-
-    <!-- There are no quick settings, so it should not be revealed with scrolling. -->
-    <bool name="config_enableQuickSettingsOverscrollExpansion">false</bool>
-
     <!-- The notification shade should only be shown on a facet click and not by dragging. -->
     <bool name="config_enableNotificationShadeDrag">false</bool>
 
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
index 5d675fe..047a6b7 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
@@ -82,19 +82,19 @@
     <dimen name="qs_panel_width">-1px</dimen>
 
     <!-- Height of a small notification in the status bar-->
-    <dimen name="notification_min_height">600dp</dimen>
+    <dimen name="notification_min_height">192dp</dimen>
 
     <!-- Height of a small notification in the status bar which was used before android N -->
-    <dimen name="notification_min_height_legacy">600dp</dimen>
+    <dimen name="notification_min_height_legacy">192dp</dimen>
 
     <!-- Height of a large notification in the status bar -->
-    <dimen name="notification_max_height">600dp</dimen>
+    <dimen name="notification_max_height">400dp</dimen>
 
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
-    <dimen name="notification_max_heads_up_height_legacy">600dp</dimen>
+    <dimen name="notification_max_heads_up_height_legacy">400dp</dimen>
 
     <!-- Height of a heads up notification in the status bar -->
-    <dimen name="notification_max_heads_up_height">600dp</dimen>
+    <dimen name="notification_max_heads_up_height">400dp</dimen>
 
     <!-- Height of the status bar header bar -->
     <dimen name="status_bar_header_height">54dp</dimen>
@@ -136,4 +136,23 @@
          value is smaller than notification_children_container_header_height to bring the first
          child closer so there is less wasted space. -->
     <dimen name="notification_children_container_margin_top">68dp</dimen>
+
+    <!-- The height of the quick settings footer that holds the user switcher, settings icon,
+         etc. in the car setting.-->
+    <dimen name="qs_footer_height">74dp</dimen>
+
+    <dimen name="volume_dialog_side_margin">@dimen/side_margin</dimen>
+
+    <dimen name="volume_dialog_elevation">6dp</dimen>
+
+    <dimen name="volume_dialog_row_margin_end">@dimen/car_keyline_3</dimen>
+
+    <dimen name="volume_dialog_row_padding_end">0dp</dimen>
+
+    <dimen name="line_item_height">128dp</dimen>
+    <dimen name="volume_icon_size">96dp</dimen>
+    <dimen name="side_margin">148dp</dimen>
+    <dimen name="car_keyline_1">24dp</dimen>
+    <dimen name="car_keyline_2">96dp</dimen>
+    <dimen name="car_keyline_3">128dp</dimen>
 </resources>
diff --git a/car_product/overlay/packages/services/Telecomm/res/values/config.xml b/car_product/overlay/packages/services/Telecomm/res/values/config.xml
index 33139ac..b19ded4 100644
--- a/car_product/overlay/packages/services/Telecomm/res/values/config.xml
+++ b/car_product/overlay/packages/services/Telecomm/res/values/config.xml
@@ -31,7 +31,7 @@
     <string name="ui_default_package" translatable="false">com.android.car.dialer</string>
 
     <!-- Class name for the default in-call UI Service [DO NOT TRANSLATE] -->
-    <string name="incall_default_class" translatable="false">com.android.car.dialer.telecom.embedded.InCallServiceImpl</string>
+    <string name="incall_default_class" translatable="false">com.android.car.dialer.telecom.InCallServiceImpl</string>
 
     <!-- Class name for the default main dialer activity [DO NOT TRANSLATE] -->
     <string name="dialer_default_class" translatable="false">com.android.car.dialer.TelecomActivity</string>
diff --git a/car_product/sepolicy/evs_app.te b/car_product/sepolicy/evs_app.te
deleted file mode 100644
index 0e8881e..0000000
--- a/car_product/sepolicy/evs_app.te
+++ /dev/null
@@ -1,14 +0,0 @@
-# evs app
-type evs_app, domain;
-type evs_app_exec, exec_type, file_type;
-
-allow evs_app evs_app_exec:dir search;
-allow evs_app evs_driver:binder call;
-allow evs_app evs_mock:binder call;
-allow evs_app gpu_device:chr_file ioctl;
-allow evs_app hal_graphics_allocator_default:fd use;
-allow evs_app hal_vehicle_default:binder call;
-
-init_daemon_domain(evs_app)
-
-binder_use(evs_app);
diff --git a/car_product/sepolicy/evs_driver.te b/car_product/sepolicy/evs_driver.te
deleted file mode 100644
index 1307616..0000000
--- a/car_product/sepolicy/evs_driver.te
+++ /dev/null
@@ -1,12 +0,0 @@
-# evs_driver mock hardware driver service
-type evs_driver, domain;
-type evs_driver_exec, exec_type, file_type;
-
-allow evs_driver hwservicemanager:binder { call transfer };
-allow evs_driver hwservicemanager_prop:file { getattr open read };
-allow evs_driver device:dir { open read };
-allow evs_driver surfaceflinger:binder call;
-
-init_daemon_domain(evs_driver)
-
-binder_use(evs_driver);
diff --git a/car_product/sepolicy/evs_manager.te b/car_product/sepolicy/evs_manager.te
deleted file mode 100644
index f5c4ba8..0000000
--- a/car_product/sepolicy/evs_manager.te
+++ /dev/null
@@ -1,11 +0,0 @@
-# evs manager
-type evs_manager, domain;
-type evs_manager_exec, exec_type, file_type;
-
-allow evs_manager hwservicemanager:binder { call transfer };
-allow evs_manager hwservicemanager_prop:file { getattr open read };
-allow evs_manager evs_driver:binder call;
-
-init_daemon_domain(evs_manager)
-
-binder_use(evs_manager);
diff --git a/car_product/sepolicy/evs_mock.te b/car_product/sepolicy/evs_mock.te
deleted file mode 100644
index b89b1ba..0000000
--- a/car_product/sepolicy/evs_mock.te
+++ /dev/null
@@ -1,11 +0,0 @@
-# evs_mock mock hardware driver service
-type evs_mock, domain;
-type evs_mock_exec, exec_type, file_type;
-
-allow evs_mock hwservicemanager:binder { call transfer };
-allow evs_mock hwservicemanager_prop:file { getattr open read };
-allow evs_mock hal_graphics_allocator_default:fd use;
-
-init_daemon_domain(evs_mock)
-
-binder_use(evs_mock);
diff --git a/car_product/sepolicy/file_contexts b/car_product/sepolicy/file_contexts
index 53759c7..3705765 100644
--- a/car_product/sepolicy/file_contexts
+++ b/car_product/sepolicy/file_contexts
@@ -7,10 +7,4 @@
 /(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.vehicle@2\.0-service  u:object_r:hal_vehicle_default_exec:s0
 /(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.vehicle@2\.1-service  u:object_r:hal_vehicle_default_exec:s0
 
-/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.evs@1\.0-service  u:object_r:evs_mock_exec:s0
-/system/bin/android\.hardware\.automotive\.evs@1\.0-sample   u:object_r:evs_driver_exec:s0
-/system/bin/android\.automotive\.evs\.manager@1\.0           u:object_r:evs_manager_exec:s0
-/system/bin/evs_app                                          u:object_r:evs_app_exec:s0
-/system/etc/automotive/evs(/.*)?                             u:object_r:evs_app_exec:s0
-
 ###################################
diff --git a/car_product/sepolicy/priv_app.te b/car_product/sepolicy/priv_app.te
index de6077e..e6b5292 100644
--- a/car_product/sepolicy/priv_app.te
+++ b/car_product/sepolicy/priv_app.te
@@ -1,3 +1 @@
 hal_client_domain(priv_app, hal_vehicle)
-
-get_prop(priv_app, opengles_prop)
diff --git a/car_product/sepolicy/property.te b/car_product/sepolicy/property.te
index 0f4e53f..64340e7 100644
--- a/car_product/sepolicy/property.te
+++ b/car_product/sepolicy/property.te
@@ -1,6 +1,3 @@
 type hw_cabl_prop, property_type;
 type wlan_driver_prop, property_type;
-
-type opengles_prop, property_type;
-
 type car_prop, property_type;
diff --git a/car_product/sepolicy/property_contexts b/car_product/sepolicy/property_contexts
index 67b90f6..3b680cb 100644
--- a/car_product/sepolicy/property_contexts
+++ b/car_product/sepolicy/property_contexts
@@ -2,4 +2,3 @@
 wlan.driver.               u:object_r:wlan_driver_prop:s0
 
 boot.car_service_created u:object_r:car_prop:s0
-ro.opengles.            u:object_r:opengles_prop:s0
\ No newline at end of file
diff --git a/car_product/sepolicy/system_server.te b/car_product/sepolicy/system_server.te
index 4e0da64..b6785c1 100644
--- a/car_product/sepolicy/system_server.te
+++ b/car_product/sepolicy/system_server.te
@@ -1,6 +1,4 @@
 # Set wlan.driver.* properties.
 set_prop(system_server, wlan_driver_prop)
 
-get_prop(system_server opengles_prop)
-
 dontaudit system_server self:capability sys_module;
diff --git a/evs/app/Android.mk b/evs/app/Android.mk
index f5d9b6e..2b15865 100644
--- a/evs/app/Android.mk
+++ b/evs/app/Android.mk
@@ -44,7 +44,8 @@
 LOCAL_MODULE:= evs_app
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -DLOG_TAG=\"EVSAPP\"
+LOCAL_CFLAGS += -DLOG_TAG=\"EvsApp\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
 include $(BUILD_EXECUTABLE)
diff --git a/evs/app/EvsStateControl.cpp b/evs/app/EvsStateControl.cpp
index 79533f7..1a925e6 100644
--- a/evs/app/EvsStateControl.cpp
+++ b/evs/app/EvsStateControl.cpp
@@ -203,8 +203,6 @@
 
 
 bool EvsStateControl::selectStateForCurrentConditions() {
-    ALOGV("selectStateForCurrentConditions");
-
     static int32_t sDummyGear   = int32_t(VehicleGear::GEAR_REVERSE);
     static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
 
@@ -214,9 +212,10 @@
             ALOGE("GEAR_SELECTION not available from vehicle.  Exiting.");
             return false;
         }
-        if (invokeGet(&mTurnSignalValue) != StatusCode::OK) {
+        if ((mTurnSignalValue.prop == 0) || (invokeGet(&mTurnSignalValue) != StatusCode::OK)) {
             // Silently treat missing turn signal state as no turn signal active
             mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
+            mTurnSignalValue.prop = 0;
         }
     } else {
         // While testing without a vehicle, behave as if we're in reverse for the first 20 seconds
@@ -236,7 +235,7 @@
     }
 
     // Choose our desired EVS state based on the current car state
-    // TODO:  Update this logic, and include user input when choosing if a view should be presented
+    // TODO:  Update this logic, and consider user input when choosing if a view should be presented
     State desiredState = OFF;
     if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
         desiredState = REVERSE;
@@ -248,16 +247,12 @@
         desiredState = PARKING;
     }
 
-    ALOGD("Selected state %d.", desiredState);
-
     // Apply the desire state
     return configureEvsPipeline(desiredState);
 }
 
 
 StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
-    ALOGV("invokeGet");
-
     StatusCode status = StatusCode::TRY_AGAIN;
 
     // Call the Vehicle HAL, which will block until the callback is complete
@@ -265,7 +260,9 @@
                   [pRequestedPropValue, &status]
                   (StatusCode s, const VehiclePropValue& v) {
                        status = s;
-                       *pRequestedPropValue = v;
+                       if (s == StatusCode::OK) {
+                           *pRequestedPropValue = v;
+                       }
                   }
     );
 
@@ -274,13 +271,12 @@
 
 
 bool EvsStateControl::configureEvsPipeline(State desiredState) {
-    ALOGV("configureEvsPipeline");
-
     if (mCurrentState == desiredState) {
         // Nothing to do here...
         return true;
     }
 
+    ALOGD("Switching to state %d.", desiredState);
     ALOGD("  Current state %d has %zu cameras", mCurrentState,
           mCameraList[mCurrentState].size());
     ALOGD("  Desired state %d has %zu cameras", desiredState,
diff --git a/evs/app/StreamHandler.cpp b/evs/app/StreamHandler.cpp
index 5477642..28da6df 100644
--- a/evs/app/StreamHandler.cpp
+++ b/evs/app/StreamHandler.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EVSAPP"
-
 #include "StreamHandler.h"
 
 #include <stdio.h>
diff --git a/evs/app/config.json.readme b/evs/app/config.json.readme
new file mode 100644
index 0000000..982e3c9
--- /dev/null
+++ b/evs/app/config.json.readme
@@ -0,0 +1,42 @@
+// With comments included, this file is no longer legal JSON, but serves to illustrate
+// the format of the configuration file the evs_app expects to read at startup to configure itself
+// for a specific car.
+// In addition to the configuration file, an image to be used to represent the car is expected
+// to be provided in CarFromTop.png.
+// Throughout this file, units of length are arbitrary, but must all be the same units.
+// X is right, Y is forward, Z is up (right handed coordinate system).
+// The origin is at the center of the read axel at ground level.
+// Units for angles are in degrees.
+// Yaw is measured from the front of the car, positive to the left (postive Z rotation).
+// Pitch is measured from the horizon, positive upward (postive X rotation).
+// Roll is always assumed to be zero.
+
+{
+  "car" : {                     // This section describes the geometry of the car
+    "width"  : 76.7,            // The width of the car body
+    "wheelBase" : 117.9,        // The distance between the front and read axel
+    "frontExtent" : 44.7,       // The extent of the car body ahead of the front axel
+    "rearExtent" : 40           // The extent of the car body behind the read axel
+  },
+  "display" : {                 // This configures the dimensions of the surround view display
+    "frontRange" : 100,         // How far to render the view in front of the front bumper
+    "rearRange" : 100           // How far the view extends behind the rear bumper
+  },
+  "graphic" : {                 // This maps the car texture into the projected view space
+    "frontPixel" : 23,          // The pixel row in CarFromTop.png at which the front bumper appears
+    "rearPixel" : 223           // The pixel row in CarFromTop.png at which the back bumper ends
+  },
+  "cameras" : [                 // This describes the cameras potentially available on the car
+    {
+      "cameraId" : "/dev/video32",  // Camera ID exposed by EVS HAL
+      "function" : "reverse,park",  // set of modes to which this camera contributes
+      "x" : 0.0,                    // Optical center distance right of vehicle center
+      "y" : -40.0,                  // Optical center distance forward of rear axel
+      "z" : 48,                     // Optical center distance above ground
+      "yaw" : 180,                  // Optical axis degrees to the left of straight ahead
+      "pitch" : -30,                // Optical axis degrees above the horizon
+      "hfov" : 125,                 // Horizontal field of view in degrees
+      "vfov" :103                   // Vertical field of view in degrees
+    }
+  ]
+}
diff --git a/evs/app/evs_app.cpp b/evs/app/evs_app.cpp
index 5f8d64f..618e8cc 100644
--- a/evs/app/evs_app.cpp
+++ b/evs/app/evs_app.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EVSAPP"
-
 #include <stdio.h>
 
 #include <hidl/HidlTransportSupport.h>
diff --git a/evs/manager/Android.mk b/evs/manager/Android.mk
index fe384d9..375a84c 100644
--- a/evs/manager/Android.mk
+++ b/evs/manager/Android.mk
@@ -28,6 +28,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_STRIP_MODULE := keep_symbols
 
+LOCAL_CFLAGS += -DLOG_TAG=\"EvsManager\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
diff --git a/evs/manager/Enumerator.cpp b/evs/manager/Enumerator.cpp
index 7bfe6f0..7e53304 100644
--- a/evs/manager/Enumerator.cpp
+++ b/evs/manager/Enumerator.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EvsManager"
-
 #include "Enumerator.h"
 
 namespace android {
diff --git a/evs/manager/HalCamera.cpp b/evs/manager/HalCamera.cpp
index 9e33717..41cff24 100644
--- a/evs/manager/HalCamera.cpp
+++ b/evs/manager/HalCamera.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EvsManager"
-
 #include "HalCamera.h"
 #include "VirtualCamera.h"
 #include "Enumerator.h"
diff --git a/evs/manager/VirtualCamera.cpp b/evs/manager/VirtualCamera.cpp
index 652caf6..af90f2c 100644
--- a/evs/manager/VirtualCamera.cpp
+++ b/evs/manager/VirtualCamera.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EvsManager"
-
 #include "VirtualCamera.h"
 #include "HalCamera.h"
 #include "Enumerator.h"
diff --git a/evs/manager/service.cpp b/evs/manager/service.cpp
index fe1136b..f9e45eb 100644
--- a/evs/manager/service.cpp
+++ b/evs/manager/service.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EvsManager"
-
 #include <unistd.h>
 
 #include <hidl/HidlTransportSupport.h>
diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 231ab6e..c4463a9 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -35,6 +35,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_STRIP_MODULE := keep_symbols
 
+LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
diff --git a/evs/sampleDriver/EvsEnumerator.cpp b/evs/sampleDriver/EvsEnumerator.cpp
index 25b8133..d31f672 100644
--- a/evs/sampleDriver/EvsEnumerator.cpp
+++ b/evs/sampleDriver/EvsEnumerator.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
 #include "EvsEnumerator.h"
 #include "EvsV4lCamera.h"
 #include "EvsGlDisplay.h"
diff --git a/evs/sampleDriver/EvsGlDisplay.cpp b/evs/sampleDriver/EvsGlDisplay.cpp
index 0f62e64..26d4c1d 100644
--- a/evs/sampleDriver/EvsGlDisplay.cpp
+++ b/evs/sampleDriver/EvsGlDisplay.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
 #include "EvsGlDisplay.h"
 
 #include <ui/GraphicBufferAllocator.h>
diff --git a/evs/sampleDriver/EvsV4lCamera.cpp b/evs/sampleDriver/EvsV4lCamera.cpp
index 045d7ab..f811a1f 100644
--- a/evs/sampleDriver/EvsV4lCamera.cpp
+++ b/evs/sampleDriver/EvsV4lCamera.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
 #include "EvsV4lCamera.h"
 #include "EvsEnumerator.h"
 #include "bufferCopy.h"
diff --git a/evs/sampleDriver/GlWrapper.cpp b/evs/sampleDriver/GlWrapper.cpp
index a49eb20..eb45bc9 100644
--- a/evs/sampleDriver/GlWrapper.cpp
+++ b/evs/sampleDriver/GlWrapper.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
 #include "GlWrapper.h"
 
 #include <stdio.h>
diff --git a/evs/sampleDriver/VideoCapture.cpp b/evs/sampleDriver/VideoCapture.cpp
index 2122a2c..d216b29 100644
--- a/evs/sampleDriver/VideoCapture.cpp
+++ b/evs/sampleDriver/VideoCapture.cpp
@@ -13,8 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <error.h>
diff --git a/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
index 81fe33a..7b11cf5 100644
--- a/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
+++ b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
@@ -2,5 +2,5 @@
     class hal
     priority -20
     user graphics
-    group automotive_evs
+    group automotive_evs camera
     onrestart restart evs_manager
diff --git a/evs/sampleDriver/bufferCopy.cpp b/evs/sampleDriver/bufferCopy.cpp
index a585040..6c8c3ef 100644
--- a/evs/sampleDriver/bufferCopy.cpp
+++ b/evs/sampleDriver/bufferCopy.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
 #include "bufferCopy.h"
 
 
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d73f758..41be742 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
 #include <unistd.h>
 
 #include <hidl/HidlTransportSupport.h>
diff --git a/evs/sepolicy/attributes b/evs/sepolicy/attributes
new file mode 100644
index 0000000..d31005b
--- /dev/null
+++ b/evs/sepolicy/attributes
@@ -0,0 +1,4 @@
+# EVS specific attributes
+attribute hal_evs;
+attribute hal_evs_client;
+attribute hal_evs_server;
diff --git a/evs/sepolicy/evs_app.te b/evs/sepolicy/evs_app.te
new file mode 100644
index 0000000..28e71a9
--- /dev/null
+++ b/evs/sepolicy/evs_app.te
@@ -0,0 +1,22 @@
+# evs app
+type evs_app, domain;
+hal_client_domain(evs_app, hal_evs)
+hal_client_domain(evs_app, hal_vehicle)
+
+# allow init to launch processes in this context
+type evs_app_exec, exec_type, file_type;
+init_daemon_domain(evs_app)
+
+# gets access to its own files on disk
+type evs_app_files, file_type;
+allow evs_app evs_app_files:file { getattr open read };
+allow evs_app evs_app_files:dir search;
+
+# Allow use of gralloc buffers and EGL
+allow evs_app hal_graphics_allocator_default:fd use;
+allow evs_app gpu_device:chr_file ioctl;
+allow evs_app gpu_device:chr_file { getattr open read write };
+
+# Permit communication with the vehicle HAL
+# (Communcations with the rest of the EVS stack is allowed via hal_evs)
+binder_call(evs_app, hal_vehicle);
\ No newline at end of file
diff --git a/evs/sepolicy/evs_default.te b/evs/sepolicy/evs_default.te
new file mode 100644
index 0000000..c90e34b
--- /dev/null
+++ b/evs/sepolicy/evs_default.te
@@ -0,0 +1,7 @@
+# evs_mock mock hardware driver service
+type hal_evs_default, domain;
+hal_server_domain(hal_evs_default, hal_evs)
+
+# allow init to launch processes in this context
+type hal_evs_default_exec, exec_type, file_type;
+init_daemon_domain(hal_evs_default)
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
new file mode 100644
index 0000000..2226dd6
--- /dev/null
+++ b/evs/sepolicy/evs_driver.te
@@ -0,0 +1,20 @@
+# evs_mock mock hardware driver service
+type hal_evs_driver, domain;
+hal_server_domain(hal_evs_driver, hal_evs)
+
+# allow init to launch processes in this context
+type hal_evs_driver_exec, exec_type, file_type;
+init_daemon_domain(hal_evs_driver)
+
+# Allow use of USB devices, gralloc buffers, and surface flinger
+allow hal_evs_driver device:dir { open read };
+allow hal_evs_driver video_device:chr_file { ioctl open read write };
+hal_client_domain(hal_evs_driver, hal_graphics_allocator);
+
+allow hal_evs_driver gpu_device:chr_file { getattr ioctl open read write };
+binder_call(hal_evs_driver, surfaceflinger);
+allow hal_evs_driver surfaceflinger_service:service_manager find;
+allow hal_evs_driver hal_graphics_composer_default:fd use;
+allow hal_evs_driver hal_graphics_allocator_default_tmpfs:file { read write };
+allow hal_evs_driver self:capability dac_override;
+allow hal_evs_driver servicemanager:binder call;
diff --git a/evs/sepolicy/evs_manager.te b/evs/sepolicy/evs_manager.te
new file mode 100644
index 0000000..70a2cda
--- /dev/null
+++ b/evs/sepolicy/evs_manager.te
@@ -0,0 +1,8 @@
+# evs manager
+type evs_manager, domain;
+hal_server_domain(evs_manager, hal_evs)
+hal_client_domain(evs_manager, hal_evs)
+
+# allow init to launch processes in this context
+type evs_manager_exec, exec_type, file_type;
+init_daemon_domain(evs_manager)
diff --git a/evs/sepolicy/file_contexts b/evs/sepolicy/file_contexts
new file mode 100644
index 0000000..097a588
--- /dev/null
+++ b/evs/sepolicy/file_contexts
@@ -0,0 +1,12 @@
+
+###################################
+# Binaries associated with the default EVS stack, plus
+# the directory which contains the configuration for the evs_app
+#
+/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.evs@1\.0-service  u:object_r:hal_evs_default_exec:s0
+/system/bin/android\.hardware\.automotive\.evs@1\.0-sample   u:object_r:hal_evs_driver_exec:s0
+/system/bin/android\.automotive\.evs\.manager@1\.0           u:object_r:evs_manager_exec:s0
+/system/bin/evs_app                                          u:object_r:evs_app_exec:s0
+/system/etc/automotive/evs(/.*)?                             u:object_r:evs_app_files:s0
+
+###################################
diff --git a/evs/sepolicy/hal_evs.te b/evs/sepolicy/hal_evs.te
new file mode 100644
index 0000000..525f2be
--- /dev/null
+++ b/evs/sepolicy/hal_evs.te
@@ -0,0 +1,6 @@
+hwbinder_use(hal_evs_client)
+hwbinder_use(hal_evs_server)
+binder_call(hal_evs_client, hal_evs_server)
+binder_call(hal_evs_server, hal_evs_client)
+
+allow hal_evs_server hal_graphics_allocator_default:fd use;
\ No newline at end of file
diff --git a/service/Android.mk b/service/Android.mk
index 9bd5e12..2b926df 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -40,7 +40,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES += \
         android.hidl.base-V1.0-java \
         android.hardware.automotive.vehicle-V2.0-java \
-        android.hardware.automotive.vehicle-V2.1-java \
         vehicle-hal-support-lib \
         car-systemtest \
 
@@ -60,7 +59,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES += \
         android.hidl.base-V1.0-java \
         android.hardware.automotive.vehicle-V2.0-java \
-        android.hardware.automotive.vehicle-V2.1-java \
         vehicle-hal-support-lib \
         car-systemtest \
 
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 175a8bb..5c4dfaa 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -61,6 +61,12 @@
         android:label="@string/car_permission_label_speed"
         android:description="@string/car_permission_desc_speed" />
     <permission
+        android:name="android.car.permission.VEHICLE_DYNAMICS_STATE"
+        android:permissionGroup="android.permission-group.CAR_INFORMATION"
+        android:protectionLevel="dangerous"
+        android:label="@string/car_permission_label_vehicle_dynamics_state"
+        android:description="@string/car_permission_desc_vehicle_dynamics_state" />
+    <permission
         android:name="android.car.permission.CAR_VENDOR_EXTENSION"
         android:protectionLevel="system|signature"
         android:label="@string/car_permission_label_vendor_extension"
@@ -86,7 +92,7 @@
         android:label="@string/car_permission_car_navigation_manager"
         android:description="@string/car_permission_desc_car_navigation_manager" />
     <permission
-      android:name="android.car.permission.DIAGNOSTIC_READ"
+      android:name="android.car.permission.DIAGNOSTIC_READ_ALL"
       android:protectionLevel="system|signature"
       android:label="@string/car_permission_label_diag_read"
       android:description="@string/car_permission_desc_diag_read" />
@@ -95,17 +101,6 @@
       android:protectionLevel="system|signature"
       android:label="@string/car_permission_label_diag_clear"
       android:description="@string/car_permission_desc_diag_clear" />
-    <permission
-        android:name="android.car.permission.VMS_PUBLISHER"
-        android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_vms_publisher"
-        android:description="@string/car_permission_desc_vms_publisher" />
-    <permission
-        android:name="android.car.permission.VMS_SUBSCRIBER"
-        android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_vms_subscriber"
-        android:description="@string/car_permission_desc_vms_subscriber" />
-
     <!--  may replace this with system permission if proper one is defined. -->
     <permission
         android:name="android.car.permission.CONTROL_APP_BLOCKING"
@@ -137,6 +132,17 @@
             android:label="@string/car_permission_label_bind_input_service"
             android:description="@string/car_permission_desc_bind_input_service"/>
 
+    <permission
+            android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
+            android:protectionLevel="system|signature"
+            android:label="@string/car_permission_car_display_in_cluster"
+            android:description="@string/car_permission_desc_car_display_in_cluster" />
+
+    <permission android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
+                android:protectionLevel="system|signature"
+                android:label="@string/car_permission_car_cluster_control"
+                android:description="@string/car_permission_desc_car_cluster_control" />
+
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index fd4a841..73028cb 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -58,7 +58,7 @@
 
     <string name="inputService">android.car.input.service/.DefaultInputService</string>
 
-    <string name="instrumentClusterRendererService">android.car.cluster.loggingrenderer/.LoggingClusterRenderingService</string>
+    <string name="instrumentClusterRendererService">android.car.cluster.sample/.SampleClusterServiceImpl</string>
 
     <!--  Whether to enable Avtivity blocking for safety. When Activity blocking is enabled,
           only whitelisted safe Activities will be allowed while car is not parked. -->
@@ -74,11 +74,4 @@
     <string name="defauiltActivityWhitelist">android,com.android.systemui</string>
     <!-- Default home activity -->
     <string name="defaultHomeActivity">com.android.car.overview/com.android.car.overview.StreamOverviewActivity</string>
-    <!--  The com.android.car.VmsPublisherService will bind to this list of clients -->
-    <string-array translatable="false" name="vmsPublisherClients">
-    </string-array>
-    <!--  Permissions that the com.android.car.VmsPublisherService is allowed to grant to publishers -->
-    <string-array translatable="false" name="vmsSafePermissions">
-        <item>"android.permission.ACCESS_FINE_LOCATION"</item>
-    </string-array>
 </resources>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index 1cd53ca..7f826d4 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -44,6 +44,10 @@
     <string name="car_permission_label_speed">Car speed</string>
     <!-- Permission text: can access your car's speed [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc_speed">Access your car\'s speed.</string>
+    <!-- Permission text: can access your car's dynamics state [CHAR LIMIT=NONE] -->
+    <string name="car_permission_label_vehicle_dynamics_state">Vehicle dynamics state</string>
+    <!-- Permission text: can access your car's dynamic state [CHAR LIMIT=NONE] -->
+    <string name="car_permission_desc_vehicle_dynamics_state">Access your car\'s dynamics state</string>
     <!-- Permission text: apps can access car-manufacturer specific data [CHAR LIMIT=NONE] -->
     <string name="car_permission_label_vendor_extension">Car vendor channel</string>
     <!-- Permission text: apps can access car-manufacturer specific data [CHAR LIMIT=NONE] -->
@@ -77,6 +81,11 @@
          cluster [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc_car_navigation_manager">Report navigation data to instrument
         cluster</string>
+    <string name="car_permission_car_display_in_cluster">Direct rendering to instrument cluster</string>
+    <string name="car_permission_desc_car_display_in_cluster">Allow an application to declare
+      activities to be displayed in the instrument cluster</string>
+    <string name="car_permission_car_cluster_control">Instrument cluster control</string>
+    <string name="car_permission_desc_car_cluster_control">Launch apps in the instrument cluster</string>
     <string name="car_permission_label_bind_instrument_cluster_rendering">Instrument Cluster Rendering</string>
     <string name="car_permission_desc_bind_instrument_cluster_rendering">Receive instrument cluster data</string>
 
@@ -107,13 +116,4 @@
     <!-- Permission text: apps can clear diagnostic data from the car [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc_diag_clear">Clear diagnostic data from the car</string>
 
-    <!-- Permission text: apps can publish VMS data [CHAR LIMIT=NONE] -->
-    <string name="car_permission_label_vms_publisher">VMS publisher</string>
-    <!-- Permission text: apps can send VMS messages to the car [CHAR LIMIT=NONE] -->
-    <string name="car_permission_desc_vms_publisher">Publish vms messages</string>
-
-    <!-- Permission text: apps can subscribe to VMS data [CHAR LIMIT=NONE] -->
-    <string name="car_permission_label_vms_subscriber">VMS subscriber</string>
-    <!-- Permission text: apps can receive VMS messages from the car [CHAR LIMIT=NONE] -->
-    <string name="car_permission_desc_vms_subscriber">Subscribe to vms messages</string>
 </resources>
diff --git a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
index e80b843..94cbdb1 100644
--- a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
+++ b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
@@ -16,6 +16,7 @@
 
 package com.android.car;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothA2dpSink;
 import android.bluetooth.BluetoothAdapter;
@@ -39,6 +40,7 @@
 import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES;
 import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES;
 
+import android.car.CarBluetoothManager;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -51,6 +53,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.StringBuilder;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -123,36 +127,47 @@
     // PerUserCarService related listeners
     private final UserServiceConnectionCallback mServiceCallback;
 
+    // Car Bluetooth Priority Settings Manager
+    private final CarBluetoothService mCarBluetoothService;
+
     // The Bluetooth profiles that the CarService will try to auto-connect on.
     private final List<Integer> mProfilesToConnect;
+    private final List<Integer> mPrioritiesSupported;
     private static final int MAX_CONNECT_RETRIES = 1;
-
     private static final int PROFILE_NOT_AVAILABLE = -1;
 
     // Device & Profile currently being connected on
     private ConnectionParams mConnectionInFlight;
+    // Allow write to Settings.Secure
+    private boolean mAllowReadWriteToSettings = true;
 
     public static BluetoothDeviceConnectionPolicy create(Context context,
             CarCabinService carCabinService, CarSensorService carSensorService,
-            PerUserCarServiceHelper userServiceHelper) {
+            PerUserCarServiceHelper userServiceHelper, CarBluetoothService bluetoothService) {
         return new BluetoothDeviceConnectionPolicy(context, carCabinService, carSensorService,
-                userServiceHelper);
+                userServiceHelper, bluetoothService);
     }
 
     private BluetoothDeviceConnectionPolicy(Context context, CarCabinService carCabinService,
-            CarSensorService carSensorService, PerUserCarServiceHelper userServiceHelper) {
+            CarSensorService carSensorService, PerUserCarServiceHelper userServiceHelper,
+            CarBluetoothService bluetoothService) {
         mContext = context;
         mCarCabinService = carCabinService;
         mCarSensorService = carSensorService;
         mUserServiceHelper = userServiceHelper;
+        mCarBluetoothService = bluetoothService;
         mCarUserServiceAccessLock = new ReentrantLock();
         mProfilesToConnect = Arrays.asList(
                 BluetoothProfile.HEADSET_CLIENT, BluetoothProfile.A2DP_SINK,
                 BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_CLIENT);
+        mPrioritiesSupported = Arrays.asList(
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0,
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1
+        );
         // mNumSupportedActiveConnections is a HashMap of mProfilesToConnect and the number of
         // connections each profile supports currently.
         mNumSupportedActiveConnections = new HashMap<>(mProfilesToConnect.size());
-        for (Integer profile: mProfilesToConnect) {
+        for (Integer profile : mProfilesToConnect) {
             switch (profile) {
                 case BluetoothProfile.HEADSET_CLIENT:
                     mNumSupportedActiveConnections.put(BluetoothProfile.HEADSET_CLIENT,
@@ -300,7 +315,7 @@
                 if (uuids != null) {
                     ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
                     for (int i = 0; i < uuidsToSend.length; i++) {
-                        uuidsToSend[i] = (ParcelUuid)uuids[i];
+                        uuidsToSend[i] = (ParcelUuid) uuids[i];
                     }
                     setProfilePriorities(device, uuidsToSend, BluetoothProfile.PRIORITY_ON);
                 }
@@ -383,7 +398,8 @@
      * Cleanup state and reinitialize whenever we connect to the PerUserCarService.
      * This happens in init() and whenever the PerUserCarService is restarted on User Switch Events
      */
-    private class UserServiceConnectionCallback implements PerUserCarServiceHelper.ServiceCallback {
+    @VisibleForTesting
+    class UserServiceConnectionCallback implements PerUserCarServiceHelper.ServiceCallback {
         @Override
         public void onServiceConnected(ICarUserService carUserService) {
             if (mCarUserServiceAccessLock != null) {
@@ -415,11 +431,13 @@
                 }
             } catch (RemoteException e) {
                 Log.e(TAG,
-                        "Remote Exception during closeBluetoothConnectionProxy(): " + e.getMessage());
+                        "Remote Exception during closeBluetoothConnectionProxy(): "
+                                + e.getMessage());
             }
             // Clean up information related to user who went background.
             cleanupUserSpecificInfo();
         }
+
         @Override
         public void onServiceDisconnected() {
             if (DBG) {
@@ -442,6 +460,7 @@
      * which acts as a top level Service running in the current user context.
      * Also sets up the connection proxy objects required to communicate with the Bluetooth
      * Profile Services.
+     *
      * @return ICarBluetoothUserService running in current user
      */
     private ICarBluetoothUserService setupBluetoothUserService() {
@@ -573,7 +592,8 @@
      * Upon receiving the event that is of interest, initiate a connection attempt by calling
      * the policy {@link BluetoothDeviceConnectionPolicy}
      */
-    private class CarPropertyListener extends ICarPropertyEventListener.Stub {
+    @VisibleForTesting
+    class CarPropertyListener extends ICarPropertyEventListener.Stub {
         @Override
         public void onEvent(CarPropertyEvent event) throws RemoteException {
             if (DBG) {
@@ -658,7 +678,9 @@
             mProfileToConnectableDevicesMap = null;
             mConnectionInFlight = null;
             if (mBluetoothBroadcastReceiver != null) {
-                mContext.unregisterReceiver(mBluetoothBroadcastReceiver);
+                if (mContext != null) {
+                    mContext.unregisterReceiver(mBluetoothBroadcastReceiver);
+                }
                 mBluetoothBroadcastReceiver = null;
             }
         }
@@ -695,6 +717,31 @@
         }
     }
 
+    @VisibleForTesting
+    BroadcastReceiver getBluetoothBroadcastReceiver() {
+        return mBluetoothBroadcastReceiver;
+    }
+
+    @VisibleForTesting
+    UserServiceConnectionCallback getServiceCallback() {
+        return mServiceCallback;
+    }
+
+    @VisibleForTesting
+    CarPropertyListener getCarPropertyListener() {
+        return mCabinEventListener;
+    }
+
+    @VisibleForTesting
+    synchronized void setAllowReadWriteToSettings(boolean allowWrite) {
+        mAllowReadWriteToSettings = allowWrite;
+    }
+
+    @VisibleForTesting
+    BluetoothDevicesInfo getBluetoothDevicesInfo(int profile) {
+        return mProfileToConnectableDevicesMap.get(profile);
+    }
+
     /**
      * Resets the {@link #mProfileToConnectableDevicesMap} to a clean and empty slate.
      */
@@ -792,7 +839,6 @@
             Log.w(TAG, "Bluetooth Adapter null");
             return;
         }
-
         if (mBluetoothAdapter.isEnabled()) {
             if (isDeviceMapEmpty()) {
                 if (DBG) {
@@ -1050,7 +1096,7 @@
         BluetoothDevice deviceThatConnected = params.getBluetoothDevice();
         if (DBG) {
             Log.d(TAG, "Profile: " + profileToUpdate + " Connected: " + didConnect + " on "
-                    + deviceThatConnected.getName());
+                    + deviceThatConnected);
         }
 
         // If the connection update is on a different profile or device (a very rare possibility),
@@ -1066,9 +1112,9 @@
 
             if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothDevice() != null) {
                 if (deviceThatConnected.equals(mConnectionInFlight.getBluetoothDevice()) == false) {
-                    Log.d(TAG, "Updating device: " + deviceThatConnected.getName()
+                    Log.d(TAG, "Updating device: " + deviceThatConnected
                             + " different from connection in flight: "
-                            + mConnectionInFlight.getBluetoothDevice().getName());
+                            + mConnectionInFlight.getBluetoothDevice());
 
                 }
             }
@@ -1150,6 +1196,7 @@
     private synchronized void resetDeviceAvailableToConnect() {
         for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
             devInfo.setDeviceAvailableToConnectLocked(true);
+            devInfo.resetDeviceIndex();
         }
     }
 
@@ -1168,12 +1215,13 @@
             writer.print(
                     "Num of Paired devices: " + devInfo.getNumberOfPairedDevicesLocked() + "\t");
             writer.print("Active Connections: " + devInfo.getNumberOfActiveConnectionsLocked());
+            writer.println("Num of paired devices: " + devInfo.getNumberOfPairedDevicesLocked());
             writer.println();
             List<BluetoothDevicesInfo.DeviceInfo> deviceInfoList = devInfo.getDeviceInfoList();
             if (deviceInfoList != null) {
                 for (BluetoothDevicesInfo.DeviceInfo devicesInfo : deviceInfoList) {
                     if (devicesInfo.getBluetoothDevice() != null) {
-                        writer.print(devicesInfo.getBluetoothDevice().getName() + ":");
+                        writer.print(devicesInfo.getBluetoothDevice() + ":");
                         writer.print(devicesInfo.getConnectionState() + "\t");
                     }
                 }
@@ -1212,6 +1260,9 @@
      * @return true if the write was successful, false otherwise
      */
     public synchronized boolean writeDeviceInfoToSettings(ConnectionParams params) {
+        if (!mAllowReadWriteToSettings) {
+            return false;
+        }
         boolean writeSuccess = true;
         Integer profileToUpdate = params.getBluetoothProfile();
 
@@ -1237,8 +1288,9 @@
             // joinedDeviceNames has something like "22:22:33:44:55:AB,22:23:xx:xx:xx:xx"
             // mac addresses of connectable devices separated by a delimiter
             String joinedDeviceNames = sb.toString();
-            Log.d(TAG, "Profile: " + profileToUpdate + " Writing: " + joinedDeviceNames);
-
+            if (DBG) {
+                Log.d(TAG, "Profile: " + profileToUpdate + " Writing: " + joinedDeviceNames);
+            }
             long userId = ActivityManager.getCurrentUser();
             switch (profileToUpdate) {
                 case BluetoothProfile.A2DP_SINK:
@@ -1297,6 +1349,9 @@
                 return false;
             }
         }
+        if (!mAllowReadWriteToSettings) {
+            return false;
+        }
         // Read from Settings.Secure for the current user.  There are 3 keys 1 each for Phone
         // (HFP & PBAP), 1 for Music (A2DP) and 1 for Messaging device (MAP)
         long userId = ActivityManager.getCurrentUser();
@@ -1349,16 +1404,82 @@
                 }
             }
             mProfileToConnectableDevicesMap.put(profile, devicesInfo);
+            // Check to see if there are any  primary or secondary devices for this profile and
+            // update BluetoothDevicesInfo with the priority information.
+            for (int priority : mPrioritiesSupported) {
+                readAndTagDeviceWithPriorityFromSettings(profile, priority);
+            }
         }
         return true;
     }
 
     /**
+     * Read from Secure Settings if there are primary or secondary devices marked for this
+     * Bluetooth profile.  If there are tagged devices, update the BluetoothDevicesInfo so the
+     * policy can prioritize those devices when making connection attempts.
+     *
+     * @param profile - Bluetooth Profile to check
+     * @param priority - Priority to check
+     */
+    private void readAndTagDeviceWithPriorityFromSettings(int profile, int priority) {
+        BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
+        if (devicesInfo == null) {
+            return;
+        }
+        if (!mCarBluetoothService.isPriorityDevicePresent(profile, priority)) {
+            // There is no device for this priority - either it hasn't been set or has been removed.
+            // So check if the policy has a device associated with this priority and remove it.
+            BluetoothDevice deviceToClear = devicesInfo.getBluetoothDeviceForPriorityLocked(
+                    priority);
+            if (deviceToClear != null) {
+                if (DBG) {
+                    Log.d(TAG, "Clearing priority for: " + deviceToClear.getAddress());
+                }
+                devicesInfo.removeBluetoothDevicePriorityLocked(deviceToClear);
+            }
+        } else {
+            // There is a device with the given priority for the given profile.  Update the
+            // policy's records.
+            String deviceName = mCarBluetoothService.getDeviceNameWithPriority(profile,
+                    priority);
+            if (deviceName != null) {
+                BluetoothDevice bluetoothDevice = getBondedDeviceWithGivenName(deviceName);
+                if (bluetoothDevice != null) {
+                    if (DBG) {
+                        Log.d(TAG, "Setting priority: " + priority + " for " + deviceName);
+                    }
+                    tagDeviceWithPriority(bluetoothDevice, profile, priority);
+                }
+            }
+        }
+    }
+
+    /**
+     * Tag a Bluetooth device with priority - Primary or Secondary.  This only updates the policy's
+     * record (BluetoothDevicesInfo) of the priority information.
+     *
+     * @param device   - BluetoothDevice to tag
+     * @param profile  - BluetoothProfile to tag
+     * @param priority - Priority to tag with
+     */
+    @VisibleForTesting
+    void tagDeviceWithPriority(BluetoothDevice device, int profile, int priority) {
+        BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
+        if (device != null) {
+            if (DBG) {
+                Log.d(TAG, "Profile: " + profile + " : " + device + " Priority: " + priority);
+            }
+            devicesInfo.setBluetoothDevicePriorityLocked(device, priority);
+        }
+    }
+
+    /**
      * Given the device name, find the corresponding {@link BluetoothDevice} from the list of
      * Bonded devices.
      *
      * @param name Bluetooth Device name
      */
+    @Nullable
     private BluetoothDevice getBondedDeviceWithGivenName(String name) {
         if (mBluetoothAdapter == null) {
             if (DBG) {
diff --git a/service/src/com/android/car/BluetoothDevicesInfo.java b/service/src/com/android/car/BluetoothDevicesInfo.java
index a383f04..4a0fee9 100644
--- a/service/src/com/android/car/BluetoothDevicesInfo.java
+++ b/service/src/com/android/car/BluetoothDevicesInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothDevice;
+import android.car.CarBluetoothManager;
 import android.util.Log;
 
 import java.util.List;
@@ -47,6 +48,7 @@
     private static final String TAG = "CarBluetoothDevicesInfo";
     private static final boolean DBG = false;
     private final int DEVICE_NOT_FOUND = -1;
+    private final int DEVICE_PRIORITY_UNDEFINED = -1;
     // The device list and the connection state information together have all the information
     // that is required to know which device(s) to connect to, when we need to connect/
     private List<DeviceInfo> mDeviceInfoList;
@@ -98,10 +100,12 @@
 
         private BluetoothDevice mBluetoothDevice;
         private int mConnectionState;
+        private int mDevicePriority;
 
         public DeviceInfo(BluetoothDevice device, int state) {
             mBluetoothDevice = device;
             mConnectionState = state;
+            mDevicePriority = DEVICE_PRIORITY_UNDEFINED;
         }
 
         public void setConnectionState(int state) {
@@ -115,6 +119,14 @@
         public BluetoothDevice getBluetoothDevice() {
             return mBluetoothDevice;
         }
+
+        public void setBluetoothDevicePriority(int priority) {
+            mDevicePriority = priority;
+        }
+
+        public int getBluetoothDevicePriority() {
+            return mDevicePriority;
+        }
     }
 
     public BluetoothDevicesInfo(int profile) {
@@ -128,6 +140,87 @@
     }
 
     /**
+     * Set the priority of the device with the given priority level
+     *
+     * @param deviceToTag - BluetoothDevice to set the priority for
+     * @param priority    - Priority to set
+     */
+
+    public void setBluetoothDevicePriorityLocked(BluetoothDevice deviceToTag, int priority) {
+        /*if (priority >= mConnectionInfo.mNumConnectionsSupported) {
+            if (DBG) {
+                Log.d(TAG, "Priority cannot exceed number of connections supported");
+            }
+            return;
+        }*/
+        // if there is a device already set to that priority, unseat that device
+        BluetoothDevice oldDeviceWithPriority = getBluetoothDeviceForPriorityLocked(priority);
+        if (oldDeviceWithPriority != null) {
+            if (DBG) {
+                Log.d(TAG, "Unsetting priority " + priority + " on " + oldDeviceWithPriority);
+            }
+            removeBluetoothDevicePriorityLocked(oldDeviceWithPriority);
+        }
+        // Tag the new device with the given priority
+        DeviceInfo newDeviceInfoWithPriority = findDeviceInfoInListLocked(deviceToTag);
+        if (newDeviceInfoWithPriority == null) {
+            if (DBG) {
+                Log.d(TAG, "setBluetoothDevicePriorityLocked():Unknown and unpaired device");
+            }
+            return;
+        }
+        if (DBG) {
+            Log.d(TAG, "Setting priority " + priority + " to "
+                    + newDeviceInfoWithPriority.mBluetoothDevice);
+        }
+        newDeviceInfoWithPriority.setBluetoothDevicePriority(priority);
+        // Update the position of the device in the device Queue
+        moveDeviceToPrioritySlotsLocked(newDeviceInfoWithPriority, priority);
+    }
+
+    /**
+     * Clear the priority of the given device.
+     *
+     * @param deviceToUntag - BluetoothDevice to untag
+     */
+    public void removeBluetoothDevicePriorityLocked(BluetoothDevice deviceToUntag) {
+        DeviceInfo deviceInfo = findDeviceInfoInListLocked(deviceToUntag);
+        deviceInfo.setBluetoothDevicePriority(DEVICE_PRIORITY_UNDEFINED);
+    }
+
+    /**
+     * Returns the number of devices that have been tagged as priority devices.
+     * If there is a device that is tagged as a Secondary device, then the number of tagged devices
+     * is 2, even if there is no primary device.
+     *
+     * @return - Number of Tagged devices Ex: Only Primary - 1, Primary and/or Secondary - 2
+     */
+    public int getNumberOfTaggedDevicesLocked() {
+        int numberOfTaggedDevices = 0;
+        if (getBluetoothDeviceForPriorityLocked(
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1) != null) {
+            return CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1 + 1;
+        } else if (getBluetoothDeviceForPriorityLocked(
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) != null) {
+            return CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0 + 1;
+        }
+        return numberOfTaggedDevices;
+    }
+
+    /**
+     * Returns the device that has the passed priority
+     */
+    public BluetoothDevice getBluetoothDeviceForPriorityLocked(int priority) {
+        BluetoothDevice device = null;
+        for (DeviceInfo deviceInfo : mDeviceInfoList) {
+            if (deviceInfo.mDevicePriority == priority) {
+                return deviceInfo.mBluetoothDevice;
+            }
+        }
+        return device;
+    }
+
+    /**
      * Get the position of the given device in the list of connectable devices for this profile.
      *
      * @param device - {@link BluetoothDevice}
@@ -169,6 +262,13 @@
         return isPresent;
     }
 
+    /**
+     * Iterate through the {@link BluetoothDevicesInfo#mDeviceInfoList} and find the
+     * {@link DeviceInfo} with the given {@link BluetoothDevice}
+     *
+     * @param device - {@link BluetoothDevice} to look for
+     * @return - {@link DeviceInfo} that contains the passed {@link BluetoothDevice}
+     */
     private DeviceInfo findDeviceInfoInListLocked(@Nullable BluetoothDevice device) {
         if (device == null) {
             return null;
@@ -180,6 +280,7 @@
         }
         return null;
     }
+
     /**
      * Get the current list of connectable devices for this profile.
      *
@@ -261,10 +362,10 @@
      * @param device - device to get the bluetooth connection state for
      * @return - Connection State.  If passed device is null, returns DEVICE_NOT_FOUND.
      */
-    public int getConnectionStateLocked(BluetoothDevice device) {
+    public int getCurrentConnectionStateLocked(BluetoothDevice device) {
         int state = DEVICE_NOT_FOUND;
         if (device == null) {
-            Log.e(TAG, "getConnectionStateLocked() device null");
+            Log.e(TAG, "getCurrentConnectionStateLocked() device null");
             return state;
         }
 
@@ -282,6 +383,39 @@
     }
 
     /**
+     * Returns the device that is currently in the middle of a connection attempt.
+     *
+     * @return BluetoothDevice that is connecting, null if no device is connecting
+     */
+    public BluetoothDevice getConnectingDeviceLocked() {
+        for (DeviceInfo devInfo : mDeviceInfoList) {
+            if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTING) {
+                return devInfo.getBluetoothDevice();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a list of connected devices for this profile.
+     *
+     * @return - List of connected devices
+     */
+    public List<BluetoothDevice> getConnectedDevicesLocked() {
+        List<BluetoothDevice> devices = new ArrayList<>();
+        for (DeviceInfo devInfo : mDeviceInfoList) {
+            if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
+                devices.add(devInfo.getBluetoothDevice());
+            }
+        }
+        if (DBG) {
+            Log.d(TAG, "Active Connections: " + getNumberOfActiveConnectionsLocked());
+            Log.d(TAG, "Connected devices Size: " + devices.size());
+        }
+        return devices;
+    }
+
+    /**
      * Remove a device from the list.  Used when a device is unpaired
      *
      * @param dev - device to remove from the list.
@@ -322,6 +456,13 @@
     public BluetoothDevice getNextDeviceInQueueLocked() {
         BluetoothDevice device = null;
         int numberOfPairedDevices = getNumberOfPairedDevicesLocked();
+        // iterate till we find a disconnected device
+        while (mConnectionInfo.mDeviceIndex < numberOfPairedDevices &&
+                mDeviceInfoList.get(mConnectionInfo.mDeviceIndex).getConnectionState()
+                        != BluetoothProfile.STATE_DISCONNECTED) {
+            mConnectionInfo.mDeviceIndex++;
+        }
+
         if (mConnectionInfo.mDeviceIndex >= numberOfPairedDevices) {
             if (DBG) {
                 Log.d(TAG,
@@ -345,6 +486,7 @@
             mConnectionInfo.mDeviceIndex = 0;
             return null;
         }
+
         device = mDeviceInfoList.get(mConnectionInfo.mDeviceIndex).mBluetoothDevice;
         if (DBG) {
             Log.d(TAG, "Getting device " + mConnectionInfo.mDeviceIndex + " from list: "
@@ -373,7 +515,6 @@
             if (DBG) {
                 Log.d(TAG, mConnectionInfo.mProfile + " connected to " + device);
             }
-            // b/34722344 - TODO
             // Get the position of this device in the device list maintained for this profile.
             int positionInQ = getPositionInListLocked(device);
             if (DBG) {
@@ -386,13 +527,15 @@
                 addDeviceLocked(device);
                 positionInQ = mDeviceInfoList.size() - 1;
             } else if (positionInQ != mConnectionInfo.mDeviceIndex) {
-                // This will happen if auto-connect request a connect on a device from its list,
-                // but the device that connected was different.  Maybe there was another requestor
-                // and the Bluetooth services chose to honor the other request.  What we do here,
-                // is to make sure we note which device connected and not assume that the device
-                // that connected is the device we requested.  The ultimate goal of the policy is
-                // to remember which devices connected on which profile (regardless of the origin
-                // of the connection request) so it knows which device to connect the next time.
+            /*
+                 This will happen if auto-connect requests to connect on a device from its list,
+                 but the device that connected was different.  Maybe there was another requestor
+                 and the Bluetooth services chose to honor the other request.  What we do here,
+                 is to make sure we note which device connected and not assume that the device
+                 that connected is the device we requested.  The ultimate goal of the policy is
+                 to remember which devices connected on which profile (regardless of the origin
+                 of the connection request) so it knows which device to connect the next time.
+            */
                 if (DBG) {
                     Log.d(TAG, "Different device connected: " + device + " CurrIndex: "
                             + mConnectionInfo.mDeviceIndex);
@@ -405,15 +548,26 @@
             // to the front of the list, the next time auto connect triggers, this will be the
             // device that the policy will try to connect on for this profile.
             if (positionInQ != 0) {
-                moveToFrontLocked(positionInQ);
+                moveDeviceToQueueFrontLocked(positionInQ);
                 // reset the device Index back to the first in the Queue
                 //mConnectionInfo.mDeviceIndex = 0;
             }
+
+            if (getCurrentConnectionStateLocked(device) != BluetoothProfile.STATE_CONNECTED) {
+                mConnectionInfo.mNumActiveConnections++;
+                if (DBG) {
+                    Log.d(TAG,
+                            "Incrementing Active Connections "
+                                    + mConnectionInfo.mNumActiveConnections);
+                }
+            }
             setConnectionStateLocked(device, BluetoothProfile.STATE_CONNECTED);
             // Reset the retry count
             mConnectionInfo.mRetryAttempt = 0;
-            mConnectionInfo.mNumActiveConnections++;
-            mConnectionInfo.mDeviceIndex++;
+
+            if (getConnectingDeviceLocked() == null) {
+                mConnectionInfo.mDeviceIndex++;
+            }
             if (DBG) {
                 Log.d(TAG,
                         "Profile: " + mConnectionInfo.mProfile + " Number of Active Connections: "
@@ -424,33 +578,72 @@
             if (DBG) {
                 Log.d(TAG, "Connection fail or Disconnected");
             }
-            // only if the given device was already connected decrement this.
-            if (getConnectionStateLocked(device) == BluetoothProfile.STATE_CONNECTED) {
+            // Decrement Number of Active Connections only if the device is already connected.
+            if (getCurrentConnectionStateLocked(device) == BluetoothProfile.STATE_CONNECTED) {
                 mConnectionInfo.mNumActiveConnections--;
+                if (DBG) {
+                    Log.d(TAG, "Decrementing Active Connections "
+                            + mConnectionInfo.mNumActiveConnections);
+                }
             }
             setConnectionStateLocked(device, BluetoothProfile.STATE_DISCONNECTED);
-            if (!retry) {
-                mConnectionInfo.mDeviceIndex++;
-                if (DBG) {
-                    Log.d(TAG, "Moving to device: " + mConnectionInfo.mDeviceIndex);
+
+            // Update the mDeviceIndex only when there is no other device currently in the middle
+            // of a connection attempt.  This is to safeguard against disconnect intents coming in
+            // for devices other than the one that the policy is currently trying to connect.
+            if (getConnectingDeviceLocked() == null) {
+                if (!retry) {
+                    mConnectionInfo.mDeviceIndex++;
+                    if (DBG) {
+                        Log.d(TAG, "Moving to device: " + mConnectionInfo.mDeviceIndex);
+                    }
+                    // Reset the retry count
+                    mConnectionInfo.mRetryAttempt = 0;
+                } else {
+                    if (DBG) {
+                        Log.d(TAG, "Staying with the same device - retrying: "
+                                + mConnectionInfo.mDeviceIndex);
+                    }
                 }
-                // Reset the retry count
-                mConnectionInfo.mRetryAttempt = 0;
             } else {
-                if (DBG) {
-                    Log.d(TAG, "Staying with the same device - retrying: "
-                            + mConnectionInfo.mDeviceIndex);
+                BluetoothDevice connectingDevice = getConnectingDeviceLocked();
+                if (connectingDevice != null) {
+                    if (DBG) {
+                        Log.d(TAG, "Not moving to next device. " + connectingDevice
+                                + " still connecting");
+                    }
+                } else {
+                    Log.e(TAG, "Unexpected. Status = connecting, but connecting device = null");
                 }
             }
         }
     }
 
     /**
-     * Move the item in the given position to the front of the list and push the rest down.
+     * Move the given device to its priority slot
      *
-     * @param position - position of the device to move from
+     * @param deviceInfo - DeviceInfo to move
+     * @param priority   - Priority of the device in the list
      */
-    private void moveToFrontLocked(int position) {
+    private void moveDeviceToPrioritySlotsLocked(DeviceInfo deviceInfo, int priority) {
+        if (DBG) {
+            Log.d(TAG, "Moving " + deviceInfo.mBluetoothDevice + " to " + priority);
+        }
+        mDeviceInfoList.remove(deviceInfo);
+        mDeviceInfoList.add(priority, deviceInfo);
+    }
+
+    /**
+     * Move the item in the given position to the front of the queue and push the rest down.
+     *
+     * @param position - current position of the device that it is moving from
+     */
+    private void moveDeviceToQueueFrontLocked(int position) {
+        int topOfList = getNumberOfTaggedDevicesLocked();
+        // If the device is a primary or secondary, its position is fixed.
+        if (position <= topOfList) {
+            return;
+        }
         DeviceInfo deviceInfo = mDeviceInfoList.get(position);
         if (deviceInfo.mBluetoothDevice == null) {
             if (DBG) {
@@ -459,7 +652,11 @@
             return;
         }
         mDeviceInfoList.remove(position);
-        mDeviceInfoList.add(0, deviceInfo);
+        // Top of the list to which a device can be moved depends on the number of tagged devices
+        // If there is a dedicated Primary device, then the newly connected device can only be moved
+        // to the second position, since the primary device always occupies the first position.
+        // Hence the topOfList is the first position after the tagged devices.
+        mDeviceInfoList.add(topOfList, deviceInfo);
     }
 
     /**
@@ -524,8 +721,14 @@
                 mConnectionInfo.mNumActiveConnections < mConnectionInfo.mNumConnectionsSupported) {
             return true;
         }
-        return false;
 
+        if (DBG) {
+            Log.d(TAG, "Connected devices for profile " + mConnectionInfo.mProfile);
+            for (BluetoothDevice device : getConnectedDevicesLocked()) {
+                Log.d(TAG, device.getAddress());
+            }
+        }
+        return false;
     }
 
     /**
@@ -537,6 +740,10 @@
         return mConnectionInfo.mNumActiveConnections;
     }
 
+    public void resetDeviceIndex() {
+        mConnectionInfo.mDeviceIndex = 0;
+    }
+
     /**
      * Reset the connection related bookkeeping information.
      * Called on a BluetoothAdapter Off to clean slate
@@ -558,5 +765,4 @@
         }
         resetConnectionInfoLocked();
     }
-
 }
diff --git a/service/src/com/android/car/CarAudioAttributesUtil.java b/service/src/com/android/car/CarAudioAttributesUtil.java
index 54ed2c6..64d2571 100644
--- a/service/src/com/android/car/CarAudioAttributesUtil.java
+++ b/service/src/com/android/car/CarAudioAttributesUtil.java
@@ -55,6 +55,9 @@
             case CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE:
                 return createAudioAttributes(AudioAttributes.CONTENT_TYPE_SPEECH,
                         AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
+            case CarAudioManager.CAR_AUDIO_USAGE_RINGTONE:
+                return createAudioAttributes(AudioAttributes.CONTENT_TYPE_SONIFICATION,
+                        AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
             case CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL:
                 return createAudioAttributes(AudioAttributes.CONTENT_TYPE_SPEECH,
                         AudioAttributes.USAGE_VOICE_COMMUNICATION);
@@ -119,6 +122,8 @@
                 }
             case AudioAttributes.USAGE_VOICE_COMMUNICATION:
                 return CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL;
+            case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
+                return CarAudioManager.CAR_AUDIO_USAGE_RINGTONE;
             case AudioAttributes.USAGE_ALARM:
                 return CarAudioManager.CAR_AUDIO_USAGE_ALARM;
             case AudioAttributes.USAGE_NOTIFICATION:
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index d161883..f56b105 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -429,6 +429,7 @@
         if (audioPolicy != null) {
             mAudioManager.unregisterAudioPolicyAsync(audioPolicy);
         }
+        mVolumeService.release();
     }
 
     public synchronized void setAudioContextChangeListener(Looper looper,
@@ -707,7 +708,6 @@
 
     private void doHandleCarFocusChange() {
         int newFocusState = AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_INVALID;
-        FocusState currentState;
         AudioFocusInfo topInfo;
         boolean systemSoundActive = false;
         synchronized (mLock) {
@@ -729,7 +729,6 @@
                 newFocusState = mFocusReceived.focusState;
             }
             mCurrentFocusState = mFocusReceived;
-            currentState = mFocusReceived;
             mFocusReceived = null;
             if (mLastFocusRequestToCar != null &&
                     (mLastFocusRequestToCar.focusRequest ==
@@ -764,22 +763,22 @@
         }
         switch (newFocusState) {
             case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN:
-                doHandleFocusGainFromCar(currentState, topInfo, systemSoundActive);
+                doHandleFocusGainFromCar(mCurrentFocusState, topInfo, systemSoundActive);
                 break;
             case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT:
-                doHandleFocusGainTransientFromCar(currentState, topInfo, systemSoundActive);
+                doHandleFocusGainTransientFromCar(mCurrentFocusState, topInfo, systemSoundActive);
                 break;
             case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS:
-                doHandleFocusLossFromCar(currentState, topInfo);
+                doHandleFocusLossFromCar(mCurrentFocusState, topInfo);
                 break;
             case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT:
-                doHandleFocusLossTransientFromCar(currentState);
+                doHandleFocusLossTransientFromCar(mCurrentFocusState);
                 break;
             case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_CAN_DUCK:
-                doHandleFocusLossTransientCanDuckFromCar(currentState);
+                doHandleFocusLossTransientCanDuckFromCar(mCurrentFocusState);
                 break;
             case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE:
-                doHandleFocusLossTransientExclusiveFromCar(currentState);
+                doHandleFocusLossTransientExclusiveFromCar(mCurrentFocusState);
                 break;
         }
     }
@@ -827,15 +826,18 @@
             Log.d(TAG_FOCUS, "doHandleFocusLossFromCar current:" + currentState +
                     " top:" + dumpAudioFocusInfo(topInfo));
         }
-        boolean shouldRequestProxyFocus = false;
-        if ((currentState.externalFocus &
-                AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG) != 0) {
-            shouldRequestProxyFocus = true;
-        }
         if (isFocusFromCarProxy(topInfo)) {
             // already car proxy is top. Nothing to do.
             return;
-        } else if (!isFocusFromCarServiceBottom(topInfo)) {
+        }
+        boolean shouldRequestProxyFocus = false;
+        if ((currentState.externalFocus &
+                AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG) != 0) {
+            // If car is playing something persistent, the car proxy should have focus
+            shouldRequestProxyFocus = true;
+        }
+        if (!isFocusFromCarServiceBottom(topInfo)) {
+            // If a car source was being ducked, it should get the primary focus back
             shouldRequestProxyFocus = true;
         }
         if (shouldRequestProxyFocus) {
@@ -1798,8 +1800,8 @@
 
         public static FocusState create(int[] state) {
             return create(state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_STATE],
-                    state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_STREAMS],
-                    state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS]);
+                          state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_STREAMS],
+                          state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS]);
         }
 
         public static FocusState STATE_LOSS =
diff --git a/service/src/com/android/car/CarBluetoothService.java b/service/src/com/android/car/CarBluetoothService.java
index c1adea9..33d19c5 100644
--- a/service/src/com/android/car/CarBluetoothService.java
+++ b/service/src/com/android/car/CarBluetoothService.java
@@ -15,7 +15,27 @@
  */
 package com.android.car;
 
+import static android.car.settings.CarSettings.Secure
+        .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0;
+import static android.car.settings.CarSettings.Secure
+        .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
+import static android.car.settings.CarSettings.Secure
+        .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0;
+import static android.car.settings.CarSettings.Secure
+        .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
+import static android.car.settings.CarSettings.Secure
+        .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0;
+import static android.car.settings.CarSettings.Secure
+        .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
+
+import android.app.ActivityManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.car.CarBluetoothManager;
+import android.car.ICarBluetooth;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
 import android.util.Log;
 
 import java.io.PrintWriter;
@@ -28,17 +48,18 @@
  * connect for a specific profile.
  */
 
-public class CarBluetoothService implements CarServiceBase {
+public class CarBluetoothService extends ICarBluetooth.Stub implements CarServiceBase {
 
     private static final String TAG = "CarBluetoothService";
     private final Context mContext;
     private final BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicy;
+    private static final boolean DBG = false;
 
     public CarBluetoothService(Context context, CarCabinService carCabinService,
             CarSensorService carSensorService, PerUserCarServiceHelper userSwitchService) {
         mContext = context;
         mBluetoothDeviceConnectionPolicy = BluetoothDeviceConnectionPolicy.create(mContext,
-                carCabinService, carSensorService, userSwitchService);
+                carCabinService, carSensorService, userSwitchService, this);
     }
 
     @Override
@@ -51,6 +72,173 @@
         mBluetoothDeviceConnectionPolicy.release();
     }
 
+    /**
+     * Set the Auto connect priority for a paired Bluetooth Device.
+     * For example, if a device is tagged as a Primary device for a supported Bluetooth Profile,
+     * every new Auto Connect attempt would start with trying to connect to *that* device.
+     * This priority is set at a Bluetooth profile granularity
+     *
+     * @param deviceToSet   - Device to set priority (Tag)
+     * @param profileToSet  - BluetoothProfile to set priority for.
+     * @param priorityToSet - What priority level to set to
+     * @hide
+     */
+    public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet,
+            int priorityToSet) {
+        setBluetoothDeviceConnectionPriority(deviceToSet.getAddress(), profileToSet, priorityToSet);
+    }
+
+    public void setBluetoothDeviceConnectionPriority(String deviceAddress, int profileToSet,
+            int priorityToSet) {
+        // Check if the caller has Bluetooth Admin Permissions
+        enforceBluetoothAdminPermission();
+        if (priorityToSet == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1) {
+            if (!isPriorityDevicePresent(profileToSet,
+                    CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)) {
+                Log.e(TAG, "Secondary Device not allowed without a primary device");
+                return;
+            }
+        }
+        // Write the priority preference to Secure settings.  The Bluetooth device connection policy
+        // will look up the Settings when it initiates a connection
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                getKeyForProfile(profileToSet, priorityToSet), deviceAddress,
+                ActivityManager.getCurrentUser());
+
+    }
+
+    /**
+     * Unset the Auto connect priority for the given profile
+     *
+     * @param profileToClear  - Profile to unset priority
+     * @param priorityToClear - Which priority to clear (Primary or Secondary)
+     * @hide
+     */
+    public void clearBluetoothDeviceConnectionPriority(int profileToClear, int priorityToClear) {
+        // Check if the caller has Bluetooth Admin F@Permissions
+        enforceBluetoothAdminPermission();
+        if (priorityToClear == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) {
+            if (isPriorityDevicePresent(profileToClear,
+                    CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1)) {
+                Log.e(TAG, "Please remove Secondary device before removing Primary Device");
+                return;
+            }
+        }
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                getKeyForProfile(profileToClear, priorityToClear),
+                CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE,
+                ActivityManager.getCurrentUser());
+    }
+
+    /**
+     * Returns if there is a device that has been tagged with the given priority for the given
+     * profile.
+     *
+     * @param profile         - BluetoothProfile
+     * @param priorityToCheck - Priority to check
+     * @return true if there is a device present with the given priority, false if not
+     */
+    public boolean isPriorityDevicePresent(int profile, int priorityToCheck) {
+        String deviceName = getDeviceNameWithPriority(profile, priorityToCheck);
+        if (deviceName != null && !deviceName.equalsIgnoreCase(
+                CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE)) {
+            return true;
+        } else {
+            if (DBG) {
+                Log.d(TAG,
+                        "No device present for priority: " + priorityToCheck + " profile: "
+                                + profile);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Returns the Bluetooth device address as a String that has been tagged with the given priority
+     * for the given profile.
+     *
+     * @param profile         - BluetoothProfile
+     * @param priorityToCheck - Priority to check
+     * @return BluetoothDevice address if present, null if absent
+     */
+    public String getDeviceNameWithPriority(int profile, int priorityToCheck) {
+        String keyToQuery = null;
+        String deviceName = null;
+        enforceBluetoothAdminPermission();
+        switch (profile) {
+            case BluetoothProfile.A2DP_SINK:
+                keyToQuery = (priorityToCheck
+                        == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+                        ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0
+                        : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
+                break;
+            case BluetoothProfile.HEADSET_CLIENT:
+            case BluetoothProfile.PBAP_CLIENT:
+                keyToQuery = (priorityToCheck
+                        == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+                        ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0
+                        : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
+                break;
+            case BluetoothProfile.MAP_CLIENT:
+                keyToQuery = (priorityToCheck
+                        == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+                        ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0
+                        : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
+                break;
+            default:
+                if (DBG) {
+                    Log.d(TAG, "Unknown Bluetooth profile");
+                }
+        }
+        if (keyToQuery != null) {
+            deviceName = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                    keyToQuery, (int) ActivityManager.getCurrentUser());
+        }
+        return deviceName;
+    }
+
+    private void enforceBluetoothAdminPermission() {
+        if (mContext != null
+                && PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.BLUETOOTH_ADMIN)) {
+            return;
+        }
+        if (mContext == null) {
+            Log.e(TAG, "CarBluetoothPrioritySettings does not have a Context");
+        }
+        throw new SecurityException("requires permission " + android.Manifest.permission.BLUETOOTH_ADMIN);
+    }
+
+    private String getKeyForProfile(int profile, int priority) {
+        String keyToLookup = null;
+        switch (profile) {
+            case BluetoothProfile.A2DP_SINK:
+                keyToLookup = (priority
+                        == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+                        ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0
+                        : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
+                break;
+            case BluetoothProfile.MAP_CLIENT:
+                keyToLookup = (priority
+                        == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+                        ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0
+                        : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
+                break;
+            case BluetoothProfile.PBAP_CLIENT:
+                // fall through
+            case BluetoothProfile.HEADSET_CLIENT:
+                keyToLookup = (priority
+                        == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+                        ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0
+                        : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
+                break;
+            default:
+                Log.e(TAG, "Unsupported Bluetooth profile to set priority to");
+                break;
+        }
+        return keyToLookup;
+    }
+
     @Override
     public synchronized void dump(PrintWriter writer) {
         mBluetoothDeviceConnectionPolicy.dump(writer);
diff --git a/service/src/com/android/car/CarDiagnosticService.java b/service/src/com/android/car/CarDiagnosticService.java
index 88e7e34..809439c 100644
--- a/service/src/com/android/car/CarDiagnosticService.java
+++ b/service/src/com/android/car/CarDiagnosticService.java
@@ -19,11 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.car.Car;
-import android.car.annotation.FutureFeature;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
-import android.car.hardware.ICarDiagnostic;
-import android.car.hardware.ICarDiagnosticEventListener;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.diagnostic.ICarDiagnostic;
+import android.car.diagnostic.ICarDiagnosticEventListener;
 import android.content.Context;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -35,7 +34,6 @@
 import com.android.car.hal.DiagnosticHalService;
 import com.android.internal.annotations.GuardedBy;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
@@ -46,7 +44,6 @@
 import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
 
-@FutureFeature
 /** @hide */
 public class CarDiagnosticService extends ICarDiagnostic.Stub
         implements CarServiceBase, DiagnosticHalService.DiagnosticListener {
@@ -81,7 +78,8 @@
     public CarDiagnosticService(Context context, DiagnosticHalService diagnosticHal) {
         mContext = context;
         mDiagnosticHal = diagnosticHal;
-        mDiagnosticReadPermission = new CarPermission(mContext, Car.PERMISSION_CAR_DIAGNOSTIC_READ);
+        mDiagnosticReadPermission = new CarPermission(mContext,
+                Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL);
         mDiagnosticClearPermission = new CarPermission(mContext,
                 Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR);
     }
@@ -395,18 +393,18 @@
         return getDiagnosticHal().getDiagnosticCapabilities().isLiveFrameSupported();
     }
 
-    public boolean isFreezeFrameSupported() {
+    public boolean isFreezeFrameNotificationSupported() {
         return getDiagnosticHal().getDiagnosticCapabilities().isFreezeFrameSupported();
     }
 
-    public boolean isFreezeFrameTimestampSupported() {
+    public boolean isGetFreezeFrameSupported() {
         DiagnosticCapabilities diagnosticCapabilities =
                 getDiagnosticHal().getDiagnosticCapabilities();
         return diagnosticCapabilities.isFreezeFrameInfoSupported() &&
                 diagnosticCapabilities.isFreezeFrameSupported();
     }
 
-    public boolean isFreezeFrameClearSupported() {
+    public boolean isClearFreezeFramesSupported() {
         DiagnosticCapabilities diagnosticCapabilities =
             getDiagnosticHal().getDiagnosticCapabilities();
         return diagnosticCapabilities.isFreezeFrameClearSupported() &&
@@ -500,12 +498,9 @@
 
         @Override
         public boolean equals(Object o) {
-            if (o instanceof CarDiagnosticService.DiagnosticClient
-                    && mListener.asBinder()
-                            == ((CarDiagnosticService.DiagnosticClient) o).mListener.asBinder()) {
-                return true;
-            }
-            return false;
+            return o instanceof DiagnosticClient
+                && mListener.asBinder()
+                == ((DiagnosticClient) o).mListener.asBinder();
         }
 
         boolean isHoldingListenerBinder(IBinder listenerBinder) {
@@ -540,16 +535,12 @@
         }
 
         void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) {
-            if (events.size() == 0) {
-                return;
-            }
-            if (mActive) {
+            if (events.size() != 0 && mActive) {
                 try {
                     mListener.onDiagnosticEvents(events);
                 } catch (RemoteException e) {
                     //ignore. crash will be handled by death handler
                 }
-            } else {
             }
         }
 
diff --git a/service/src/com/android/car/CarProjectionService.java b/service/src/com/android/car/CarProjectionService.java
index 89ee8b0..5eb453f 100644
--- a/service/src/com/android/car/CarProjectionService.java
+++ b/service/src/com/android/car/CarProjectionService.java
@@ -149,7 +149,7 @@
     }
 
     @Override
-    public void regsiterProjectionListener(ICarProjectionCallback listener, int filter) {
+    public void registerProjectionListener(ICarProjectionCallback listener, int filter) {
         synchronized (this) {
             ListenerInfo info = (ListenerInfo) mAllListeners.getBinderInterface(listener);
             if (info == null) {
@@ -163,7 +163,7 @@
     }
 
     @Override
-    public void unregsiterProjectionListener(ICarProjectionCallback listener) {
+    public void unregisterProjectionListener(ICarProjectionCallback listener) {
         synchronized (this) {
             mAllListeners.removeBinder(listener);
         }
@@ -204,7 +204,7 @@
     @Override
     public void onBinderDeath(
             BinderInterfaceContainer.BinderInterface<ICarProjectionCallback> bInterface) {
-        unregsiterProjectionListener(bInterface.binderInterface);
+        unregisterProjectionListener(bInterface.binderInterface);
     }
 
     @Override
diff --git a/service/src/com/android/car/CarSensorEventFactory.java b/service/src/com/android/car/CarSensorEventFactory.java
index fbae0ed..6497e16 100644
--- a/service/src/com/android/car/CarSensorEventFactory.java
+++ b/service/src/com/android/car/CarSensorEventFactory.java
@@ -17,29 +17,50 @@
 package com.android.car;
 
 import android.car.hardware.CarSensorEvent;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
 
 //TODO add memory pool and recycling
 public class CarSensorEventFactory {
 
     public static CarSensorEvent createBooleanEvent(int sensorType, long timestamp,
             boolean value) {
-        CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1);
+        CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1, 0);
         event.intValues[0] = value ? 1 : 0;
         return event;
     }
 
     public static CarSensorEvent createIntEvent(int sensorType, long timestamp, int value) {
-        CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1);
+        CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1, 0);
         event.intValues[0] = value;
         return event;
     }
 
     public static CarSensorEvent createFloatEvent(int sensorType, long timestamp, float value) {
-        CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 1, 0);
+        CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 1, 0, 0);
         event.floatValues[0] = value;
         return event;
     }
 
+    public static CarSensorEvent createComplexEvent(int sensorType, long timestamp,
+                                                    VehiclePropValue v) {
+        int numFloats = v.value.floatValues.size();
+        int numInts = v.value.int32Values.size();
+        int numLongs = v.value.int64Values.size();
+        CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, numFloats, numInts,
+            numLongs);
+        // Copy arraylist elements into final arrays
+        for (int i=0; i<numFloats; i++) {
+            event.floatValues[i] = v.value.floatValues.get(i);
+        }
+        for (int i=0; i<numInts; i++) {
+            event.intValues[i] = v.value.int32Values.get(i);
+        }
+        for (int i=0; i<numLongs; i++) {
+            event.longValues[i] = v.value.int64Values.get(i);
+        }
+        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 ffb13ca..171a8c4 100644
--- a/service/src/com/android/car/CarSensorService.java
+++ b/service/src/com/android/car/CarSensorService.java
@@ -17,6 +17,7 @@
 package com.android.car;
 
 import android.car.Car;
+import android.car.hardware.CarSensorConfig;
 import android.car.hardware.CarSensorEvent;
 import android.car.hardware.CarSensorManager;
 import android.car.hardware.ICarSensor;
@@ -420,6 +421,7 @@
         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:
@@ -454,6 +456,7 @@
         String permission = null;
         switch (sensorType) {
             case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
+            case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
                 permission = Car.PERMISSION_SPEED;
                 break;
             case CarSensorManager.SENSOR_TYPE_ODOMETER:
@@ -462,6 +465,10 @@
             case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
                 permission = Car.PERMISSION_FUEL;
                 break;
+            case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE:
+            case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE:
+                permission = Car.PERMISSION_VEHICLE_DYNAMICS_STATE;
+                break;
             default:
                 break;
         }
@@ -559,6 +566,22 @@
         }
     }
 
+    @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);
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 298290b..87e891e 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -44,11 +44,6 @@
 
     private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
 
-    private static final String IVHAL_20 =
-            android.hardware.automotive.vehicle.V2_0.IVehicle.kInterfaceName;
-    private static final String IVHAL_21 =
-            android.hardware.automotive.vehicle.V2_1.IVehicle.kInterfaceName;
-
     private CanBusErrorNotifier mCanBusErrorNotifier;
     private ICarImpl mICarImpl;
     private IVehicle mVehicle;
@@ -77,7 +72,7 @@
     public void onCreate() {
         Log.i(CarLog.TAG_SERVICE, "Service onCreate");
         mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);
-        mVehicle = getVehicle(null /* Any Vehicle HAL interface name */);
+        mVehicle = getVehicle();
 
         if (mVehicle == null) {
             throw new IllegalStateException("Vehicle HAL service is not available.");
@@ -152,8 +147,8 @@
     }
 
     @Nullable
-    private IVehicle getVehicleWithTimeout(long waitMilliseconds, @Nullable String interfaceName) {
-        IVehicle vehicle = getVehicle(interfaceName);
+    private IVehicle getVehicleWithTimeout(long waitMilliseconds) {
+        IVehicle vehicle = getVehicle();
         long start = elapsedRealtime();
         while (vehicle == null && (start + waitMilliseconds) > elapsedRealtime()) {
             try {
@@ -162,7 +157,7 @@
                 throw new RuntimeException("Sleep was interrupted", e);
             }
 
-            vehicle = getVehicle(interfaceName);
+            vehicle = getVehicle();
         }
 
         if (vehicle != null) {
@@ -173,20 +168,9 @@
     }
 
     @Nullable
-    private static IVehicle getVehicle(@Nullable String interfaceName) {
+    private static IVehicle getVehicle() {
         try {
-            boolean anyVersion = interfaceName == null || interfaceName.isEmpty();
-            IVehicle vehicle = null;
-            if (anyVersion || IVHAL_21.equals(interfaceName)) {
-                vehicle = android.hardware.automotive.vehicle.V2_1.IVehicle
-                        .getService();
-            }
-
-            if (vehicle == null && (anyVersion || IVHAL_20.equals(interfaceName))) {
-                vehicle = android.hardware.automotive.vehicle.V2_0.IVehicle
-                        .getService();
-            }
-            return vehicle;
+            return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
         } catch (RemoteException e) {
             Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
         } catch (NoSuchElementException e) {
@@ -213,8 +197,7 @@
 
             Log.i(CarLog.TAG_SERVICE, "Trying to reconnect to Vehicle HAL: " +
                     mVehicleInterfaceName);
-            mVehicle = getVehicleWithTimeout(WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS,
-                    mVehicleInterfaceName);
+            mVehicle = getVehicleWithTimeout(WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS);
             if (mVehicle == null) {
                 throw new IllegalStateException("Failed to reconnect to Vehicle HAL");
             }
diff --git a/service/src/com/android/car/CarVolumeControllerFactory.java b/service/src/com/android/car/CarVolumeControllerFactory.java
index 4e9bd4f..c83ef93 100644
--- a/service/src/com/android/car/CarVolumeControllerFactory.java
+++ b/service/src/com/android/car/CarVolumeControllerFactory.java
@@ -17,6 +17,7 @@
 package com.android.car;
 
 import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.media.IVolumeController;
@@ -254,7 +255,8 @@
                         AudioHalService.carContextToCarUsage(carContext));
                 return physicalStream;
             } else {
-                return carContext;
+                return carContext == VehicleAudioContextFlag.UNKNOWN_FLAG ?
+                        mCurrentContext : carContext;
             }
         }
 
@@ -533,7 +535,7 @@
             synchronized (this) {
                 int flag = getVolumeUpdateFlag(true);
                 if (DBG) {
-                    Log.d(TAG, "onVolumeChange carStream:" + carStream + " volume: " + volume
+                    Log.d(TAG, "onVolumeChange carStream: " + carStream + " volume: " + volume
                             + " volumeState: " + volumeState
                             + " suppressUI? " + mShouldSuppress
                             + " stream: " + mSuppressUiForVolume[0]
@@ -543,6 +545,13 @@
                 if (mMasterVolumeOnly) { //for master volume only H/W, always assume current stream
                     carStream = currentCarStream;
                 }
+
+                // Map the UNKNOWN context to the current context.
+                if (mSupportedAudioContext != 0
+                        && carStream == VehicleAudioContextFlag.UNKNOWN_FLAG) {
+                    carStream = mCurrentContext;
+                }
+
                 if (currentCarStream == carStream) {
                     mCurrentCarContextVolume.put(mCurrentContext, volume);
                     writeVolumeToSettings(mCurrentContext, volume);
@@ -552,6 +561,7 @@
                 } else {
                     // Hal is telling us a car stream volume has changed, but it is not the current
                     // stream.
+                    // TODO:  b/63778359
                     Log.w(TAG, "Car stream" + carStream
                             + " volume changed, but it is not current stream, ignored.");
                 }
diff --git a/service/src/com/android/car/DayNightModePolicy.java b/service/src/com/android/car/DayNightModePolicy.java
index dae9a14..48072eb 100644
--- a/service/src/com/android/car/DayNightModePolicy.java
+++ b/service/src/com/android/car/DayNightModePolicy.java
@@ -86,7 +86,7 @@
 
     private static CarSensorEvent createEvent(boolean isNight, long timestamp) {
         CarSensorEvent event = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_NIGHT,
-                timestamp, 0, 1);
+                timestamp, 0, 1, 0);
         event.intValues[0] = isNight ? 1 : 0;
         return event;
     }
diff --git a/service/src/com/android/car/DrivingStatePolicy.java b/service/src/com/android/car/DrivingStatePolicy.java
index 167bb93..37bbbc0 100644
--- a/service/src/com/android/car/DrivingStatePolicy.java
+++ b/service/src/com/android/car/DrivingStatePolicy.java
@@ -167,7 +167,7 @@
                 if (lastGear != null && isGearInParkingOrNeutral(lastGear)) {
                     drivingState = CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
                 }
-            } else { // parking break not applied or not available
+            } else { // parking brake not applied or not available
                 if (lastGear != null && isGearInParking(lastGear)) { // gear in P
                     drivingState = CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
                 }
@@ -204,7 +204,8 @@
                 CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
                 timestamp,
                 0 /* float values */,
-                1 /* int values */);
+                1 /* int values */,
+                0 /* long values */);
         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 f23f10c..bbb86f6 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -24,13 +24,13 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.automotive.vehicle.V2_0.IVehicle;
-import android.hardware.automotive.vehicle.V2_0.VehicleAreaDoor;
-import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.Trace;
 import android.util.Log;
-
+import android.util.Slog;
+import android.util.TimingsTraceLog;
 import com.android.car.cluster.InstrumentClusterService;
 import com.android.car.hal.VehicleHal;
 import com.android.car.internal.FeatureConfiguration;
@@ -38,7 +38,6 @@
 import com.android.car.pm.CarPackageManagerService;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.car.ICarServiceHelper;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -72,15 +71,15 @@
     private final CarVendorExtensionService mCarVendorExtensionService;
     private final CarBluetoothService mCarBluetoothService;
     private final PerUserCarServiceHelper mPerUserCarServiceHelper;
-    @FutureFeature
     private CarDiagnosticService mCarDiagnosticService;
-    @FutureFeature
-    private VmsSubscriberService mVmsSubscriberService;
-    @FutureFeature
-    private VmsPublisherService mVmsPublisherService;
 
     private final CarServiceBase[] mAllServices;
 
+    private static final String TAG = "ICarImpl";
+    private static final String VHAL_TIMING_TAG = "VehicleHalTiming";
+    private static final TimingsTraceLog mBootTiming = new TimingsTraceLog(VHAL_TIMING_TAG,
+        Trace.TRACE_TAG_HAL);
+
     /** Test only service. Populate it only when necessary. */
     @GuardedBy("this")
     private CarTestService mCarTestService;
@@ -118,10 +117,6 @@
         mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext);
         mCarBluetoothService = new CarBluetoothService(serviceContext, mCarCabinService,
                 mCarSensorService, mPerUserCarServiceHelper);
-        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-            mVmsSubscriberService = new VmsSubscriberService(serviceContext, mHal.getVmsHal());
-            mVmsPublisherService = new VmsPublisherService(serviceContext, mHal.getVmsHal());
-        }
         mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal());
 
         // Be careful with order. Service depending on other service should be inited later.
@@ -147,18 +142,18 @@
                 mCarDiagnosticService,
                 mPerUserCarServiceHelper
         ));
-        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-            allServices.add(mVmsSubscriberService);
-            allServices.add(mVmsPublisherService);
-        }
         mAllServices = allServices.toArray(new CarServiceBase[0]);
     }
 
     public void init() {
+        traceBegin("VehicleHal.init");
         mHal.init();
+        traceEnd();
+        traceBegin("CarService.initAllServices");
         for (CarServiceBase service : mAllServices) {
             service.init();
         }
+        traceEnd();
     }
 
     public void release() {
@@ -217,18 +212,15 @@
                 IInstrumentClusterNavigation navService =
                         mInstrumentClusterService.getNavigationService();
                 return navService == null ? null : navService.asBinder();
+            case Car.CAR_INSTRUMENT_CLUSTER_SERVICE:
+                assertClusterManagerPermission(mContext);
+                return mInstrumentClusterService.getManagerService();
             case Car.PROJECTION_SERVICE:
                 assertProjectionPermission(mContext);
                 return mCarProjectionService;
             case Car.VENDOR_EXTENSION_SERVICE:
                 assertVendorExtensionPermission(mContext);
                 return mCarVendorExtensionService;
-            case Car.VMS_SUBSCRIBER_SERVICE:
-                FeatureUtil.assertFeature(FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE);
-                if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-                    assertVmsSubscriberPermission(mContext);
-                    return mVmsSubscriberService;
-                }
             case Car.TEST_SERVICE: {
                 assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE);
                 synchronized (this) {
@@ -238,6 +230,8 @@
                     return mCarTestService;
                 }
             }
+            case Car.BLUETOOTH_SERVICE:
+                return mCarBluetoothService;
             default:
                 Log.w(CarLog.TAG_SERVICE, "getCarService for unknown service:" + serviceName);
                 return null;
@@ -274,6 +268,10 @@
         assertPermission(context, Car.PERMISSION_CAR_NAVIGATION_MANAGER);
     }
 
+    public static void assertClusterManagerPermission(Context context) {
+        assertPermission(context, Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL);
+    }
+
     public static void assertHvacPermission(Context context) {
         assertPermission(context, Car.PERMISSION_CAR_HVAC);
     }
@@ -290,23 +288,12 @@
         assertPermission(context, Car.PERMISSION_VENDOR_EXTENSION);
     }
 
-    @FutureFeature
     public static void assertAnyDiagnosticPermission(Context context) {
         assertAnyPermission(context,
-                Car.PERMISSION_CAR_DIAGNOSTIC_READ,
+                Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL,
                 Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR);
     }
 
-    @FutureFeature
-    public static void assertVmsPublisherPermission(Context context) {
-        assertPermission(context, Car.PERMISSION_VMS_PUBLISHER);
-    }
-
-    @FutureFeature
-    public static void assertVmsSubscriberPermission(Context context) {
-        assertPermission(context, Car.PERMISSION_VMS_SUBSCRIBER);
-    }
-
     public static void assertPermission(Context context, String permission) {
         if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("requires " + permission);
@@ -341,6 +328,15 @@
         new CarShellCommand().exec(args, writer);
     }
 
+    private static void traceBegin(String name) {
+        Slog.i(TAG, name);
+        mBootTiming.traceBegin(name);
+    }
+
+    private static void traceEnd() {
+        mBootTiming.traceEnd();
+    }
+
     private class CarShellCommand {
         private static final String COMMAND_HELP = "-h";
         private static final String COMMAND_DAY_NIGHT_MODE = "day-night-mode";
diff --git a/service/src/com/android/car/VmsLayersAvailability.java b/service/src/com/android/car/VmsLayersAvailability.java
deleted file mode 100644
index d6e89f2..0000000
--- a/service/src/com/android/car/VmsLayersAvailability.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-import android.car.annotation.FutureFeature;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsLayerDependency;
-import android.car.vms.VmsLayersOffering;
-import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Manages VMS availability for layers.
- *
- * Each VMS publisher sets its layers offering which are a list of layers the publisher claims
- * it might publish. VmsLayersAvailability calculates from all the offering what are the
- * available layers.
- */
-
-@FutureFeature
-public class VmsLayersAvailability {
-
-    private static final boolean DBG = true;
-    private static final String TAG = "VmsLayersAvailability";
-
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private final Map<VmsLayer, Set<Set<VmsLayer>>> mPotentialLayersAndDependencies =
-        new HashMap<>();
-    @GuardedBy("mLock")
-    private final Set<VmsLayer> mCyclicAvoidanceSet = new HashSet<>();
-    @GuardedBy("mLock")
-    private Set<VmsLayer> mAvailableLayers = Collections.EMPTY_SET;
-    @GuardedBy("mLock")
-    private Set<VmsLayer> mUnavailableLayers = Collections.EMPTY_SET;
-
-    /**
-     * Setting the current layers offerings as reported by publishers.
-     */
-    public void setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings) {
-        synchronized (mLock) {
-            reset();
-
-            for (VmsLayersOffering offering : publishersLayersOfferings) {
-                for (VmsLayerDependency dependency : offering.getDependencies()) {
-                    VmsLayer layer = dependency.getLayer();
-                    Set<Set<VmsLayer>> curDependencies =
-                        mPotentialLayersAndDependencies.get(layer);
-                    if (curDependencies == null) {
-                        curDependencies = new HashSet<>();
-                        mPotentialLayersAndDependencies.put(layer, curDependencies);
-                    }
-                    curDependencies.add(dependency.getDependencies());
-                }
-            }
-            calculateLayers();
-        }
-    }
-
-    /**
-     * Returns a collection of all the layers which may be published.
-     */
-    public Set<VmsLayer> getAvailableLayers() {
-        synchronized (mLock) {
-            return mAvailableLayers;
-        }
-    }
-
-    /**
-     * Returns a collection of all the layers which publishers could have published if the
-     * dependencies were satisfied.
-     */
-    public Set<VmsLayer> getUnavailableLayers() {
-        synchronized (mLock) {
-            return mUnavailableLayers;
-        }
-    }
-
-    private void reset() {
-        synchronized (mLock) {
-            mCyclicAvoidanceSet.clear();
-            mPotentialLayersAndDependencies.clear();
-            mAvailableLayers = Collections.EMPTY_SET;
-            mUnavailableLayers = Collections.EMPTY_SET;
-        }
-    }
-
-    private void calculateLayers() {
-        synchronized (mLock) {
-            final Set<VmsLayer> availableLayers = new HashSet<>();
-
-            availableLayers.addAll(
-                mPotentialLayersAndDependencies.keySet()
-                    .stream()
-                    .filter(layer -> isLayerSupportedLocked(layer, availableLayers))
-                    .collect(Collectors.toSet()));
-
-            mAvailableLayers = Collections.unmodifiableSet(availableLayers);
-            mUnavailableLayers = Collections.unmodifiableSet(
-                mPotentialLayersAndDependencies.keySet()
-                    .stream()
-                    .filter(layer -> !availableLayers.contains(layer))
-                    .collect(Collectors.toSet()));
-        }
-    }
-
-    private boolean isLayerSupportedLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers) {
-        if (DBG) {
-            Log.d(TAG, "isLayerSupported: checking layer: " + layer);
-        }
-        // If we already know that this layer is supported then we are done.
-        if (currentAvailableLayers.contains(layer)) {
-            return true;
-        }
-        // If there is no offering for this layer we're done.
-        if (!mPotentialLayersAndDependencies.containsKey(layer)) {
-            return false;
-        }
-        // Avoid cyclic dependency.
-        if (mCyclicAvoidanceSet.contains(layer)) {
-            Log.e(TAG, "Detected a cyclic dependency: " + mCyclicAvoidanceSet + " -> " + layer);
-            return false;
-        }
-        for (Set<VmsLayer> dependencies : mPotentialLayersAndDependencies.get(layer)) {
-            // If layer does not have any dependencies then add to supported.
-            if (dependencies == null || dependencies.isEmpty()) {
-                currentAvailableLayers.add(layer);
-                return true;
-            }
-            // Add the layer to cyclic avoidance set
-            mCyclicAvoidanceSet.add(layer);
-
-            boolean isSupported = true;
-            for (VmsLayer dependency : dependencies) {
-                if (!isLayerSupportedLocked(dependency, currentAvailableLayers)) {
-                    isSupported = false;
-                    break;
-                }
-            }
-            mCyclicAvoidanceSet.remove(layer);
-
-            if (isSupported) {
-                currentAvailableLayers.add(layer);
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
deleted file mode 100644
index 37d265a..0000000
--- a/service/src/com/android/car/VmsPublisherService.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-import android.car.annotation.FutureFeature;
-import android.car.vms.IVmsSubscriberClient;
-import android.car.vms.IVmsPublisherClient;
-import android.car.vms.IVmsPublisherService;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsLayersOffering;
-import android.car.vms.VmsSubscriptionState;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-import com.android.car.hal.VmsHalService;
-import com.android.internal.annotations.GuardedBy;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * + Receives HAL updates by implementing VmsHalService.VmsHalListener.
- * + Binds to publishers and configures them to use this service.
- * + Notifies publishers of subscription changes.
- */
-@FutureFeature
-public class VmsPublisherService extends IVmsPublisherService.Stub
-        implements CarServiceBase, VmsHalService.VmsHalPublisherListener {
-    private static final boolean DBG = true;
-    private static final String TAG = "VmsPublisherService";
-
-    private final Context mContext;
-    private final VmsHalService mHal;
-    private final VmsPublisherManager mPublisherManager;
-    private Set<String> mSafePermissions;
-
-    public VmsPublisherService(Context context, VmsHalService hal) {
-        mContext = context;
-        mHal = hal;
-        mPublisherManager = new VmsPublisherManager(this);
-    }
-
-    // Implements CarServiceBase interface.
-    @Override
-    public void init() {
-        mHal.addPublisherListener(this);
-        // Load permissions that can be granted to publishers.
-        mSafePermissions = new HashSet<>(
-                Arrays.asList(mContext.getResources().getStringArray(R.array.vmsSafePermissions)));
-        // Launch publishers.
-        String[] publisherNames = mContext.getResources().getStringArray(
-                R.array.vmsPublisherClients);
-        for (String publisherName : publisherNames) {
-            if (TextUtils.isEmpty(publisherName)) {
-                Log.e(TAG, "empty publisher name");
-                continue;
-            }
-            ComponentName name = ComponentName.unflattenFromString(publisherName);
-            if (name == null) {
-                Log.e(TAG, "invalid publisher name: " + publisherName);
-            }
-            mPublisherManager.bind(name);
-        }
-    }
-
-    @Override
-    public void release() {
-        mPublisherManager.release();
-        mHal.removePublisherListener(this);
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-    }
-
-    @Override
-    public void setLayersOffering(IBinder token, VmsLayersOffering offering) {
-        mHal.setPublisherLayersOffering(token, offering);
-    }
-
-    // Implements IVmsPublisherService interface.
-    @Override
-    public void publish(IBinder token, VmsLayer layer, byte[] payload) {
-        if (DBG) {
-            Log.d(TAG, "Publishing for layer: " + layer);
-        }
-        ICarImpl.assertVmsPublisherPermission(mContext);
-
-        // Send the message to application listeners.
-        Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
-
-        if (DBG) {
-            Log.d(TAG, "Number of subscribed apps: " + listeners.size());
-        }
-        for (IVmsSubscriberClient listener : listeners) {
-            try {
-                listener.onVmsMessageReceived(layer, payload);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "unable to publish to listener: " + listener);
-            }
-        }
-
-        // Send the message to HAL
-        if (mHal.isHalSubscribed(layer)) {
-            Log.d(TAG, "HAL is subscribed");
-            mHal.setDataMessage(layer, payload);
-        } else {
-            Log.d(TAG, "HAL is NOT subscribed");
-        }
-    }
-
-    @Override
-    public VmsSubscriptionState getSubscriptions() {
-        ICarImpl.assertVmsPublisherPermission(mContext);
-        return mHal.getSubscriptionState();
-    }
-
-    @Override
-    public int getPublisherStaticId(byte[] publisherInfo) {
-        ICarImpl.assertVmsPublisherPermission(mContext);
-        return mHal.getPublisherStaticId(publisherInfo);
-    }
-
-    // Implements VmsHalListener interface
-    /**
-     * This method is only invoked by VmsHalService.notifyPublishers which is synchronized.
-     * Therefore this method only sees a non-decreasing sequence.
-     */
-    @Override
-    public void onChange(VmsSubscriptionState subscriptionState) {
-        // Send the message to application listeners.
-        for (IVmsPublisherClient client : mPublisherManager.getClients()) {
-            try {
-                client.onVmsSubscriptionChange(subscriptionState);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "unable to send notification to: " + client, ex);
-            }
-        }
-    }
-
-    /**
-     * Keeps track of publishers that are using this service.
-     */
-    private static class VmsPublisherManager {
-        /**
-         * Allows to modify mPublisherMap and mPublisherConnectionMap as a single unit.
-         */
-        private final Object mLock = new Object();
-        @GuardedBy("mLock")
-        private final Map<String, PublisherConnection> mPublisherConnectionMap = new HashMap<>();
-        @GuardedBy("mLock")
-        private final Map<String, IVmsPublisherClient> mPublisherMap = new HashMap<>();
-        private final WeakReference<VmsPublisherService> mPublisherService;
-
-        public VmsPublisherManager(VmsPublisherService publisherService) {
-            mPublisherService = new WeakReference<>(publisherService);
-        }
-
-        /**
-         * Tries to bind to a publisher.
-         *
-         * @param name publisher component name (e.g. android.car.vms.logger/.LoggingService).
-         */
-        public void bind(ComponentName name) {
-            VmsPublisherService publisherService = mPublisherService.get();
-            if (publisherService == null) return;
-            String publisherName = name.flattenToString();
-            if (DBG) {
-                Log.d(TAG, "binding to: " + publisherName);
-            }
-            synchronized (mLock) {
-                if (mPublisherConnectionMap.containsKey(publisherName)) {
-                    // Already registered, nothing to do.
-                    return;
-                }
-                grantPermissions(name);
-                Intent intent = new Intent();
-                intent.setComponent(name);
-                PublisherConnection connection = new PublisherConnection();
-                if (publisherService.mContext.bindServiceAsUser(intent, connection,
-                        Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
-                    mPublisherConnectionMap.put(publisherName, connection);
-                } else {
-                    Log.e(TAG, "unable to bind to: " + publisherName);
-                }
-            }
-        }
-
-        /**
-         * Removes the publisher and associated connection.
-         *
-         * @param name publisher component name (e.g. android.car.vms.Logger).
-         */
-        public void unbind(ComponentName name) {
-            VmsPublisherService publisherService = mPublisherService.get();
-            if (publisherService == null) return;
-            String publisherName = name.flattenToString();
-            if (DBG) {
-                Log.d(TAG, "unbinding from: " + publisherName);
-            }
-            synchronized (mLock) {
-                boolean found = mPublisherMap.remove(publisherName) != null;
-                if (found) {
-                    PublisherConnection connection = mPublisherConnectionMap.get(publisherName);
-                    publisherService.mContext.unbindService(connection);
-                    mPublisherConnectionMap.remove(publisherName);
-                } else {
-                    Log.e(TAG, "unbind: unknown publisher." + publisherName);
-                }
-            }
-        }
-
-        /**
-         * Returns the list of publishers currently registered.
-         *
-         * @return list of publishers.
-         */
-        public List<IVmsPublisherClient> getClients() {
-            synchronized (mLock) {
-                return new ArrayList<>(mPublisherMap.values());
-            }
-        }
-
-        public void release() {
-            VmsPublisherService publisherService = mPublisherService.get();
-            if (publisherService == null) return;
-            for (PublisherConnection connection : mPublisherConnectionMap.values()) {
-                publisherService.mContext.unbindService(connection);
-            }
-            mPublisherConnectionMap.clear();
-            mPublisherMap.clear();
-        }
-
-        private void grantPermissions(ComponentName component) {
-            VmsPublisherService publisherService = mPublisherService.get();
-            if (publisherService == null) return;
-            final PackageManager packageManager = publisherService.mContext.getPackageManager();
-            final String packageName = component.getPackageName();
-            PackageInfo packageInfo;
-            try {
-                packageInfo = packageManager.getPackageInfo(packageName,
-                        PackageManager.GET_PERMISSIONS);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "Error getting package info for " + packageName, e);
-                return;
-            }
-            if (packageInfo.requestedPermissions == null) return;
-            for (String permission : packageInfo.requestedPermissions) {
-                if (!publisherService.mSafePermissions.contains(permission)) {
-                    continue;
-                }
-                if (packageManager.checkPermission(permission, packageName)
-                        == PackageManager.PERMISSION_GRANTED) {
-                    continue;
-                }
-                try {
-                    packageManager.grantRuntimePermission(packageName, permission,
-                            UserHandle.SYSTEM);
-                    Log.d(TAG, "Permission " + permission + " granted to " + packageName);
-                } catch (SecurityException | IllegalArgumentException e) {
-                    Log.e(TAG, "Error while trying to grant " + permission + " to " + packageName,
-                            e);
-                }
-            }
-        }
-
-        class PublisherConnection implements ServiceConnection {
-
-            private final IBinder mToken = new Binder();
-
-            /**
-             * Once the service binds to a publisher service, the publisher binder is added to
-             * mPublisherMap
-             * and the publisher is configured to use this service.
-             */
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder binder) {
-                VmsPublisherService publisherService = mPublisherService.get();
-                if (publisherService == null) return;
-                if (DBG) {
-                    Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder);
-                }
-                IVmsPublisherClient service = IVmsPublisherClient.Stub.asInterface(binder);
-                synchronized (mLock) {
-                    mPublisherMap.put(name.flattenToString(), service);
-                }
-                try {
-                    service.setVmsPublisherService(mToken, publisherService);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "unable to configure publisher: " + name);
-                }
-            }
-
-            /**
-             * Tries to rebind to the publisher service.
-             */
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                String publisherName = name.flattenToString();
-                Log.d(TAG, "onServiceDisconnected, name: " + publisherName);
-                VmsPublisherManager.this.unbind(name);
-                VmsPublisherManager.this.bind(name);
-            }
-        }
-    }
-}
diff --git a/service/src/com/android/car/VmsPublishersInfo.java b/service/src/com/android/car/VmsPublishersInfo.java
deleted file mode 100644
index 04ee82f..0000000
--- a/service/src/com/android/car/VmsPublishersInfo.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-
-import android.car.annotation.FutureFeature;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
-import com.android.internal.annotations.GuardedBy;
-import android.util.Log;
-
-@FutureFeature
-public class VmsPublishersInfo {
-    private static final String TAG = "VmsPublishersInfo";
-    private static final boolean DBG = true;
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private final Map<InfoWrapper, Integer> mPublishersIds = new HashMap();
-    @GuardedBy("mLock")
-    private final Map<Integer, byte[]> mPublishersInfo = new HashMap();
-
-    private static class InfoWrapper {
-        private final byte[] mInfo;
-
-        public InfoWrapper(byte[] info) {
-            mInfo = info;
-        }
-
-        public byte[] getInfo() {
-            return mInfo;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (!(o instanceof InfoWrapper)) {
-                return false;
-            }
-            InfoWrapper p = (InfoWrapper) o;
-            return Arrays.equals(this.mInfo, p.mInfo);
-        }
-
-        @Override
-        public int hashCode() {
-            return Arrays.hashCode(mInfo);
-        }
-    }
-
-    /**
-     * Returns the ID associated with the publisher info. When called for the first time for a
-     * publisher info will store the info and assign an ID
-     */
-    public int getIdForInfo(byte[] publisherInfo) {
-        Integer publisherId;
-        InfoWrapper wrappedPublisherInfo = new InfoWrapper(publisherInfo);
-        synchronized (mLock) {
-            maybeAddPublisherInfoLocked(wrappedPublisherInfo);
-            publisherId = mPublishersIds.get(wrappedPublisherInfo);
-        }
-        if (DBG) {
-            Log.i(TAG, "Publisher ID is: " + publisherId);
-        }
-        return publisherId;
-    }
-
-    public byte[] getPublisherInfo(int publisherId) {
-        synchronized (mLock) {
-            return mPublishersInfo.get(publisherId).clone();
-        }
-    }
-
-    private void maybeAddPublisherInfoLocked(InfoWrapper wrappedPublisherInfo) {
-        if (!mPublishersIds.containsKey(wrappedPublisherInfo)) {
-            // Assign ID to the info
-            Integer publisherId = mPublishersIds.size();
-
-            mPublishersIds.put(wrappedPublisherInfo, publisherId);
-            mPublishersInfo.put(publisherId, wrappedPublisherInfo.getInfo());
-        }
-    }
-}
-
diff --git a/service/src/com/android/car/VmsRouting.java b/service/src/com/android/car/VmsRouting.java
deleted file mode 100644
index 2829cc0..0000000
--- a/service/src/com/android/car/VmsRouting.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-import android.car.annotation.FutureFeature;
-import android.car.vms.IVmsSubscriberClient;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsSubscriptionState;
-import com.android.internal.annotations.GuardedBy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Manages all the VMS subscriptions:
- * + Subscriptions to data messages of individual layer + version.
- * + Subscriptions to all data messages.
- * + HAL subscriptions to layer + version.
- */
-@FutureFeature
-public class VmsRouting {
-    private final Object mLock = new Object();
-    // A map of Layer + Version to listeners.
-    @GuardedBy("mLock")
-    private Map<VmsLayer, Set<IVmsSubscriberClient>> mLayerSubscriptions =
-        new HashMap<>();
-    // A set of listeners that are interested in any layer + version.
-    @GuardedBy("mLock")
-    private Set<IVmsSubscriberClient> mPromiscuousSubscribers =
-        new HashSet<>();
-    // A set of all the layers + versions the HAL is subscribed to.
-    @GuardedBy("mLock")
-    private Set<VmsLayer> mHalSubscriptions = new HashSet<>();
-    // A sequence number that is increased every time the subscription state is modified. Note that
-    // modifying the list of promiscuous subscribers does not affect the subscription state.
-    @GuardedBy("mLock")
-    private int mSequenceNumber = 0;
-
-    /**
-     * Add a listener subscription to a data messages from layer + version.
-     *
-     * @param listener a VMS subscriber.
-     * @param layer the layer subscribing to.
-     */
-    public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
-        //TODO(b/36902947): revise if need to sync, and return value.
-        synchronized (mLock) {
-            ++mSequenceNumber;
-            // Get or create the list of listeners for layer and version.
-            Set<IVmsSubscriberClient> listeners = mLayerSubscriptions.get(layer);
-
-            if (listeners == null) {
-                listeners = new HashSet<>();
-                mLayerSubscriptions.put(layer, listeners);
-            }
-            // Add the listener to the list.
-            listeners.add(listener);
-        }
-    }
-
-    /**
-     * Add a listener subscription to all data messages.
-     *
-     * @param listener a VMS subscriber.
-     */
-    public void addSubscription(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            ++mSequenceNumber;
-            mPromiscuousSubscribers.add(listener);
-        }
-    }
-
-    /**
-     * Remove a subscription for a layer + version and make sure to remove the key if there are no
-     * more subscribers.
-     *
-     * @param listener to remove.
-     * @param layer of the subscription.
-     */
-    public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
-        synchronized (mLock) {
-            ++mSequenceNumber;
-            Set<IVmsSubscriberClient> listeners = mLayerSubscriptions.get(layer);
-
-            // If there are no listeners we are done.
-            if (listeners == null) {
-                return;
-            }
-            listeners.remove(listener);
-
-            // If there are no more listeners then remove the list.
-            if (listeners.isEmpty()) {
-                mLayerSubscriptions.remove(layer);
-            }
-        }
-    }
-
-    /**
-     * Remove a listener subscription to all data messages.
-     *
-     * @param listener a VMS subscriber.
-     */
-    public void removeSubscription(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            ++mSequenceNumber;
-            mPromiscuousSubscribers.remove(listener);
-        }
-    }
-
-    /**
-     * Remove a subscriber from all routes (optional operation).
-     *
-     * @param listener a VMS subscriber.
-     */
-    public void removeDeadListener(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            // Remove the listener from all the routes.
-            for (VmsLayer layer : mLayerSubscriptions.keySet()) {
-                removeSubscription(listener, layer);
-            }
-            // Remove the listener from the loggers.
-            removeSubscription(listener);
-        }
-    }
-
-    /**
-     * Returns a list with all the listeners for a layer and version. This include the subscribers
-     * which explicitly subscribed to this layer and version and the promiscuous subscribers.
-     *
-     * @param layer to get listeners to.
-     * @return a list of the listeners.
-     */
-    public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) {
-        Set<IVmsSubscriberClient> listeners = new HashSet<>();
-        synchronized (mLock) {
-            // Add the subscribers which explicitly subscribed to this layer and version
-            if (mLayerSubscriptions.containsKey(layer)) {
-                listeners.addAll(mLayerSubscriptions.get(layer));
-            }
-            // Add the promiscuous subscribers.
-            listeners.addAll(mPromiscuousSubscribers);
-        }
-        return listeners;
-    }
-
-    /**
-     * Returns a list with all the listeners.
-     */
-    public Set<IVmsSubscriberClient> getAllListeners() {
-        Set<IVmsSubscriberClient> listeners = new HashSet<>();
-        synchronized (mLock) {
-            for (VmsLayer layer : mLayerSubscriptions.keySet()) {
-                listeners.addAll(mLayerSubscriptions.get(layer));
-            }
-            // Add the promiscuous subscribers.
-            listeners.addAll(mPromiscuousSubscribers);
-        }
-        return listeners;
-    }
-
-    /**
-     * Checks if a listener is subscribed to any messages.
-     * @param listener that may have subscription.
-     * @return true if the listener uis subscribed to messages.
-     */
-    public boolean containsListener(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            // Check if listener is subscribed to a layer.
-            for (Set<IVmsSubscriberClient> layerListeners: mLayerSubscriptions.values()) {
-                if (layerListeners.contains(listener)) {
-                    return true;
-                }
-            }
-            // Check is listener is subscribed to all data messages.
-            return mPromiscuousSubscribers.contains(listener);
-        }
-    }
-
-    /**
-     * Add a layer and version to the HAL subscriptions.
-     * @param layer the HAL subscribes to.
-     */
-    public void addHalSubscription(VmsLayer layer) {
-        synchronized (mLock) {
-            ++mSequenceNumber;
-            mHalSubscriptions.add(layer);
-        }
-    }
-
-    /**
-     * remove a layer and version to the HAL subscriptions.
-     * @param layer the HAL unsubscribes from.
-     */
-    public void removeHalSubscription(VmsLayer layer) {
-        synchronized (mLock) {
-            ++mSequenceNumber;
-            mHalSubscriptions.remove(layer);
-        }
-    }
-
-    /**
-     * checks if the HAL is subscribed to a layer.
-     * @param layer
-     * @return true if the HAL is subscribed to layer.
-     */
-    public boolean isHalSubscribed(VmsLayer layer) {
-        synchronized (mLock) {
-            return mHalSubscriptions.contains(layer);
-        }
-    }
-
-    /**
-     * checks if there are subscribers to a layer.
-     * @param layer
-     * @return true if there are subscribers to layer.
-     */
-    public boolean hasLayerSubscriptions(VmsLayer layer) {
-        synchronized (mLock) {
-            return mLayerSubscriptions.containsKey(layer) || mHalSubscriptions.contains(layer);
-        }
-    }
-
-    /**
-     * @return a Set of layers and versions which VMS clients are subscribed to.
-     */
-    public VmsSubscriptionState getSubscriptionState() {
-        synchronized (mLock) {
-            List<VmsLayer> layers = new ArrayList<>();
-            layers.addAll(mLayerSubscriptions.keySet());
-            layers.addAll(mHalSubscriptions);
-            return new VmsSubscriptionState(mSequenceNumber, layers);
-        }
-    }
-}
\ No newline at end of file
diff --git a/service/src/com/android/car/VmsSubscriberService.java b/service/src/com/android/car/VmsSubscriberService.java
deleted file mode 100644
index fc0a885..0000000
--- a/service/src/com/android/car/VmsSubscriberService.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-import android.car.Car;
-import android.car.annotation.FutureFeature;
-import android.car.vms.IVmsSubscriberClient;
-import android.car.vms.IVmsSubscriberService;
-import android.car.vms.VmsLayer;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.car.hal.VmsHalService;
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * + Receives HAL updates by implementing VmsHalService.VmsHalListener.
- * + Offers subscriber/publisher services by implementing IVmsService.Stub.
- */
-@FutureFeature
-public class VmsSubscriberService extends IVmsSubscriberService.Stub
-        implements CarServiceBase, VmsHalService.VmsHalSubscriberListener {
-    private static final boolean DBG = true;
-    private static final String PERMISSION = Car.PERMISSION_VMS_SUBSCRIBER;
-    private static final String TAG = "VmsSubscriberService";
-
-    private final Context mContext;
-    private final VmsHalService mHal;
-
-    @GuardedBy("mSubscriberServiceLock")
-    private final VmsListenerManager mMessageReceivedManager = new VmsListenerManager();
-    private final Object mSubscriberServiceLock = new Object();
-
-    /**
-     * Keeps track of listeners of this service.
-     */
-    class VmsListenerManager {
-        /**
-         * Allows to modify mListenerMap and mListenerDeathRecipientMap as a single unit.
-         */
-        private final Object mListenerManagerLock = new Object();
-        @GuardedBy("mListenerManagerLock")
-        private final Map<IBinder, ListenerDeathRecipient> mListenerDeathRecipientMap =
-                new HashMap<>();
-        @GuardedBy("mListenerManagerLock")
-        private final Map<IBinder, IVmsSubscriberClient> mListenerMap = new HashMap<>();
-
-        class ListenerDeathRecipient implements IBinder.DeathRecipient {
-            private IBinder mListenerBinder;
-
-            ListenerDeathRecipient(IBinder listenerBinder) {
-                mListenerBinder = listenerBinder;
-            }
-
-            /**
-             * Listener died. Remove it from this service.
-             */
-            @Override
-            public void binderDied() {
-                if (DBG) {
-                    Log.d(TAG, "binderDied " + mListenerBinder);
-                }
-
-                // Get the Listener from the Binder
-                IVmsSubscriberClient listener = mListenerMap.get(mListenerBinder);
-
-                // Remove the listener subscriptions.
-                if (listener != null) {
-                    Log.d(TAG, "Removing subscriptions for dead listener: " + listener);
-                    mHal.removeDeadListener(listener);
-                } else {
-                    Log.d(TAG, "Handling dead binder with no matching listener");
-
-                }
-
-                // Remove binder
-                VmsListenerManager.this.removeListener(mListenerBinder);
-            }
-
-            void release() {
-                mListenerBinder.unlinkToDeath(this, 0);
-            }
-        }
-
-        public void release() {
-            for (ListenerDeathRecipient recipient : mListenerDeathRecipientMap.values()) {
-                recipient.release();
-            }
-            mListenerDeathRecipientMap.clear();
-            mListenerMap.clear();
-        }
-
-        /**
-         * Adds the listener and a death recipient associated to it.
-         *
-         * @param listener to be added.
-         * @throws IllegalArgumentException if the listener is null.
-         * @throws IllegalStateException    if it was not possible to link a death recipient to the
-         *                                  listener.
-         */
-        public void add(IVmsSubscriberClient listener) {
-            ICarImpl.assertPermission(mContext, PERMISSION);
-            if (listener == null) {
-                Log.e(TAG, "register: listener is null.");
-                throw new IllegalArgumentException("listener cannot be null.");
-            }
-            if (DBG) {
-                Log.d(TAG, "register: " + listener);
-            }
-            IBinder listenerBinder = listener.asBinder();
-            synchronized (mListenerManagerLock) {
-                if (mListenerMap.containsKey(listenerBinder)) {
-                    // Already registered, nothing to do.
-                    return;
-                }
-                ListenerDeathRecipient deathRecipient = new ListenerDeathRecipient(listenerBinder);
-                try {
-                    listenerBinder.linkToDeath(deathRecipient, 0);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to link death for recipient. ", e);
-                    throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
-                }
-                mListenerDeathRecipientMap.put(listenerBinder, deathRecipient);
-                mListenerMap.put(listenerBinder, listener);
-            }
-        }
-
-        /**
-         * Removes the listener and associated death recipient.
-         *
-         * @param listener to be removed.
-         * @throws IllegalArgumentException if listener is null.
-         */
-        public void remove(IVmsSubscriberClient listener) {
-            if (DBG) {
-                Log.d(TAG, "unregisterListener");
-            }
-            ICarImpl.assertPermission(mContext, PERMISSION);
-            if (listener == null) {
-                Log.e(TAG, "unregister: listener is null.");
-                throw new IllegalArgumentException("Listener is null");
-            }
-            IBinder listenerBinder = listener.asBinder();
-            removeListener(listenerBinder);
-        }
-
-        // Removes the listenerBinder from the current state.
-        // The function assumes that binder will exist both in listeners and death recipients list.
-        private void removeListener(IBinder listenerBinder) {
-            synchronized (mListenerManagerLock) {
-                boolean found = mListenerMap.remove(listenerBinder) != null;
-                if (found) {
-                    mListenerDeathRecipientMap.get(listenerBinder).release();
-                    mListenerDeathRecipientMap.remove(listenerBinder);
-                } else {
-                    Log.e(TAG, "removeListener: listener was not previously registered.");
-                }
-            }
-        }
-
-        /**
-         * Returns list of listeners currently registered.
-         *
-         * @return list of listeners.
-         */
-        public List<IVmsSubscriberClient> getListeners() {
-            synchronized (mListenerManagerLock) {
-                return new ArrayList<>(mListenerMap.values());
-            }
-        }
-    }
-
-    public VmsSubscriberService(Context context, VmsHalService hal) {
-        mContext = context;
-        mHal = hal;
-    }
-
-    // Implements CarServiceBase interface.
-    @Override
-    public void init() {
-        mHal.addSubscriberListener(this);
-    }
-
-    @Override
-    public void release() {
-        mMessageReceivedManager.release();
-        mHal.removeSubscriberListener(this);
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-    }
-
-    // Implements IVmsService interface.
-    @Override
-    public void addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
-        synchronized (mSubscriberServiceLock) {
-            // Add the listener so it can subscribe.
-            mMessageReceivedManager.add(listener);
-
-            // Add the subscription for the layer.
-            mHal.addSubscription(listener, layer);
-        }
-    }
-
-    @Override
-    public void removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
-        synchronized (mSubscriberServiceLock) {
-            // Remove the subscription.
-            mHal.removeSubscription(listener, layer);
-
-            // Remove the listener if it has no more subscriptions.
-            if (!mHal.containsListener(listener)) {
-                mMessageReceivedManager.remove(listener);
-            }
-        }
-    }
-
-    @Override
-    public void addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
-        synchronized (mSubscriberServiceLock) {
-            mMessageReceivedManager.add(listener);
-            mHal.addSubscription(listener);
-        }
-    }
-
-    @Override
-    public void removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
-        synchronized (mSubscriberServiceLock) {
-            // Remove the subscription.
-            mHal.removeSubscription(listener);
-
-            // Remove the listener if it has no more subscriptions.
-            if (!mHal.containsListener(listener)) {
-                mMessageReceivedManager.remove(listener);
-            }
-        }
-    }
-
-    @Override
-    public byte[] getPublisherInfo(int publisherId) {
-        synchronized (mSubscriberServiceLock) {
-            return mHal.getPublisherInfo(publisherId);
-        }
-    }
-
-    @Override
-    public List<VmsLayer> getAvailableLayers() {
-        //TODO(asafro): return the list of available layers once logic is implemented.
-        return Collections.emptyList();
-    }
-
-    // Implements VmsHalSubscriberListener interface
-    @Override
-    public void onDataMessage(VmsLayer layer, byte[] payload) {
-        if(DBG) {
-            Log.d(TAG, "Publishing a message for layer: " + layer);
-        }
-
-        Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
-
-        for (IVmsSubscriberClient subscriber : listeners) {
-            try {
-                subscriber.onVmsMessageReceived(layer, payload);
-            } catch (RemoteException e) {
-                // If we could not send a record, its likely the connection snapped. Let the binder
-                // death handle the situation.
-                Log.e(TAG, "onVmsMessageReceived calling failed: ", e);
-            }
-        }
-    }
-
-    @Override
-    public void onLayersAvaiabilityChange(List<VmsLayer> availableLayers) {
-        if(DBG) {
-            Log.d(TAG, "Publishing layers availability change: " + availableLayers);
-        }
-
-        Set<IVmsSubscriberClient> listeners = mHal.getAllListeners();
-
-        for (IVmsSubscriberClient subscriber : listeners) {
-            try {
-                subscriber.onLayersAvailabilityChange(availableLayers);
-            } catch (RemoteException e) {
-                // If we could not send a record, its likely the connection snapped. Let the binder
-                // death handle the situation.
-                Log.e(TAG, "onLayersAvailabilityChange calling failed: ", e);
-            }
-        }
-    }
-}
diff --git a/service/src/com/android/car/VolumeUtils.java b/service/src/com/android/car/VolumeUtils.java
index 3164aea..265615b 100644
--- a/service/src/com/android/car/VolumeUtils.java
+++ b/service/src/com/android/car/VolumeUtils.java
@@ -43,6 +43,7 @@
             VehicleAudioContextFlag.NAVIGATION_FLAG,
             VehicleAudioContextFlag.VOICE_COMMAND_FLAG,
             VehicleAudioContextFlag.CALL_FLAG,
+            VehicleAudioContextFlag.RINGTONE_FLAG,
             VehicleAudioContextFlag.ALARM_FLAG,
             VehicleAudioContextFlag.NOTIFICATION_FLAG,
             VehicleAudioContextFlag.UNKNOWN_FLAG,
@@ -67,6 +68,8 @@
                 CarSettings.Global.KEY_VOLUME_VOICE_COMMAND);
         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.CALL_FLAG,
                 CarSettings.Global.KEY_VOLUME_CALL);
+        CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.RINGTONE_FLAG,
+                CarSettings.Global.KEY_VOLUME_RINGTONE);
         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.ALARM_FLAG,
                 CarSettings.Global.KEY_VOLUME_ALARM);
         CAR_AUDIO_CONTEXT_SETTINGS.put(
@@ -106,7 +109,7 @@
             case AudioManager.STREAM_SYSTEM:
                 return VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
             case AudioManager.STREAM_RING:
-                return VehicleAudioContextFlag.NOTIFICATION_FLAG;
+                return VehicleAudioContextFlag.RINGTONE_FLAG;
             case AudioManager.STREAM_MUSIC:
                 return VehicleAudioContextFlag.MUSIC_FLAG;
             case AudioManager.STREAM_ALARM:
@@ -124,6 +127,8 @@
         switch (carContext) {
             case VehicleAudioContextFlag.CALL_FLAG:
                 return AudioManager.STREAM_VOICE_CALL;
+            case VehicleAudioContextFlag.RINGTONE_FLAG:
+                return AudioManager.STREAM_RING;
             case VehicleAudioContextFlag.SYSTEM_SOUND_FLAG:
                 return AudioManager.STREAM_SYSTEM;
             case VehicleAudioContextFlag.NOTIFICATION_FLAG:
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index 8e0507b..eef177c 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -15,19 +15,34 @@
  */
 package com.android.car.cluster;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.car.Car;
 import android.car.CarAppFocusManager;
+import android.car.cluster.CarInstrumentClusterManager;
+import android.car.cluster.IInstrumentClusterManagerCallback;
+import android.car.cluster.IInstrumentClusterManagerService;
 import android.car.cluster.renderer.IInstrumentCluster;
+import android.car.cluster.renderer.IInstrumentClusterCallback;
 import android.car.cluster.renderer.IInstrumentClusterNavigation;
+import android.car.cluster.renderer.InstrumentClusterRenderingService;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Process;
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.view.KeyEvent;
 
 import com.android.car.AppFocusService;
@@ -40,6 +55,11 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Service responsible for interaction with car's instrument cluster.
@@ -54,14 +74,23 @@
     private static final Boolean DBG = false;
 
     private final Context mContext;
+
     private final AppFocusService mAppFocusService;
     private final CarInputService mCarInputService;
+    private final PackageManager mPackageManager;
     private final Object mSync = new Object();
 
+    private final ClusterServiceCallback mClusterCallback = new ClusterServiceCallback();
+    private final ClusterManagerService mClusterManagerService = new ClusterManagerService();
+
     @GuardedBy("mSync")
     private ContextOwner mNavContextOwner;
     @GuardedBy("mSync")
     private IInstrumentCluster mRendererService;
+    @GuardedBy("mSync")
+    private final HashMap<String, ClusterActivityInfo> mActivityInfoByCategory = new HashMap<>();
+    @GuardedBy("mSync")
+    private final HashMap<IBinder, ManagerCallbackInfo> mManagerCallbacks = new HashMap<>();
 
     private boolean mRendererBound = false;
 
@@ -85,7 +114,9 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             Log.d(TAG, "onServiceDisconnected, name: " + name);
-            mRendererService = null;
+            synchronized (mSync) {
+                mRendererService = null;
+            }
             // Try to rebind with instrument cluster.
             mRendererBound = bindInstrumentClusterRendererService();
         }
@@ -96,6 +127,7 @@
         mContext = context;
         mAppFocusService = appFocusService;
         mCarInputService = carInputService;
+        mPackageManager = mContext.getPackageManager();
     }
 
     @Override
@@ -188,6 +220,11 @@
 
         Intent intent = new Intent();
         intent.setComponent(ComponentName.unflattenFromString(rendererService));
+        Bundle extras = new Bundle();
+        extras.putBinder(
+                InstrumentClusterRenderingService.EXTRA_KEY_CALLBACK_SERVICE,
+                mClusterCallback);
+        intent.putExtras(extras);
         return mContext.bindService(intent, mRendererServiceConnection, Context.BIND_AUTO_CREATE);
     }
 
@@ -206,6 +243,10 @@
         }
     }
 
+    public IInstrumentClusterManagerService.Stub getManagerService() {
+        return mClusterManagerService;
+    }
+
     @Override
     public boolean onKeyEvent(KeyEvent event) {
         if (DBG) {
@@ -236,4 +277,247 @@
             this.pid = pid;
         }
     }
+
+    private static class ClusterActivityInfo {
+        Bundle launchOptions;  // ActivityOptions
+        Bundle state;          // ClusterActivityState
+    }
+
+    private void enforcePermission(String permission) {
+        int callingUid = Binder.getCallingUid();
+        int callingPid = Binder.getCallingPid();
+        if (Binder.getCallingUid() == Process.myUid()) {
+            if (mContext.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
+                throw new SecurityException("Permission " + permission + " is not granted to "
+                        + "client {uid: " + callingUid + ", pid: " + callingPid + "}");
+            }
+        }
+    }
+
+    private void enforceClusterControlPermission() {
+        enforcePermission(Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL);
+    }
+
+    private void doStartClusterActivity(Intent intent) {
+        enforceClusterControlPermission();
+
+        // Category from given intent should match category from cluster vendor implementation.
+        List<ResolveInfo> resolveList = mPackageManager.queryIntentActivities(intent,
+                PackageManager.GET_RESOLVED_FILTER);
+        if (resolveList == null || resolveList.isEmpty()) {
+            Log.w(TAG, "Failed to resolve an intent: " + intent);
+            return;
+        }
+
+        resolveList = checkPermission(resolveList, Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER);
+        if (resolveList.isEmpty()) {
+            return;
+        }
+
+        // TODO(b/63861009): we may have multiple navigation apps that eligible to be launched in
+        // the cluster. We need to resolve intent that may have multiple activity candidates, right
+        // now we pickup the first one that matches registered category (resolveList is sorted
+        // priority).
+        Pair<ResolveInfo, ClusterActivityInfo> attributedResolveInfo =
+                findClusterActivityOptions(resolveList);
+        if (attributedResolveInfo == null) {
+            Log.w(TAG, "Unable to start an activity with intent: " + intent + " in the cluster: "
+                    + "category intent didn't match with any categories from vendor "
+                    + "implementation");
+            return;
+        }
+        ClusterActivityInfo opts = attributedResolveInfo.second;
+
+        // Intent was already checked for permission and resolved, make it explicit.
+        intent.setComponent(attributedResolveInfo.first.getComponentInfo().getComponentName());
+
+        intent.putExtra(CarInstrumentClusterManager.KEY_EXTRA_ACTIVITY_STATE, opts.state);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // Virtual display could be private and not available to calling process.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mContext.startActivity(intent, opts.launchOptions);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private List<ResolveInfo> checkPermission(List<ResolveInfo> resolveList,
+            String permission) {
+        List<ResolveInfo> permittedResolveList = new ArrayList<>(resolveList.size());
+        for (ResolveInfo info : resolveList) {
+            String pkgName = info.getComponentInfo().packageName;
+            if (mPackageManager.checkPermission(permission, pkgName) == PERMISSION_GRANTED) {
+                permittedResolveList.add(info);
+            } else {
+                Log.w(TAG, "Permission " + permission + " not granted for "
+                        + info.getComponentInfo());
+            }
+
+        }
+        return permittedResolveList;
+    }
+
+    private void doRegisterManagerCallback(IInstrumentClusterManagerCallback callback)
+            throws RemoteException {
+        enforceClusterControlPermission();
+        IBinder binder = callback.asBinder();
+
+        List<Pair<String, Bundle>> knownActivityStates = null;
+        ManagerCallbackDeathRecipient deathRecipient = new ManagerCallbackDeathRecipient(binder);
+        synchronized (mSync) {
+            if (mManagerCallbacks.containsKey(binder)) {
+                Log.w(TAG, "Manager callback already registered for binder: " + binder);
+                return;
+            }
+            mManagerCallbacks.put(binder, new ManagerCallbackInfo(callback, deathRecipient));
+            if (!mActivityInfoByCategory.isEmpty()) {
+                knownActivityStates = new ArrayList<>(mActivityInfoByCategory.size());
+                for (Map.Entry<String, ClusterActivityInfo> it : mActivityInfoByCategory.entrySet()) {
+                    knownActivityStates.add(new Pair<>(it.getKey(), it.getValue().state));
+                }
+            }
+        }
+        binder.linkToDeath(deathRecipient, 0);
+
+        // Notify manager immediately with known states.
+        if (knownActivityStates != null) {
+            for (Pair<String, Bundle> it : knownActivityStates) {
+                callback.setClusterActivityState(it.first, it.second);
+            }
+        }
+    }
+
+    private void doUnregisterManagerCallback(IBinder binder) throws RemoteException {
+        enforceClusterControlPermission();
+        ManagerCallbackInfo info;
+        synchronized (mSync) {
+            info = mManagerCallbacks.get(binder);
+            if (info == null) {
+                Log.w(TAG, "Unable to unregister manager callback binder: " + binder + " because "
+                        + "it wasn't previously registered.");
+                return;
+            }
+            mManagerCallbacks.remove(binder);
+        }
+        binder.unlinkToDeath(info.deathRecipient, 0);
+    }
+
+    @Nullable
+    private Pair<ResolveInfo, ClusterActivityInfo> findClusterActivityOptions(
+            List<ResolveInfo> resolveList) {
+        synchronized (mSync) {
+            Set<String> registeredCategories = mActivityInfoByCategory.keySet();
+
+            for (ResolveInfo resolveInfo : resolveList) {
+                for (String category : registeredCategories) {
+                    if (resolveInfo.filter != null && resolveInfo.filter.hasCategory(category)) {
+                        ClusterActivityInfo categoryInfo = mActivityInfoByCategory.get(category);
+                        return new Pair<>(resolveInfo, categoryInfo);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private class ManagerCallbackDeathRecipient implements DeathRecipient {
+        private final IBinder mBinder;
+
+        ManagerCallbackDeathRecipient(IBinder binder) {
+            mBinder = binder;
+        }
+
+        @Override
+        public void binderDied() {
+            try {
+                doUnregisterManagerCallback(mBinder);
+            } catch (RemoteException e) {
+                // Ignore, shutdown route.
+            }
+        }
+    }
+
+    private class ClusterManagerService extends IInstrumentClusterManagerService.Stub {
+
+        @Override
+        public void startClusterActivity(Intent intent) throws RemoteException {
+            doStartClusterActivity(intent);
+        }
+
+        @Override
+        public void registerCallback(IInstrumentClusterManagerCallback callback)
+                throws RemoteException {
+            doRegisterManagerCallback(callback);
+        }
+
+        @Override
+        public void unregisterCallback(IInstrumentClusterManagerCallback callback)
+                throws RemoteException {
+            doUnregisterManagerCallback(callback.asBinder());
+        }
+    }
+
+    private ClusterActivityInfo getOrCreateActivityInfoLocked(String category) {
+        return mActivityInfoByCategory.computeIfAbsent(category, k -> new ClusterActivityInfo());
+    }
+
+    /** This is communication channel from vendor cluster implementation to Car Service. */
+    private class ClusterServiceCallback extends IInstrumentClusterCallback.Stub {
+
+        @Override
+        public void setClusterActivityLaunchOptions(String category, Bundle activityOptions)
+                throws RemoteException {
+            doSetActivityLaunchOptions(category, activityOptions);
+        }
+
+        @Override
+        public void setClusterActivityState(String category, Bundle clusterActivityState)
+                throws RemoteException {
+            doSetClusterActivityState(category, clusterActivityState);
+        }
+    }
+
+    /** Called from cluster vendor implementation */
+    private void doSetActivityLaunchOptions(String category, Bundle activityOptions) {
+        if (DBG) {
+            Log.d(TAG, "doSetActivityLaunchOptions, category: " + category
+                    + ", options: " + activityOptions);
+        }
+        synchronized (mSync) {
+            ClusterActivityInfo info = getOrCreateActivityInfoLocked(category);
+            info.launchOptions = activityOptions;
+        }
+    }
+
+    /** Called from cluster vendor implementation */
+    private void doSetClusterActivityState(String category, Bundle clusterActivityState)
+            throws RemoteException {
+        if (DBG) {
+            Log.d(TAG, "doSetClusterActivityState, category: " + category
+                    + ", state: " + clusterActivityState);
+        }
+
+        List<ManagerCallbackInfo> managerCallbacks;
+        synchronized (mSync) {
+            ClusterActivityInfo info = getOrCreateActivityInfoLocked(category);
+            info.state = clusterActivityState;
+            managerCallbacks = new ArrayList<>(mManagerCallbacks.values());
+        }
+
+        for (ManagerCallbackInfo cbInfo : managerCallbacks) {
+            cbInfo.callback.setClusterActivityState(category, clusterActivityState);
+        }
+    }
+
+    private static class ManagerCallbackInfo {
+        final IInstrumentClusterManagerCallback callback;
+        final ManagerCallbackDeathRecipient deathRecipient;
+
+        ManagerCallbackInfo(IInstrumentClusterManagerCallback callback,
+                ManagerCallbackDeathRecipient deathRecipient) {
+            this.callback = callback;
+            this.deathRecipient = deathRecipient;
+        }
+    }
 }
diff --git a/service/src/com/android/car/hal/AudioHalService.java b/service/src/com/android/car/hal/AudioHalService.java
index 14b8319..879bb6c 100644
--- a/service/src/com/android/car/hal/AudioHalService.java
+++ b/service/src/com/android/car/hal/AudioHalService.java
@@ -101,9 +101,12 @@
 
     public static final int STREAM_NUM_DEFAULT = 0;
 
-    public static final int FOCUS_STATE_ARRAY_INDEX_STATE = 0;
-    public static final int FOCUS_STATE_ARRAY_INDEX_STREAMS = 1;
-    public static final int FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS = 2;
+    public static final int FOCUS_STATE_ARRAY_INDEX_STATE =
+            VehicleAudioFocusIndex.FOCUS;
+    public static final int FOCUS_STATE_ARRAY_INDEX_STREAMS =
+            VehicleAudioFocusIndex.STREAMS;
+    public static final int FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS =
+            VehicleAudioFocusIndex.EXTERNAL_FOCUS_STATE;
 
     public static final int AUDIO_CONTEXT_MUSIC_FLAG =
             VehicleAudioContextFlag.MUSIC_FLAG;
@@ -131,6 +134,8 @@
             VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
     public static final int AUDIO_CONTEXT_EXT_SOURCE_FLAG =
             VehicleAudioContextFlag.EXT_SOURCE_FLAG;
+    public static final int AUDIO_CONTEXT_RINGTONE_FLAG =
+            VehicleAudioContextFlag.RINGTONE_FLAG;
 
     public interface AudioHalFocusListener {
         /**
@@ -261,6 +266,8 @@
                 return VehicleAudioContextFlag.RADIO_FLAG;
             case CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL:
                 return VehicleAudioContextFlag.CALL_FLAG;
+            case CarAudioManager.CAR_AUDIO_USAGE_RINGTONE:
+                return VehicleAudioContextFlag.RINGTONE_FLAG;
             case CarAudioManager.CAR_AUDIO_USAGE_MUSIC:
                 return VehicleAudioContextFlag.MUSIC_FLAG;
             case CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE:
@@ -281,15 +288,15 @@
                 if (extType != null) {
                     switch (extType) {
                     case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_CD_DVD:
-                        return AudioHalService.AUDIO_CONTEXT_CD_ROM_FLAG;
+                        return VehicleAudioContextFlag.CD_ROM_FLAG;
                     case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0:
                     case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN1:
-                        return AudioHalService.AUDIO_CONTEXT_AUX_AUDIO_FLAG;
+                        return VehicleAudioContextFlag.AUX_AUDIO_FLAG;
                     default:
                         if (extType.startsWith("RADIO_")) {
                             return VehicleAudioContextFlag.RADIO_FLAG;
                         } else {
-                            return AudioHalService.AUDIO_CONTEXT_EXT_SOURCE_FLAG;
+                            return VehicleAudioContextFlag.EXT_SOURCE_FLAG;
                         }
                     }
                 } else { // no external source specified. fall back to radio
@@ -323,6 +330,8 @@
                 return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
             case VehicleAudioContextFlag.CALL_FLAG:
                 return CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL;
+            case VehicleAudioContextFlag.RINGTONE_FLAG:
+                return CarAudioManager.CAR_AUDIO_USAGE_RINGTONE;
             case VehicleAudioContextFlag.CD_ROM_FLAG:
                 return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
             case VehicleAudioContextFlag.NOTIFICATION_FLAG:
@@ -476,7 +485,7 @@
             VehiclePropValue propValue = mVehicleHal.get(VehicleProperty.AUDIO_FOCUS);
             return toIntArray(propValue.value.int32Values);
         } catch (PropertyTimeoutException e) {
-            Log.e(CarLog.TAG_AUDIO, "VehicleProperty.AUDIO_HW_VARIANT not ready", e);
+            Log.e(CarLog.TAG_AUDIO, "VehicleProperty.AUDIO_FOCUS not ready", e);
             return new int[] { VEHICLE_AUDIO_FOCUS_STATE_LOSS, 0x0, 0};
         }
     }
@@ -708,9 +717,9 @@
                 } break;
                 case AUDIO_VOLUME: {
                     ArrayList<Integer> vec = v.value.int32Values;
-                    int volume = vec.get(VehicleAudioVolumeIndex.INDEX_VOLUME);
-                    int streamNum = vec.get(VehicleAudioVolumeIndex.INDEX_STREAM);
-                    int volumeState = vec.get(VehicleAudioVolumeIndex.INDEX_STATE);
+                    int streamNum = vec.get(VehicleAudioVolumeIndex.STREAM);
+                    int volume = vec.get(VehicleAudioVolumeIndex.VOLUME);
+                    int volumeState = vec.get(VehicleAudioVolumeIndex.STATE);
                     if (volumeListener != null) {
                         volumeListener.onVolumeChange(streamNum, volume, volumeState);
                     }
diff --git a/service/src/com/android/car/hal/DiagnosticHalService.java b/service/src/com/android/car/hal/DiagnosticHalService.java
index 98a3b3d..4ff0ada 100644
--- a/service/src/com/android/car/hal/DiagnosticHalService.java
+++ b/service/src/com/android/car/hal/DiagnosticHalService.java
@@ -17,26 +17,22 @@
 package com.android.car.hal;
 
 import android.annotation.Nullable;
-import android.car.annotation.FutureFeature;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
 import android.car.hardware.CarSensorManager;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.Obd2FloatSensorIndex;
-import android.hardware.automotive.vehicle.V2_1.Obd2IntegerSensorIndex;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticFloatSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticIntegerSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.util.Log;
 import android.util.SparseArray;
 import com.android.car.CarLog;
 import com.android.car.CarServiceUtils;
 import com.android.car.vehiclehal.VehiclePropValueBuilder;
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.BitSet;
-import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -45,7 +41,6 @@
  * Diagnostic HAL service supporting gathering diagnostic info from VHAL and translating it into
  * higher-level semantic information
  */
-@FutureFeature
 public class DiagnosticHalService extends SensorHalServiceBase {
     public static class DiagnosticCapabilities {
         private final CopyOnWriteArraySet<Integer> mProperties = new CopyOnWriteArraySet<>();
@@ -129,7 +124,7 @@
     }
 
     private int getNumIntegerSensors(int halPropId) {
-        int count = Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1;
+        int count = DiagnosticIntegerSensorIndex.LAST_SYSTEM_INDEX + 1;
         List<Integer> configArray = getPropConfigArray(halPropId);
         if(configArray.size() < 2) {
             Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
@@ -143,7 +138,7 @@
     }
 
     private int getNumFloatSensors(int halPropId) {
-        int count = Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1;
+        int count = DiagnosticFloatSensorIndex.LAST_SYSTEM_INDEX + 1;
         List<Integer> configArray = getPropConfigArray(halPropId);
         if(configArray.size() < 2) {
             Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
@@ -185,7 +180,7 @@
             }
         }
 
-        builder.withDTC(value.value.stringValue);
+        builder.withDtc(value.value.stringValue);
 
         return builder.build();
     }
diff --git a/service/src/com/android/car/hal/HvacHalService.java b/service/src/com/android/car/hal/HvacHalService.java
index d364850..77ab1be 100644
--- a/service/src/com/android/car/hal/HvacHalService.java
+++ b/service/src/com/android/car/hal/HvacHalService.java
@@ -23,44 +23,46 @@
     private static final String TAG = "HvacHalService";
 
     private final ManagerToHalPropIdMap mMgrHalPropIdMap = ManagerToHalPropIdMap.create(
-           CarHvacManager.ID_MIRROR_DEFROSTER_ON, VehicleProperty.HVAC_SIDE_MIRROR_HEAT,
+            CarHvacManager.ID_MIRROR_DEFROSTER_ON, VehicleProperty.HVAC_SIDE_MIRROR_HEAT,
 
-           CarHvacManager.ID_STEERING_WHEEL_TEMP, VehicleProperty.HVAC_STEERING_WHEEL_TEMP,
+            CarHvacManager.ID_STEERING_WHEEL_TEMP, VehicleProperty.HVAC_STEERING_WHEEL_TEMP,
 
-           CarHvacManager.ID_OUTSIDE_AIR_TEMP, VehicleProperty.ENV_OUTSIDE_TEMPERATURE,
+            CarHvacManager.ID_OUTSIDE_AIR_TEMP, VehicleProperty.ENV_OUTSIDE_TEMPERATURE,
 
-           CarHvacManager.ID_TEMPERATURE_UNITS, VehicleProperty.HVAC_TEMPERATURE_UNITS,
+            CarHvacManager.ID_TEMPERATURE_UNITS, VehicleProperty.HVAC_TEMPERATURE_UNITS,
 
-           CarHvacManager.ID_ZONED_TEMP_SETPOINT, VehicleProperty.HVAC_TEMPERATURE_SET,
+            CarHvacManager.ID_ZONED_TEMP_SETPOINT, VehicleProperty.HVAC_TEMPERATURE_SET,
 
-           CarHvacManager.ID_ZONED_TEMP_ACTUAL, VehicleProperty.HVAC_TEMPERATURE_CURRENT,
+            CarHvacManager.ID_ZONED_TEMP_ACTUAL, VehicleProperty.HVAC_TEMPERATURE_CURRENT,
 
-           CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, VehicleProperty.HVAC_FAN_SPEED,
+            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_SPEED_RPM, VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM,
 
-           CarHvacManager.ID_ZONED_FAN_POSITION_AVAILABLE,
-           VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE,
+            CarHvacManager.ID_ZONED_FAN_POSITION_AVAILABLE,
+            VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE,
 
-           CarHvacManager.ID_ZONED_FAN_POSITION, VehicleProperty.HVAC_FAN_DIRECTION,
+            CarHvacManager.ID_ZONED_FAN_POSITION, VehicleProperty.HVAC_FAN_DIRECTION,
 
-           CarHvacManager.ID_ZONED_SEAT_TEMP, VehicleProperty.HVAC_SEAT_TEMPERATURE,
+            CarHvacManager.ID_ZONED_SEAT_TEMP, VehicleProperty.HVAC_SEAT_TEMPERATURE,
 
-           CarHvacManager.ID_ZONED_AC_ON, VehicleProperty.HVAC_AC_ON,
+            CarHvacManager.ID_ZONED_AC_ON, VehicleProperty.HVAC_AC_ON,
 
-           CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON, VehicleProperty.HVAC_AUTO_ON,
+            CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON, VehicleProperty.HVAC_AUTO_ON,
 
-           CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,VehicleProperty.HVAC_RECIRC_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_MAX_AC_ON, VehicleProperty.HVAC_MAX_AC_ON,
 
-           CarHvacManager.ID_ZONED_DUAL_ZONE_ON, VehicleProperty.HVAC_DUAL_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_MAX_DEFROST_ON, VehicleProperty.HVAC_MAX_DEFROST_ON,
 
-           CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleProperty.HVAC_POWER_ON,
+            CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleProperty.HVAC_POWER_ON,
 
-           CarHvacManager.ID_WINDOW_DEFROSTER_ON, VehicleProperty.HVAC_DEFROSTER
+            CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON, VehicleProperty.HVAC_AUTO_RECIRC_ON,
+
+            CarHvacManager.ID_WINDOW_DEFROSTER_ON, VehicleProperty.HVAC_DEFROSTER
     );
 
     public HvacHalService(VehicleHal vehicleHal) {
diff --git a/service/src/com/android/car/hal/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
index a4fd77d..6ee56a4 100644
--- a/service/src/com/android/car/hal/SensorHalService.java
+++ b/service/src/com/android/car/hal/SensorHalService.java
@@ -19,6 +19,7 @@
 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;
@@ -29,11 +30,13 @@
 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;
 
@@ -50,6 +53,7 @@
     public interface SensorListener {
         /**
          * Sensor events are available.
+         *
          * @param events
          */
         void onSensorEvents(List<CarSensorEvent> events);
@@ -57,42 +61,67 @@
 
     // 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_DRIVING_STATUS, VehicleProperty.DRIVING_STATUS,
-                    CarSensorManager.SENSOR_TYPE_FUEL_LEVEL, VehicleProperty.FUEL_LEVEL_LOW,
-                    CarSensorManager.SENSOR_TYPE_IGNITION_STATE, VehicleProperty.IGNITION_STATE);
+        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_DRIVING_STATUS, VehicleProperty.DRIVING_STATUS,
+            CarSensorManager.SENSOR_TYPE_FUEL_LEVEL, VehicleProperty.FUEL_LEVEL_LOW,
+            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
+        );
 
     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_LOW, CarSensorEvent.GEAR_FIRST, // Also GEAR_1 - the value is the same.
-            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);
+        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_LOW, CarSensorEvent.GEAR_FIRST, // Also GEAR_1 - the value is the same.
+        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);
+        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);
     }
@@ -114,6 +143,7 @@
 
     // 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) {
@@ -142,7 +172,7 @@
                 mgrValue = mMgrGearToHalMap.get(halValue, -1);
                 break;
             case VehicleProperty.IGNITION_STATE:
-                mgrValue =  mMgrIgnitionStateToHalMap.get(halValue, -1);
+                mgrValue = mMgrIgnitionStateToHalMap.get(halValue, -1);
             default:
                 break; // Do nothing
         }
@@ -156,27 +186,45 @@
         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);
+                    v.value.int32Values.get(0) == 1);
+                break;
+            case VehiclePropertyType.COMPLEX:
+                event = CarSensorEventFactory.createComplexEvent(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);
+                event = mgrVal == null ? null
+                    : CarSensorEventFactory.createIntEvent(sensorType, v.timestamp, mgrVal);
                 break;
-            case VehiclePropertyType.FLOAT: {
+            case VehiclePropertyType.FLOAT:
                 event = CarSensorEventFactory.createFloatEvent(sensorType, v.timestamp,
-                        v.value.floatValues.get(0));
+                    v.value.floatValues.get(0));
                 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;
@@ -225,6 +273,9 @@
         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) {
@@ -239,4 +290,50 @@
         }
         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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index c60d16d..44c81d7 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -78,12 +78,10 @@
     private final HvacHalService mHvacHal;
     private final InputHalService mInputHal;
     private final VendorExtensionHalService mVendorExtensionHal;
-    @FutureFeature
-    private VmsHalService mVmsHal;
-
-    @FutureFeature
     private DiagnosticHalService mDiagnosticHal = null;
 
+
+
     /** Might be re-assigned if Vehicle HAL is reconnected. */
     private volatile HalClient mHalClient;
 
@@ -108,9 +106,6 @@
         mHvacHal = new HvacHalService(this);
         mInputHal = new InputHalService(this);
         mVendorExtensionHal = new VendorExtensionHalService(this);
-        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-            mVmsHal = new VmsHalService(this);
-        }
         mDiagnosticHal = new DiagnosticHalService(this);
         mAllServices.addAll(Arrays.asList(mPowerHal,
                 mSensorHal,
@@ -122,9 +117,6 @@
                 mInputHal,
                 mVendorExtensionHal,
                 mDiagnosticHal));
-        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-            mAllServices.add(mVmsHal);
-        }
 
         mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/);
     }
@@ -146,16 +138,11 @@
         mVendorExtensionHal = null;
         mDiagnosticHal = null;
 
-        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-            mVmsHal = null;
-        }
-
         mHalClient = halClient;
     }
 
     /** Dummy version only for testing */
     @VisibleForTesting
-    @FutureFeature
     public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal,
             AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal,
             RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) {
@@ -170,7 +157,6 @@
             mHvacHal = hvacHal;
             mInputHal = null;
             mVendorExtensionHal = null;
-            mVmsHal = null;
             mHalClient = halClient;
             mDiagnosticHal = diagnosticHal;
     }
@@ -260,7 +246,6 @@
         return mCabinHal;
     }
 
-    @FutureFeature
     public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; }
 
     public RadioHalService getRadioHal() {
@@ -283,9 +268,6 @@
         return mVendorExtensionHal;
     }
 
-    @FutureFeature
-    public VmsHalService getVmsHal() { return mVmsHal; }
-
     private void assertServiceOwnerLocked(HalServiceBase service, int property) {
         if (service != mPropertyHandlers.get(property)) {
             throw new IllegalArgumentException("Property 0x" + toHexString(property)
diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java
deleted file mode 100644
index 8ab5427..0000000
--- a/service/src/com/android/car/hal/VmsHalService.java
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.car.hal;
-
-import static com.android.car.CarServiceUtils.toByteArray;
-import static java.lang.Integer.toHexString;
-
-import android.car.VehicleAreaType;
-import android.car.annotation.FutureFeature;
-import android.car.vms.IVmsSubscriberClient;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsLayerDependency;
-import android.car.vms.VmsLayersOffering;
-import android.car.vms.VmsSubscriptionState;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-import android.hardware.automotive.vehicle.V2_1.VmsOfferingMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
-import android.os.Binder;
-import android.os.IBinder;
-import android.util.Log;
-import com.android.car.CarLog;
-import com.android.car.VmsLayersAvailability;
-import com.android.car.VmsPublishersInfo;
-import com.android.car.VmsRouting;
-import com.android.internal.annotations.GuardedBy;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * This is a glue layer between the VehicleHal and the VmsService. It sends VMS properties back and
- * forth.
- */
-@FutureFeature
-public class VmsHalService extends HalServiceBase {
-
-    private static final boolean DBG = true;
-    private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE;
-    private static final String TAG = "VmsHalService";
-
-    private boolean mIsSupported = false;
-    private CopyOnWriteArrayList<VmsHalPublisherListener> mPublisherListeners =
-        new CopyOnWriteArrayList<>();
-    private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners =
-        new CopyOnWriteArrayList<>();
-
-    private final IBinder mHalPublisherToken = new Binder();
-    private final VehicleHal mVehicleHal;
-
-    private final Object mLock = new Object();
-    private final VmsRouting mRouting = new VmsRouting();
-    @GuardedBy("mLock")
-    private final Map<IBinder, VmsLayersOffering> mOfferings = new HashMap<>();
-    @GuardedBy("mLock")
-    private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
-    private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo();
-
-    /**
-     * The VmsPublisherService implements this interface to receive data from the HAL.
-     */
-    public interface VmsHalPublisherListener {
-        void onChange(VmsSubscriptionState subscriptionState);
-    }
-
-    /**
-     * The VmsSubscriberService implements this interface to receive data from the HAL.
-     */
-    public interface VmsHalSubscriberListener {
-        // Notify listener on a data Message.
-        void onDataMessage(VmsLayer layer, byte[] payload);
-
-        // Notify listener on a change in available layers.
-        void onLayersAvaiabilityChange(List<VmsLayer> availableLayers);
-    }
-
-    /**
-     * The VmsService implements this interface to receive data from the HAL.
-     */
-    protected VmsHalService(VehicleHal vehicleHal) {
-        mVehicleHal = vehicleHal;
-        if (DBG) {
-            Log.d(TAG, "started VmsHalService!");
-        }
-    }
-
-    public void addPublisherListener(VmsHalPublisherListener listener) {
-        mPublisherListeners.add(listener);
-    }
-
-    public void addSubscriberListener(VmsHalSubscriberListener listener) {
-        mSubscriberListeners.add(listener);
-    }
-
-    public void removePublisherListener(VmsHalPublisherListener listener) {
-        mPublisherListeners.remove(listener);
-    }
-
-    public void removeSubscriberListener(VmsHalSubscriberListener listener) {
-        mSubscriberListeners.remove(listener);
-    }
-
-    public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
-        boolean firstSubscriptionForLayer = false;
-        synchronized (mLock) {
-            // Check if publishers need to be notified about this change in subscriptions.
-            firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
-
-            // Add the listeners subscription to the layer
-            mRouting.addSubscription(listener, layer);
-        }
-        if (firstSubscriptionForLayer) {
-            notifyPublishers(layer, true);
-        }
-    }
-
-    public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
-        boolean layerHasSubscribers = true;
-        synchronized (mLock) {
-            if (!mRouting.hasLayerSubscriptions(layer)) {
-                Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
-                return;
-            }
-
-            // Remove the listeners subscription to the layer
-            mRouting.removeSubscription(listener, layer);
-
-            // Check if publishers need to be notified about this change in subscriptions.
-            layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
-        }
-        if (!layerHasSubscribers) {
-            notifyPublishers(layer, false);
-        }
-    }
-
-    public void addSubscription(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            mRouting.addSubscription(listener);
-        }
-    }
-
-    public void removeSubscription(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            mRouting.removeSubscription(listener);
-        }
-    }
-
-    public void removeDeadListener(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            mRouting.removeDeadListener(listener);
-        }
-    }
-
-    public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) {
-        synchronized (mLock) {
-            return mRouting.getListeners(layer);
-        }
-    }
-
-    public Set<IVmsSubscriberClient> getAllListeners() {
-        synchronized (mLock) {
-            return mRouting.getAllListeners();
-        }
-    }
-
-    public boolean isHalSubscribed(VmsLayer layer) {
-        synchronized (mLock) {
-            return mRouting.isHalSubscribed(layer);
-        }
-    }
-
-    public VmsSubscriptionState getSubscriptionState() {
-        synchronized (mLock) {
-            return mRouting.getSubscriptionState();
-        }
-    }
-
-    /**
-     * Assigns an idempotent ID for publisherInfo and stores it. The idempotency in this case means
-     * that the same publisherInfo will always, within a trip of the vehicle, return the same ID.
-     * The publisherInfo should be static for a binary and should only change as part of a software
-     * update. The publisherInfo is a serialized proto message which VMS clients can interpret.
-     */
-    public int getPublisherStaticId(byte[] publisherInfo) {
-        if (DBG) {
-            Log.i(TAG, "Getting publisher static ID");
-        }
-        synchronized (mLock) {
-            return mPublishersInfo.getIdForInfo(publisherInfo);
-        }
-    }
-
-    public byte[] getPublisherInfo(int publisherId) {
-        if (DBG) {
-            Log.i(TAG, "Getting information for publisher ID: " + publisherId);
-        }
-        synchronized (mLock) {
-            return mPublishersInfo.getPublisherInfo(publisherId);
-        }
-    }
-
-    public void addHalSubscription(VmsLayer layer) {
-        boolean firstSubscriptionForLayer = true;
-        synchronized (mLock) {
-            // Check if publishers need to be notified about this change in subscriptions.
-            firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
-
-            // Add the listeners subscription to the layer
-            mRouting.addHalSubscription(layer);
-        }
-        if (firstSubscriptionForLayer) {
-            notifyPublishers(layer, true);
-        }
-    }
-
-    public void removeHalSubscription(VmsLayer layer) {
-        boolean layerHasSubscribers = true;
-        synchronized (mLock) {
-            if (!mRouting.hasLayerSubscriptions(layer)) {
-                Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
-                return;
-            }
-
-            // Remove the listeners subscription to the layer
-            mRouting.removeHalSubscription(layer);
-
-            // Check if publishers need to be notified about this change in subscriptions.
-            layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
-        }
-        if (!layerHasSubscribers) {
-            notifyPublishers(layer, false);
-        }
-    }
-
-    public boolean containsListener(IVmsSubscriberClient listener) {
-        synchronized (mLock) {
-            return mRouting.containsListener(listener);
-        }
-    }
-
-    public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering){
-        Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
-        synchronized (mLock) {
-            updateOffering(publisherToken, offering);
-            availableLayers = mAvailableLayers.getAvailableLayers();
-        }
-        notifySubscribers(availableLayers);
-    }
-
-    public Set<VmsLayer> getAvailableLayers() {
-        //TODO(b/36872877): wrap available layers in VmsAvailabilityState similar to VmsSubscriptionState.
-        synchronized (mLock) {
-            return mAvailableLayers.getAvailableLayers();
-        }
-    }
-
-    /**
-     * Notify all the publishers and the HAL on subscription changes regardless of who triggered
-     * the change.
-     *
-     * @param layer          layer which is being subscribed to or unsubscribed from.
-     * @param hasSubscribers indicates if the notification is for subscription or unsubscription.
-     */
-    private void notifyPublishers(VmsLayer layer, boolean hasSubscribers) {
-        // notify the HAL
-        setSubscriptionRequest(layer, hasSubscribers);
-
-        // Notify the App publishers
-        for (VmsHalPublisherListener listener : mPublisherListeners) {
-            // Besides the list of layers, also a timestamp is provided to the clients.
-            // They should ignore any notification with a timestamp that is older than the most
-            // recent timestamp they have seen.
-            listener.onChange(getSubscriptionState());
-        }
-    }
-
-    /**
-     * Notify all the subscribers and the HAL on layers availability change.
-     *
-     * @param availableLayers the layers which publishers claim they made publish.
-     */
-    private void notifySubscribers(Set<VmsLayer> availableLayers) {
-        // notify the HAL
-        setAvailableLayers(availableLayers);
-
-        // Notify the App subscribers
-        for (VmsHalSubscriberListener listener : mSubscriberListeners) {
-            listener.onLayersAvaiabilityChange(new ArrayList<>(availableLayers));
-        }
-    }
-
-    @Override
-    public void init() {
-        if (DBG) {
-            Log.d(TAG, "init()");
-        }
-        if (mIsSupported) {
-            mVehicleHal.subscribeProperty(this, HAL_PROPERTY_ID);
-        }
-    }
-
-    @Override
-    public void release() {
-        if (DBG) {
-            Log.d(TAG, "release()");
-        }
-        if (mIsSupported) {
-            mVehicleHal.unsubscribeProperty(this, HAL_PROPERTY_ID);
-        }
-        mPublisherListeners.clear();
-        mSubscriberListeners.clear();
-    }
-
-    @Override
-    public Collection<VehiclePropConfig> takeSupportedProperties(
-            Collection<VehiclePropConfig> allProperties) {
-        List<VehiclePropConfig> taken = new LinkedList<>();
-        for (VehiclePropConfig p : allProperties) {
-            if (p.prop == HAL_PROPERTY_ID) {
-                taken.add(p);
-                mIsSupported = true;
-                if (DBG) {
-                    Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop));
-                }
-                break;
-            }
-        }
-        return taken;
-    }
-
-    /**
-     * Consumes/produces HAL messages. The format of these messages is defined in:
-     * hardware/interfaces/automotive/vehicle/2.1/types.hal
-     */
-    @Override
-    public void handleHalEvents(List<VehiclePropValue> values) {
-        if (DBG) {
-            Log.d(TAG, "Handling a VMS property change");
-        }
-        for (VehiclePropValue v : values) {
-            ArrayList<Integer> vec = v.value.int32Values;
-            int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
-
-            if (DBG) {
-                Log.d(TAG, "Handling VMS message type: " + messageType);
-            }
-            switch(messageType) {
-                case VmsMessageType.DATA:
-                    handleDataEvent(vec, toByteArray(v.value.bytes));
-                    break;
-                case VmsMessageType.SUBSCRIBE:
-                    handleSubscribeEvent(vec);
-                    break;
-                case VmsMessageType.UNSUBSCRIBE:
-                    handleUnsubscribeEvent(vec);
-                    break;
-                case VmsMessageType.OFFERING:
-                    handleOfferingEvent(vec);
-                    break;
-                case VmsMessageType.AVAILABILITY_REQUEST:
-                    handleAvailabilityEvent();
-                    break;
-                case VmsMessageType.SUBSCRIPTION_REQUEST:
-                    handleSubscriptionRequestEvent();
-                    break;
-                default:
-                    throw new IllegalArgumentException("Unexpected message type: " + messageType);
-            }
-        }
-    }
-
-    /**
-     * Data message format:
-     * <ul>
-     * <li>Message type.
-     * <li>Layer id.
-     * <li>Layer version.
-     * <li>Payload.
-     * </ul>
-     */
-    private void handleDataEvent(List<Integer> integerValues, byte[] payload) {
-        int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
-        int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
-        if (DBG) {
-            Log.d(TAG,
-                "Handling a data event for Layer Id: " + layerId +
-                    " Version: " + layerVersion);
-        }
-
-        // Send the message.
-        for (VmsHalSubscriberListener listener : mSubscriberListeners) {
-            listener.onDataMessage(new VmsLayer(layerId, layerVersion), payload);
-        }
-    }
-
-    /**
-     * Subscribe message format:
-     * <ul>
-     * <li>Message type.
-     * <li>Layer id.
-     * <li>Layer version.
-     * </ul>
-     */
-    private void handleSubscribeEvent(List<Integer> integerValues) {
-        int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
-        int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
-        if (DBG) {
-            Log.d(TAG,
-                "Handling a subscribe event for Layer Id: " + layerId +
-                    " Version: " + layerVersion);
-        }
-        addHalSubscription(new VmsLayer(layerId, layerVersion));
-    }
-
-    /**
-     * Unsubscribe message format:
-     * <ul>
-     * <li>Message type.
-     * <li>Layer id.
-     * <li>Layer version.
-     * </ul>
-     */
-    private void handleUnsubscribeEvent(List<Integer> integerValues) {
-        int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
-        int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
-        if (DBG) {
-            Log.d(TAG,
-                "Handling an unsubscribe event for Layer Id: " + layerId +
-                    " Version: " + layerVersion);
-        }
-        removeHalSubscription(new VmsLayer(layerId, layerVersion));
-    }
-
-    /**
-     * Offering message format:
-     * <ul>
-     * <li>Message type.
-     * <li>Number of offerings.
-     * <li>Each offering consists of:
-     *   <ul>
-     *   <li>Layer id.
-     *   <li>Layer version.
-     *   <li>Number of layer dependencies.
-     *   <li>Layer type/version pairs.
-     *   </ul>
-     * </ul>
-     */
-    private void handleOfferingEvent(List<Integer> integerValues) {
-        int numLayersDependencies =
-            integerValues.get(VmsOfferingMessageIntegerValuesIndex.VMS_NUMBER_OF_LAYERS_DEPENDENCIES);
-        int idx = VmsOfferingMessageIntegerValuesIndex.FIRST_DEPENDENCIES_INDEX;
-
-        List<VmsLayerDependency> offeredLayers = new ArrayList<>();
-
-        // An offering is layerId, LayerVersion, NumDeps, <LayerId, LayerVersion> X NumDeps.
-        for (int i = 0; i < numLayersDependencies; i++) {
-            int layerId = integerValues.get(idx++);
-            int layerVersion = integerValues.get(idx++);
-            VmsLayer offeredLayer = new VmsLayer(layerId, layerVersion);
-
-            int numDependenciesForLayer = integerValues.get(idx++);
-            if (numDependenciesForLayer == 0) {
-                offeredLayers.add(new VmsLayerDependency(offeredLayer));
-            } else {
-                Set<VmsLayer> dependencies = new HashSet<>();
-
-                for (int j = 0; j < numDependenciesForLayer; j++) {
-                    int dependantLayerId = integerValues.get(idx++);
-                    int dependantLayerVersion = integerValues.get(idx++);
-
-                    VmsLayer dependantLayer = new VmsLayer(dependantLayerId, dependantLayerVersion);
-                    dependencies.add(dependantLayer);
-                }
-                offeredLayers.add(new VmsLayerDependency(offeredLayer, dependencies));
-            }
-        }
-        // Store the HAL offering.
-        VmsLayersOffering offering = new VmsLayersOffering(offeredLayers);
-        synchronized (mLock) {
-            updateOffering(mHalPublisherToken, offering);
-        }
-    }
-
-    /**
-     * Availability message format:
-     * <ul>
-     * <li>Message type.
-     * <li>Number of layers.
-     * <li>Layer type/version pairs.
-     * </ul>
-     */
-    private void handleAvailabilityEvent() {
-        synchronized (mLock) {
-            Collection<VmsLayer> availableLayers = mAvailableLayers.getAvailableLayers();
-            VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
-                VmsMessageType.AVAILABILITY_RESPONSE, availableLayers);
-            setPropertyValue(vehiclePropertyValue);
-        }
-    }
-
-    /**
-     * VmsSubscriptionRequestFormat:
-     * <ul>
-     * <li>Message type.
-     * </ul>
-     *
-     * VmsSubscriptionResponseFormat:
-     * <ul>
-     * <li>Message type.
-     * <li>Sequence number.
-     * <li>Number of layers.
-     * <li>Layer type/version pairs.
-     * </ul>
-     */
-    private void handleSubscriptionRequestEvent() {
-        VmsSubscriptionState subscription = getSubscriptionState();
-        VehiclePropValue vehicleProp = toVehiclePropValue(VmsMessageType.SUBSCRIPTION_RESPONSE);
-        VehiclePropValue.RawValue v = vehicleProp.value;
-        v.int32Values.add(subscription.getSequenceNumber());
-        List<VmsLayer> layers = subscription.getLayers();
-        v.int32Values.add(layers.size());
-        for (VmsLayer layer : layers) {
-            v.int32Values.add(layer.getId());
-            v.int32Values.add(layer.getVersion());
-        }
-        setPropertyValue(vehicleProp);
-    }
-
-    private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) {
-        Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
-        synchronized (mLock) {
-            mOfferings.put(publisherToken, offering);
-
-            // Update layers availability.
-            mAvailableLayers.setPublishersOffering(mOfferings.values());
-
-            availableLayers = mAvailableLayers.getAvailableLayers();
-        }
-        notifySubscribers(availableLayers);
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
-        writer.println(TAG);
-        writer.println("VmsProperty " + (mIsSupported ? "" : "not") + " supported.");
-    }
-
-    /**
-     * Updates the VMS HAL property with the given value.
-     *
-     * @param layer          layer data to update the hal property.
-     * @param hasSubscribers if it is a subscribe or unsubscribe message.
-     * @return true if the call to the HAL to update the property was successful.
-     */
-    public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) {
-        VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
-            hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer);
-        return setPropertyValue(vehiclePropertyValue);
-    }
-
-    public boolean setDataMessage(VmsLayer layer, byte[] payload) {
-        VehiclePropValue vehiclePropertyValue = toVehiclePropValue(VmsMessageType.DATA,
-            layer,
-            payload);
-        return setPropertyValue(vehiclePropertyValue);
-    }
-
-    public boolean setAvailableLayers(Collection<VmsLayer> availableLayers) {
-        VehiclePropValue vehiclePropertyValue =
-                toVehiclePropValue(VmsMessageType.AVAILABILITY_RESPONSE,
-            availableLayers);
-
-        return setPropertyValue(vehiclePropertyValue);
-    }
-
-    public boolean setPropertyValue(VehiclePropValue vehiclePropertyValue) {
-        try {
-            mVehicleHal.set(vehiclePropertyValue);
-            return true;
-        } catch (PropertyTimeoutException e) {
-            Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(HAL_PROPERTY_ID));
-        }
-        return false;
-    }
-
-    /** Creates a {@link VehiclePropValue} */
-    private static VehiclePropValue toVehiclePropValue(int messageType) {
-        VehiclePropValue vehicleProp = new VehiclePropValue();
-        vehicleProp.prop = HAL_PROPERTY_ID;
-        vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
-        VehiclePropValue.RawValue v = vehicleProp.value;
-
-        v.int32Values.add(messageType);
-        return vehicleProp;
-    }
-
-    /** Creates a {@link VehiclePropValue} */
-    private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) {
-        VehiclePropValue vehicleProp = toVehiclePropValue(messageType);
-        VehiclePropValue.RawValue v = vehicleProp.value;
-        v.int32Values.add(layer.getId());
-        v.int32Values.add(layer.getVersion());
-        return vehicleProp;
-    }
-
-    /** Creates a {@link VehiclePropValue} with payload */
-    private static VehiclePropValue toVehiclePropValue(int messageType,
-        VmsLayer layer,
-        byte[] payload) {
-        VehiclePropValue vehicleProp = toVehiclePropValue(messageType, layer);
-        VehiclePropValue.RawValue v = vehicleProp.value;
-        v.bytes.ensureCapacity(payload.length);
-        for (byte b : payload) {
-            v.bytes.add(b);
-        }
-        return vehicleProp;
-    }
-
-    /** Creates a {@link VehiclePropValue} with payload */
-    private static VehiclePropValue toVehiclePropValue(int messageType,
-        Collection<VmsLayer> layers) {
-        VehiclePropValue vehicleProp = toVehiclePropValue(messageType);
-        VehiclePropValue.RawValue v = vehicleProp.value;
-        int numLayers = layers.size();
-        v.int32Values.add(numLayers);
-        for (VmsLayer layer : layers) {
-            v.int32Values.add(layer.getId());
-            v.int32Values.add(layer.getVersion());
-        }
-        return vehicleProp;
-    }
-}
diff --git a/tests/CarDiagnosticVerifier/Android.mk b/tests/CarDiagnosticVerifier/Android.mk
new file mode 100644
index 0000000..a006476
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/Android.mk
@@ -0,0 +1,52 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+        $(LOCAL_PATH)/res \
+        $(TOP)/frameworks/support/v7/recyclerview/res \
+
+LOCAL_PACKAGE_NAME := CarDiagnosticVerifier
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay \
+        --extra-packages android.support.v7.recyclerview \
+
+LOCAL_JAVA_VERSION := 1.8
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES += \
+        vehicle-hal-support-lib \
+        android-support-v4 \
+        android-support-v7-recyclerview \
+
+LOCAL_JAVA_LIBRARIES += android.car
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/VmsSubscriberClientSample/AndroidManifest.xml b/tests/CarDiagnosticVerifier/AndroidManifest.xml
similarity index 63%
rename from tests/VmsSubscriberClientSample/AndroidManifest.xml
rename to tests/CarDiagnosticVerifier/AndroidManifest.xml
index bc798d7..443825a 100644
--- a/tests/VmsSubscriberClientSample/AndroidManifest.xml
+++ b/tests/CarDiagnosticVerifier/AndroidManifest.xml
@@ -13,23 +13,18 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="com.google.android.car.vms.subscriber"
-          android:sharedUserId="android.uid.system">
-    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion='25'/>
+    package="com.google.android.car.diagnosticverifier">
 
-    <application android:label="@string/app_name"
-                 android:icon="@mipmap/ic_launcher">
-        <meta-data
-            android:name="android.car.application"
-            android:resource="@xml/automotive_app_desc"/>
-        <activity android:name=".VmsSubscriberClientSampleActivity">
+    <uses-permission android:name="android.car.permission.DIAGNOSTIC_READ_ALL" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application android:label="Car Diagnostic Verification">
+        <activity android:name=".MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
     </application>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/CarDiagnosticVerifier/res/layout/result_message_item.xml b/tests/CarDiagnosticVerifier/res/layout/result_message_item.xml
new file mode 100644
index 0000000..5c742b6
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/res/layout/result_message_item.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/result_message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="16dp"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml b/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml
new file mode 100644
index 0000000..0c33ed8
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="16dp"
+    android:paddingRight="16dp" >
+
+    <TextView
+        android:id="@+id/status_bar"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content" />
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/verification_results"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/status_bar"
+        android:layout_marginTop="16dp" />
+
+</RelativeLayout>
diff --git a/tests/VmsPublisherClientSample/res/values/strings.xml b/tests/CarDiagnosticVerifier/res/values/strings.xml
similarity index 69%
rename from tests/VmsPublisherClientSample/res/values/strings.xml
rename to tests/CarDiagnosticVerifier/res/values/strings.xml
index df8bf05..ca6b33a 100644
--- a/tests/VmsPublisherClientSample/res/values/strings.xml
+++ b/tests/CarDiagnosticVerifier/res/values/strings.xml
@@ -5,7 +5,7 @@
      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
+          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,
@@ -14,5 +14,7 @@
      limitations under the License.
 -->
 <resources>
-  <string name="app_name">VmsPublisherClientSample</string>
+    <string name="status_receiving">Receiving car diagnostic events...</string>
+    <string name="status_verifying">Verifying car diagnostic events...</string>
+    <string name="status_done">Done with verification</string>
 </resources>
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java
new file mode 100644
index 0000000..85d4fc8
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.util.JsonReader;
+
+import com.android.car.vehiclehal.DiagnosticJson;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides method to convert JSON into car diagnostic event object.
+ */
+public class DiagnosticJsonConverter {
+
+    public static List<CarDiagnosticEvent> readFromJson(InputStream in) throws IOException {
+        JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+
+        try {
+            return readEventsArray(reader);
+        } finally {
+            reader.close();
+        }
+    }
+
+    private static List<CarDiagnosticEvent> readEventsArray(JsonReader reader) throws IOException {
+        List<CarDiagnosticEvent> events = new ArrayList<>();
+
+        reader.beginArray();
+        while (reader.hasNext()) {
+            events.add(readEventAndCanonicalize(reader));
+        }
+        reader.endArray();
+        return events;
+    }
+
+    public static CarDiagnosticEvent readEventAndCanonicalize(InputStream in) throws IOException {
+        JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+        return readEventAndCanonicalize(reader);
+    }
+
+    /**
+     * This method convert JSON to a car diagnostic event object.
+     * Note: it will always set timestamp to 0 and set dtc to null if it is empty string.
+     */
+    private static CarDiagnosticEvent readEventAndCanonicalize(JsonReader reader)
+            throws IOException {
+        DiagnosticJson diagnosticJson = DiagnosticJson.build(reader);
+        //Build event
+        CarDiagnosticEvent.Builder builder = "freeze".equals(diagnosticJson.type) ?
+                CarDiagnosticEvent.Builder.newFreezeFrameBuilder() :
+                CarDiagnosticEvent.Builder.newLiveFrameBuilder();
+        //Always skip timestamp because it is not useful for test
+        builder.atTimestamp(0);
+        for (int i = 0; i < diagnosticJson.intValues.size(); i++) {
+            builder.withIntValue(diagnosticJson.intValues.keyAt(i),
+                    diagnosticJson.intValues.valueAt(i));
+        }
+        for (int i = 0; i < diagnosticJson.floatValues.size(); i++) {
+            builder.withFloatValue(diagnosticJson.floatValues.keyAt(i),
+                    diagnosticJson.floatValues.valueAt(i));
+        }
+        //Always set dtc to null if it is empty string
+        builder.withDtc("".equals(diagnosticJson.dtc) ? null : diagnosticJson.dtc);
+
+        return builder.build();
+    }
+}
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java
new file mode 100644
index 0000000..1b6e447
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.util.JsonWriter;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DiagVerifier implements verification logic for car diagnostic events.
+ *
+ * The main idea for the verification is similar to a "diff" command on two files, whereas here
+ * is a diff on two event lists. The available diff operations are: "add", "delete", "modify"
+ *
+ * For example, think about doing a diff on two sequences:
+ *
+ *   Truth:     A B C D E
+ *   Received:  A C D E F
+ *
+ * The goal is to find the minimal number of diff operations applied on Truth list in order to
+ * become Received list. It is the same problem to find edit distance between two sequences and keep
+ * track of the corresponding edit operations. This verifier applies dynamic programming algorithm
+ * to find the minimal set of diff operations. And the result would be:
+ *
+ *   Truth:     A - C D E +
+ *   Received:  A   C D E F
+ *
+ * It means in order to become Received list, "B" will be missing and an extra "F" will be added
+ * at the end of list.
+ */
+public class DiagnosticVerifier {
+
+    private static final String TAG = "DiagnosticVerifier";
+    /**
+     * Below are 4 diff operations when comparing two event lists
+     */
+    private static final int DELETE = 0;
+    private static final int ADD = 1;
+    private static final int MODIFY = 2;
+    private static final int KEEP = 3;
+
+    /**
+     * A list of truth diagnostic events for comparison.
+     */
+    private final List<CarDiagnosticEvent> mTruthEventList = new ArrayList<>();
+    /**
+     * A list of received diagnostic events from car service.
+     */
+    private final List<CarDiagnosticEvent> mReceivedEventList = new ArrayList<>();
+
+    /**
+     * Definition of the verification result
+     */
+    static class VerificationResult {
+        public final String testCase;
+        public final boolean success;
+        public final String errorMessage;
+
+        private VerificationResult(String testCase, boolean success, String errorMessage) {
+            this.testCase = testCase;
+            this.success = success;
+            this.errorMessage = errorMessage;
+        }
+
+        public static VerificationResult fromMessage(String testCase, String message) {
+            return new VerificationResult(testCase, message.length() == 0, message);
+        }
+
+        public void writeToJson(JsonWriter jsonWriter) throws IOException {
+            jsonWriter.beginObject();
+
+            jsonWriter.name("testCase");
+            jsonWriter.value(this.testCase);
+
+            jsonWriter.name("success");
+            jsonWriter.value(this.success);
+
+            jsonWriter.name("errorMessage");
+            jsonWriter.value(this.errorMessage);
+
+            jsonWriter.endObject();
+        }
+    }
+
+    public DiagnosticVerifier(List<CarDiagnosticEvent> truthEvents) {
+        if (truthEvents != null) {
+            for (CarDiagnosticEvent event : truthEvents) {
+                CarDiagnosticEvent canonicalEvent = canonicalize(event);
+                mTruthEventList.add(canonicalEvent);
+            }
+        }
+    }
+
+    public void receiveEvent(CarDiagnosticEvent event) {
+        CarDiagnosticEvent newEvent = canonicalize(event);
+        mReceivedEventList.add(newEvent);
+    }
+
+    public List<VerificationResult> verify() {
+        List<Integer> diff = calculateDiffOperations();
+        StringBuilder missingEventMsgBuilder = new StringBuilder();
+        StringBuilder extraEventMsgBuilder = new StringBuilder();
+        StringBuilder mismatchEventMsgBuilder = new StringBuilder();
+        for (int i = 0, j = 0, k = diff.size() - 1; k >= 0; k--) {
+            if (diff.get(k) == DELETE) {
+                missingEventMsgBuilder.append(String.format(
+                        "Missing event at position %d: %s\n", i, mTruthEventList.get(i)));
+                i++;
+            } else if (diff.get(k) == ADD) {
+                extraEventMsgBuilder.append(String.format(
+                        "Extra event at position %d: %s\n", i, mReceivedEventList.get(j)));
+                j++;
+            } else if (diff.get(k) == MODIFY) {
+                mismatchEventMsgBuilder.append(String.format(
+                        "Mismatched event pair at position %d:\n" +
+                        "True event -- %s\nWrong event -- %s\n",
+                        i, mTruthEventList.get(i), mReceivedEventList.get(j)));
+                i++;
+                j++;
+            } else {
+                i++;
+                j++;
+            }
+        }
+        List<VerificationResult> results = new ArrayList<>();
+        results.add(VerificationResult.fromMessage(
+                "test_mismatched_event", mismatchEventMsgBuilder.toString()));
+        results.add(VerificationResult.fromMessage(
+                "test_missing_event", missingEventMsgBuilder.toString()));
+        results.add(VerificationResult.fromMessage(
+                "test_extra_event", extraEventMsgBuilder.toString()));
+        return results;
+    }
+
+    /**
+     * The function applies a dynamic programming algorithm to find the minimal set of diff
+     * operations that applied on truth event list in order to become received event list
+     */
+    private List<Integer> calculateDiffOperations() {
+        final int n = mTruthEventList.size();
+        final int m = mReceivedEventList.size();
+
+        int[][] diffTable = new int[n + 1][m + 1];
+        int[][] costTable = new int[n + 1][m + 1];
+
+        for (int i = 1; i <= n; i++) {
+            costTable[i][0] = i;
+            diffTable[i][0] = DELETE;
+        }
+
+        for (int i = 1; i <= m; i++) {
+            costTable[0][i] = i;
+            diffTable[0][i] = ADD;
+        }
+
+        for (int i = 1; i <= n; i++) {
+            for (int j = 1; j <= m; j++) {
+                int deleteCost = costTable[i - 1][j] + 1;
+                int addCost = costTable[i][j - 1] + 1;
+                int modifyCost = costTable[i - 1][j - 1];
+
+                CarDiagnosticEvent trueEvent = mTruthEventList.get(i - 1);
+                CarDiagnosticEvent receivedEvent = mReceivedEventList.get(j - 1);
+
+                //TODO: Use a more meaningful comparison. Instead of strict object level equality,
+                //can check logical equality and allow an acceptable difference.
+                boolean isEqual = trueEvent.equals(receivedEvent);
+                modifyCost += isEqual ? 0 : 1;
+
+                int minCost = modifyCost;
+                int move = isEqual ? KEEP : MODIFY;
+                if (minCost > addCost) {
+                    minCost = addCost;
+                    move = ADD;
+                }
+                if (minCost > deleteCost) {
+                    minCost = deleteCost;
+                    move = DELETE;
+                }
+
+                costTable[i][j] = minCost;
+                diffTable[i][j] = move;
+            }
+        }
+        List<Integer> diff = new ArrayList<>();
+
+        for (int i = n, j = m; i > 0 || j > 0; ) {
+            diff.add(diffTable[i][j]);
+            if (diffTable[i][j] == DELETE) {
+                i--;
+            } else if (diffTable[i][j] == ADD) {
+                j--;
+            } else {
+                i--;
+                j--;
+            }
+        }
+        return diff;
+    }
+
+    /**
+     * The function will canonicalize a given event by using JSON converter which will reset event
+     * timestamp to 0 and set DTC field with empty string to null. Doing JSON conversion is because
+     * CarDiagnosticEvent does not provide direct accessor for intValues and floatValues.
+     */
+    private CarDiagnosticEvent canonicalize(CarDiagnosticEvent event) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        JsonWriter writer = new JsonWriter(new OutputStreamWriter(out));
+        CarDiagnosticEvent newEvent = event;
+        try {
+            event.writeToJson(writer);
+            writer.flush();
+            writer.close();
+            byte[] rawJson = out.toByteArray();
+            ByteArrayInputStream in = new ByteArrayInputStream(rawJson);
+            newEvent = DiagnosticJsonConverter.readEventAndCanonicalize(in);
+            in.close();
+            out.close();
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to clear timestamp ");
+        }
+        return newEvent;
+    }
+}
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java
new file mode 100644
index 0000000..75db2ed
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.app.Activity;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.hardware.CarSensorManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.widget.TextView;
+
+import com.google.android.car.diagnosticverifier.DiagnosticVerifier.VerificationResult;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The test app that does the verification of car diagnostic event data. It first reads the
+ * truth (golden) event data from a JSON file upon starting. Then a broadcast intent such as:
+ *
+ *     am broadcast -a com.google.android.car.diagnosticverifier.action.START_LISTEN
+ *
+ * will activate the car diagnostics listener. The test app will receive events from diagnostic API.
+ * Once it receives all the events, a broadcast intent with "stop" action such as:
+ *
+ *     am broadcast -a com.google.android.car.diagnosticverifier.action.STOP_LISTEN
+ *
+ * will deactivate the listener and start the verification process (see {@link DiagnosticVerifier}).
+ *
+ * Verification result will be output to a JSON file on device.
+ */
+public class MainActivity extends Activity {
+    public static final String TAG = "DiagnosticVerifier";
+
+    public static final String ACTION_START_LISTEN =
+            "com.google.android.car.diagnosticverifier.action.START_LISTEN";
+    public static final String ACTION_STOP_LISTEN =
+            "com.google.android.car.diagnosticverifier.action.STOP_LISTEN";
+
+    private static final String DEFAULT_JSON_PATH = "/data/local/tmp/diag.json";
+
+    private static final String JSON_PATH_KEY = "jsonPath";
+    private static final String JSON_RESULT = "verification_result.json";
+
+    private Car mCar;
+    private CarDiagnosticManager mCarDiagnosticManager;
+    private DiagnosticListener mDiagnosticListener;
+    private BroadcastReceiver mBroadcastReceiver;
+    private DiagnosticVerifier mVerifier;
+    private TextView mStatusBar;
+    private RecyclerView mRecyclerView;
+    private VerificationResultAdapter mResultAdapter;
+    private boolean mListening = false;
+
+    private final ServiceConnection mCarConnectionListener =
+            new ServiceConnection() {
+                @Override
+                public void onServiceConnected(ComponentName name, IBinder iBinder) {
+                    Log.d(TAG, "Connected to " + name.flattenToString());
+                    try {
+                        mCarDiagnosticManager =
+                                (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE);
+                    } catch (CarNotConnectedException e) {
+                        Log.e(TAG, "Failed to get a connection", e);
+                    }
+                }
+
+                @Override
+                public void onServiceDisconnected(ComponentName name) {
+                    Log.d(TAG, "Disconnected from " + name.flattenToString());
+
+                    mCar = null;
+                    mCarDiagnosticManager = null;
+                }
+            };
+
+    class DiagnosticListener implements CarDiagnosticManager.OnDiagnosticEventListener {
+
+        @Override
+        public void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent) {
+            Log.v(TAG, "Received Car Diagnostic Event: " + carDiagnosticEvent.toString());
+            mVerifier.receiveEvent(carDiagnosticEvent);
+        }
+    }
+
+    class VerifierMsgReceiver extends BroadcastReceiver {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.d(TAG, "Received intent with action: " + action);
+            if (ACTION_START_LISTEN.equals(action)) {
+                try {
+                    startListen();
+                } catch (CarNotConnectedException e) {
+                    Log.e(TAG, "Failed to listen for car diagnostic event", e);
+                }
+            } else if (ACTION_STOP_LISTEN.equals(action)) {
+                stopListen();
+                verify();
+            }
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.verifier_activity);
+
+        mStatusBar = (TextView) findViewById(R.id.status_bar);
+
+        //Setting up RecyclerView to show verification result messages
+        mRecyclerView = (RecyclerView) findViewById(R.id.verification_results);
+        LinearLayoutManager layoutManager =
+                new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
+        mRecyclerView.setLayoutManager(layoutManager);
+        mResultAdapter = new VerificationResultAdapter();
+        mRecyclerView.setAdapter(mResultAdapter);
+
+        //Connect to car service
+        mCar = Car.createCar(this, mCarConnectionListener);
+        mCar.connect();
+
+        //Initialize broadcast intent receiver
+        mBroadcastReceiver = new VerifierMsgReceiver();
+        IntentFilter filter = new IntentFilter(ACTION_START_LISTEN);
+        filter.addAction(ACTION_STOP_LISTEN);
+        this.registerReceiver(mBroadcastReceiver, filter);
+
+        //Read golden diagnostics JSON file
+        String jsonPath = this.getIntent().getStringExtra(JSON_PATH_KEY);
+        if (jsonPath == null || jsonPath.isEmpty()) {
+            jsonPath = DEFAULT_JSON_PATH;
+        }
+        List<CarDiagnosticEvent> events;
+        try {
+            events = DiagnosticJsonConverter.readFromJson(new FileInputStream(jsonPath));
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to read diagnostic JSON file", e);
+        }
+        Log.d(TAG, String.format("Read %d events from JSON file %s.", events.size(), jsonPath));
+
+        mVerifier = new DiagnosticVerifier(events);
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mCar != null) {
+            mCar.disconnect();
+        }
+        mVerifier = null;
+        this.unregisterReceiver(mBroadcastReceiver);
+    }
+
+    private void startListen() throws CarNotConnectedException {
+        if (mListening) {
+            return;
+        }
+        if (mDiagnosticListener == null) {
+            mDiagnosticListener = new DiagnosticListener();
+        }
+        Log.i(TAG, "Start listening for car diagnostics events");
+        mCarDiagnosticManager.registerListener(
+                mDiagnosticListener,
+                CarDiagnosticManager.FRAME_TYPE_LIVE,
+                CarSensorManager.SENSOR_RATE_NORMAL);
+        mCarDiagnosticManager.registerListener(
+                mDiagnosticListener,
+                CarDiagnosticManager.FRAME_TYPE_FREEZE,
+                CarSensorManager.SENSOR_RATE_NORMAL);
+
+        mListening = true;
+        mStatusBar.setText(R.string.status_receiving);
+    }
+
+    private void stopListen() {
+        Log.i(TAG, "Stop listening for car diagnostics events");
+        mCarDiagnosticManager.unregisterListener(mDiagnosticListener);
+        mListening = false;
+    }
+
+    private boolean isExternalStorageWritable() {
+        String state = Environment.getExternalStorageState();
+        return Environment.MEDIA_MOUNTED.equals(state);
+    }
+
+    private File getResultJsonFile() throws IOException {
+        if (!isExternalStorageWritable()) {
+            throw new IOException("External storage is not writable. Cannot save content");
+        }
+
+        File resultJson = new File(Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_DOCUMENTS), JSON_RESULT);
+        if (!resultJson.getParentFile().mkdirs()) {
+            Log.w(TAG, "Parent directory may already exist");
+        }
+        return resultJson;
+    }
+
+    private void verify() {
+        Log.d(TAG, "Start verifying car diagnostics events");
+        mStatusBar.setText(R.string.status_verifying);
+        List<VerificationResult> results = mVerifier.verify();
+        mStatusBar.setText(R.string.status_done);
+
+        if (results.isEmpty()) {
+            Log.d(TAG, "Verification result is empty.");
+            return;
+        }
+
+        List<String> resultMessages = new ArrayList<>();
+        try {
+            File resultJson = getResultJsonFile();
+            JsonWriter writer = new JsonWriter(
+                new OutputStreamWriter(new FileOutputStream(resultJson)));
+
+            writer.beginArray();
+            for (VerificationResult result : results) {
+                resultMessages.add("Test case: " + result.testCase);
+                resultMessages.add("Result: " + result.success);
+                resultMessages.add(result.errorMessage);
+                result.writeToJson(writer);
+            }
+            writer.endArray();
+            writer.flush();
+            writer.close();
+            Log.i(TAG, "Verification result: " + resultJson.getAbsolutePath());
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to save verification result.", e);
+        }
+        mResultAdapter.setResultMessages(resultMessages);
+    }
+}
+
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/VerificationResultAdapter.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/VerificationResultAdapter.java
new file mode 100644
index 0000000..e6453b6
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/VerificationResultAdapter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * A recycler view adapter for verification result messages
+ */
+public class VerificationResultAdapter extends
+        RecyclerView.Adapter<VerificationResultAdapter.VerificationResultViewHolder> {
+
+    private List<String> mResultMessages;
+
+    @Override
+    public VerificationResultViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+        Context context = viewGroup.getContext();
+        int messageItemLayoutId = R.layout.result_message_item;
+        LayoutInflater inflater = LayoutInflater.from(context);
+        boolean shouldAttachToParentImmediately = false;
+
+        View view = inflater.inflate(
+                messageItemLayoutId, viewGroup, shouldAttachToParentImmediately);
+        return new VerificationResultViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(VerificationResultViewHolder verificationResultViewHolder, int i) {
+        String resultMessage = mResultMessages.get(i);
+        verificationResultViewHolder.mResultMessageTextView.setText(resultMessage);
+    }
+
+    @Override
+    public int getItemCount() {
+        if (mResultMessages == null) {
+            return 0;
+        }
+        return mResultMessages.size();
+    }
+
+    public void setResultMessages(List<String> resultMessages) {
+        mResultMessages = resultMessages;
+        notifyDataSetChanged();
+    }
+
+    public class VerificationResultViewHolder extends RecyclerView.ViewHolder {
+        public final TextView mResultMessageTextView;
+
+        public VerificationResultViewHolder(View view) {
+            super(view);
+            mResultMessageTextView = (TextView) view.findViewById(R.id.result_message);
+        }
+    }
+}
diff --git a/tests/VmsSubscriberClientSample/Android.mk b/tests/DirectRenderingClusterSample/Android.mk
similarity index 77%
rename from tests/VmsSubscriberClientSample/Android.mk
rename to tests/DirectRenderingClusterSample/Android.mk
index f59e267..7fb6e8e 100644
--- a/tests/VmsSubscriberClientSample/Android.mk
+++ b/tests/DirectRenderingClusterSample/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Android Open Source Project
+# 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.
@@ -20,20 +20,16 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_PACKAGE_NAME := DirectRenderingClusterSample
 
-LOCAL_PACKAGE_NAME := VmsSubscriberClientSample
-
-LOCAL_MODULE_TAGS := optional
-
+# Each update should be signed by OEMs
+LOCAL_CERTIFICATE := platform
 LOCAL_PRIVILEGED_MODULE := true
 
-LOCAL_CERTIFICATE := platform
-
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_JAVA_LIBRARIES += android.car
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
 
-include packages/services/Car/car-support-lib/car-support.mk
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
+include $(BUILD_PACKAGE)
diff --git a/tests/DirectRenderingClusterSample/AndroidManifest.xml b/tests/DirectRenderingClusterSample/AndroidManifest.xml
new file mode 100644
index 0000000..fc41ef0
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.car.cluster.sample"
+      android:versionCode="1"
+      android:versionName="1.0">
+
+    <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="25"/>
+
+    <!-- We set TYPE_SYSTEM_ALERT window flag to presentation in order
+         to show it outside of activity context -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+    <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+        tools:ignore="ProtectedPermissions"/>
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+    <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+
+    <application android:label="@string/app_name"
+                 android:icon="@mipmap/ic_launcher"
+                 android:directBootAware="true"
+                 android:persistent="true">
+        <service android:name=".SampleClusterServiceImpl"
+                 android:exported="false"
+                 android:permission="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
+
+        <activity android:name=".MainClusterActivity"
+            android:exported="false"
+            android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_car_info.png
new file mode 100644
index 0000000..adb07d6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_music.png
new file mode 100644
index 0000000..f1b2533
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_nav.png
new file mode 100644
index 0000000..8036b89
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_phone.png
new file mode 100644
index 0000000..2daaa8e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_car_info.png
new file mode 100644
index 0000000..9c45d3e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_music.png
new file mode 100644
index 0000000..fb0671a
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_nav.png
new file mode 100644
index 0000000..4844c89
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_phone.png
new file mode 100644
index 0000000..ef50db6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_car_info.png
new file mode 100644
index 0000000..2db5645
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_music.png
new file mode 100644
index 0000000..6154079
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_nav.png
new file mode 100644
index 0000000..f94db1e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_phone.png
new file mode 100644
index 0000000..ca0be39
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_car_info.png
new file mode 100644
index 0000000..f00be30
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_music.png
new file mode 100644
index 0000000..8a875ad
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_nav.png
new file mode 100644
index 0000000..7b2b514
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_phone.png
new file mode 100644
index 0000000..fce6fb7
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_car_info.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_car_info.xml
new file mode 100644
index 0000000..437ad41
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_car_info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" >
+        <layer-list>
+            <item android:drawable="@drawable/ic_car_info"/>
+            <item android:drawable="@drawable/focused_button_shape"/>
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_car_info"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_music.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_music.xml
new file mode 100644
index 0000000..65e01b6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_music.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" >
+        <layer-list>
+            <item android:drawable="@drawable/ic_music"/>
+            <item android:drawable="@drawable/focused_button_shape"/>
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_music"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_nav.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_nav.xml
new file mode 100644
index 0000000..9f9c6bc
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_nav.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" >
+        <layer-list>
+            <item android:drawable="@drawable/ic_nav"/>
+            <item android:drawable="@drawable/focused_button_shape"/>
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_nav"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_phone.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_phone.xml
new file mode 100644
index 0000000..2a6e249
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_phone.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" >
+        <layer-list>
+            <item android:drawable="@drawable/ic_phone"/>
+            <item android:drawable="@drawable/focused_button_shape"/>
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_phone"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/car_top_view.png b/tests/DirectRenderingClusterSample/res/drawable/car_top_view.png
new file mode 100644
index 0000000..b19ee12
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/car_top_view.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable/focused_button_shape.xml b/tests/DirectRenderingClusterSample/res/drawable/focused_button_shape.xml
new file mode 100644
index 0000000..b84ef30
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/focused_button_shape.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <stroke
+        android:width="2dp"
+        android:color="#333333" />
+    <corners
+        android:radius="7dp" />
+    <padding
+        android:left="5dp"
+        android:top="5dp"
+        android:right="5dp"
+        android:bottom="5dp" />
+</shape>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/layout/activity_main.xml b/tests/DirectRenderingClusterSample/res/layout/activity_main.xml
new file mode 100644
index 0000000..7e1ef9f
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/activity_main.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/activity_main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/background_dark"
+    tools:context=".MainClusterActivity"
+    android:windowIsFloating="true">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <android.support.v4.view.ViewPager
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/pager"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            />
+
+        <LinearLayout
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/btn_nav"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:background="@drawable/btn_nav"
+                android:layout_margin="10dp"
+                android:focusableInTouchMode="true" />
+            <Button
+                android:id="@+id/btn_phone"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:layout_margin="10dp"
+                android:background="@drawable/btn_phone"
+                android:focusableInTouchMode="true" />
+            <Button
+                android:id="@+id/btn_music"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:layout_margin="10dp"
+                android:background="@drawable/btn_music"
+                android:focusableInTouchMode="true" />
+            <Button
+                android:id="@+id/btn_car_info"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:layout_margin="10dp"
+                android:background="@drawable/btn_car_info"
+                android:focusableInTouchMode="true" />
+        </LinearLayout>
+    </LinearLayout>
+
+    <TextView
+            android:id="@+id/text_overlay"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:background="@android:color/background_light"
+            android:textSize="30sp"
+    />
+</RelativeLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_car_info.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_car_info.xml
new file mode 100644
index 0000000..3a1f5ab
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_car_info.xml
@@ -0,0 +1,46 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.google.experiments.client.pavelm.fakeclusterux.CarInfoFragment">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="32 psi"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginLeft="-80dp"
+        />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="33 psi"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginLeft="80dp"
+        />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="33 psi"
+        android:layout_gravity="center_horizontal|bottom"
+        android:layout_marginLeft="80dp"
+        />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="31 psi"
+        android:layout_gravity="center_horizontal|bottom"
+        android:layout_marginLeft="-80dp"
+        />
+
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:src="@drawable/car_top_view"
+        android:padding="30dp"
+        android:scaleType="fitCenter" />
+
+</FrameLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml
new file mode 100644
index 0000000..1a11b3f
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml
@@ -0,0 +1,14 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.google.experiments.client.pavelm.fakeclusterux.MusicFragment"
+    android:background="@color/colorPrimaryDark">
+
+    <!-- TODO: Update blank fragment layout -->
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/hello_blank_fragment" />
+
+</FrameLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_navigation.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_navigation.xml
new file mode 100644
index 0000000..c0fb4b3
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_navigation.xml
@@ -0,0 +1,22 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/darkBlue"
+    tools:context=".NavigationFragment">
+
+    <FrameLayout
+        android:id="@+id/nav_frame_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <SurfaceView
+            android:id="@+id/nav_surface"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="20dp"/>
+
+    </FrameLayout>
+
+
+</FrameLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_phone.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_phone.xml
new file mode 100644
index 0000000..42fe24a
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_phone.xml
@@ -0,0 +1,13 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.google.experiments.client.pavelm.fakeclusterux.PhoneFragment">
+
+    <!-- TODO: Update blank fragment layout -->
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/hello_blank_fragment" />
+
+</FrameLayout>
diff --git a/tests/VmsPublisherClientSample/res/mipmap-hdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from tests/VmsPublisherClientSample/res/mipmap-hdpi/ic_launcher.png
rename to tests/DirectRenderingClusterSample/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-mdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from tests/VmsPublisherClientSample/res/mipmap-mdpi/ic_launcher.png
rename to tests/DirectRenderingClusterSample/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-xhdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from tests/VmsPublisherClientSample/res/mipmap-xhdpi/ic_launcher.png
rename to tests/DirectRenderingClusterSample/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-xxhdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from tests/VmsPublisherClientSample/res/mipmap-xxhdpi/ic_launcher.png
rename to tests/DirectRenderingClusterSample/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-xxxhdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from tests/VmsPublisherClientSample/res/mipmap-xxxhdpi/ic_launcher.png
rename to tests/DirectRenderingClusterSample/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/values-w820dp/dimens.xml b/tests/DirectRenderingClusterSample/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/colors.xml b/tests/DirectRenderingClusterSample/res/values/colors.xml
new file mode 100644
index 0000000..a71c0d5
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/colors.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+    <color name="darkBlue">#2b2b77</color>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/dimens.xml b/tests/DirectRenderingClusterSample/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/strings.xml b/tests/DirectRenderingClusterSample/res/values/strings.xml
new file mode 100644
index 0000000..778ecf6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/strings.xml
@@ -0,0 +1,6 @@
+<resources>
+    <string name="app_name">Fake Cluster Implementation</string>
+
+    <!-- TODO: Remove or change this placeholder text -->
+    <string name="hello_blank_fragment">Hello blank fragment</string>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/styles.xml b/tests/DirectRenderingClusterSample/res/values/styles.xml
new file mode 100644
index 0000000..f11f745
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/styles.xml
@@ -0,0 +1,3 @@
+<resources>
+
+</resources>
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/CarInfoFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/CarInfoFragment.java
new file mode 100644
index 0000000..d1e7112
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/CarInfoFragment.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class CarInfoFragment extends Fragment {
+
+
+    public CarInfoFragment() {
+        // Required empty public constructor
+    }
+
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        return inflater.inflate(R.layout.fragment_car_info, container, false);
+    }
+
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java
new file mode 100644
index 0000000..1c3f7af
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import static android.car.cluster.sample.SampleClusterServiceImpl.LOCAL_BINDING_ACTION;
+
+import android.car.cluster.sample.SampleClusterServiceImpl.Listener;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+
+public class MainClusterActivity extends FragmentActivity
+        implements Listener {
+    private static final String TAG = MainClusterActivity.class.getSimpleName();
+
+    private Button mNavButton;
+    private Button mPhoneButton;
+    private Button mCarInfoButton;
+    private Button mMusicButton;
+    private TextView mTextOverlay;
+    private ViewPager mPager;
+
+    private SampleClusterServiceImpl mService;
+
+    private final Handler mHandler = new Handler();
+
+    private HashMap<Button, Facet<?>> mButtonToFacet = new HashMap<>();
+    private SparseArray<Facet<?>> mOrderToFacet = new SparseArray<>();
+
+    private final View.OnFocusChangeListener mFacetButtonFocusListener =
+            new View.OnFocusChangeListener() {
+        @Override
+        public void onFocusChange(View v, boolean hasFocus) {
+            if (hasFocus) {
+                mPager.setCurrentItem(mButtonToFacet.get(v).order);
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        Intent intent = new Intent(this, SampleClusterServiceImpl.class);
+        intent.setAction(LOCAL_BINDING_ACTION);
+        bindService(intent,
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        Log.i(TAG, "onServiceConnected, name: " + name + ", service: " + service);
+                        mService = ((SampleClusterServiceImpl.LocalBinder) service)
+                                .getService();
+                        mService.registerListener(MainClusterActivity.this);
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        Log.i(TAG, "onServiceDisconnected, name: " + name);
+                        mService = null;
+                    }
+                }, BIND_AUTO_CREATE);
+
+        mNavButton = findViewById(R.id.btn_nav);
+        mPhoneButton = findViewById(R.id.btn_phone);
+        mCarInfoButton = findViewById(R.id.btn_car_info);
+        mMusicButton = findViewById(R.id.btn_music);
+        mTextOverlay = findViewById(R.id.text_overlay);
+
+        registerFacets(
+                new Facet<>(mNavButton, 0, NavigationFragment.class),
+                new Facet<>(mPhoneButton, 1, PhoneFragment.class),
+                new Facet<>(mMusicButton, 2, MusicFragment.class),
+                new Facet<>(mCarInfoButton, 3, CarInfoFragment.class));
+
+        mPager = (ViewPager) findViewById(R.id.pager);
+        mPager.setAdapter(new ClusterPageAdapter(getSupportFragmentManager()));
+
+        mNavButton.requestFocus();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mService != null) {
+            mService.unregisterListener();
+        }
+    }
+
+    @Override
+    public void onShowToast(String text) {
+        if (mTextOverlay.getVisibility() == View.VISIBLE) {
+            if (!TextUtils.isEmpty(mTextOverlay.getText())) {
+                mTextOverlay.setText(mTextOverlay.getText() + "\n" + text);
+            } else {
+                mTextOverlay.setText(text);
+            }
+        }
+
+        mTextOverlay.setVisibility(View.VISIBLE);
+
+        mHandler.removeCallbacksAndMessages(null);
+        mHandler.postDelayed(() -> {
+            mTextOverlay.setVisibility(View.GONE);
+            mTextOverlay.setText("");
+        }, 3000);
+    }
+
+    @Override
+    public void onKeyEvent(KeyEvent event) {
+        Log.i(TAG, "onKeyEvent, event: " + event);
+        dispatchKeyEvent(event);  // TODO: dispatch event doesn't work for some reason.
+
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
+                int nextItem = (mPager.getCurrentItem() + 1) % mButtonToFacet.size();
+                mOrderToFacet.get(nextItem).button.requestFocus();
+            } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
+                int nextItem = (mPager.getCurrentItem() - 1);
+                if (nextItem < 0) nextItem =  mButtonToFacet.size() - 1;
+                mOrderToFacet.get(nextItem).button.requestFocus();
+            }
+        }
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        boolean consumed = super.dispatchKeyEvent(event);
+        Log.i(TAG, "dispatchKeyEvent, event: " + event + ", consumed: " + consumed);
+        return consumed;
+    }
+
+    public class ClusterPageAdapter extends FragmentPagerAdapter {
+        public ClusterPageAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        @Override
+        public int getCount() {
+            return mButtonToFacet.size();
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            return mOrderToFacet.get(position).getOrCreateFragment();
+        }
+    }
+
+    private void registerFacets(Facet<?>... facets) {
+        for (Facet<?> f : facets) {
+            registerFacet(f);
+        }
+    }
+
+    private <T> void registerFacet(Facet<T> facet) {
+        mOrderToFacet.append(facet.order, facet);
+        mButtonToFacet.put(facet.button, facet);
+
+        facet.button.setOnFocusChangeListener(mFacetButtonFocusListener);
+    }
+
+    private static class Facet<T> {
+        Button button;
+        Class<T> clazz;
+        int order;
+
+        Facet(Button button, int order, Class<T> clazz) {
+            this.button = button;
+            this.order = order;
+            this.clazz = clazz;
+        }
+
+        private Fragment mFragment;
+
+        Fragment getOrCreateFragment() {
+            if (mFragment == null) {
+                try {
+                    mFragment = (Fragment) clazz.getConstructors()[0].newInstance();
+                } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            return mFragment;
+        }
+    }
+
+    SampleClusterServiceImpl getService() {
+        return mService;
+    }
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MusicFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MusicFragment.java
new file mode 100644
index 0000000..2c82065
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MusicFragment.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class MusicFragment extends Fragment {
+
+    public MusicFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        return inflater.inflate(R.layout.fragment_music, container, false);
+    }
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavigationFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavigationFragment.java
new file mode 100644
index 0000000..0ffac1e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavigationFragment.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import static android.car.cluster.CarInstrumentClusterManager.CATEGORY_NAVIGATION;
+
+import android.app.ActivityOptions;
+import android.car.CarNotConnectedException;
+import android.car.cluster.ClusterActivityState;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.hardware.display.VirtualDisplay;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class NavigationFragment extends Fragment {
+    private final static String TAG = "Cluster.NavigationFragment";
+
+    private SurfaceView mSurfaceView;
+    private DisplayManager mDisplayManager;
+    private Rect mUnobscuredBounds;
+
+    // Static because we want to keep alive this virtual display when navigating through
+    // ViewPager (this fragment gets dynamically destroyed and created)
+    private static VirtualDisplay mVirtualDisplay;
+    private static int mRegisteredNavDisplayId = Display.INVALID_DISPLAY;
+
+    public NavigationFragment() {
+        // Required empty public constructor
+    }
+
+    private final DisplayListener mDisplayListener = new DisplayListener() {
+        @Override
+        public void onDisplayAdded(int displayId) {
+            int navDisplayId = getVirtualDisplayId();
+            Log.i(TAG, "onDisplayAdded, displayId: " + displayId
+                    + ", navigation display id: " + navDisplayId);
+
+            if (navDisplayId == displayId) {
+                try {
+                    getService().setClusterActivityLaunchOptions(
+                            CATEGORY_NAVIGATION,
+                            ActivityOptions.makeBasic()
+                                    .setLaunchDisplayId(displayId));
+                    mRegisteredNavDisplayId = displayId;
+
+                    getService().setClusterActivityState(
+                            CATEGORY_NAVIGATION,
+                            ClusterActivityState.create(true, mUnobscuredBounds).toBundle());
+                } catch (CarNotConnectedException e) {
+                    throw new IllegalStateException(
+                            "Failed to report nav activity cluster launch options", e);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            if (mRegisteredNavDisplayId == displayId) {
+                try {
+                    mRegisteredNavDisplayId = Display.INVALID_DISPLAY;
+                    getService().setClusterActivityLaunchOptions(
+                            CATEGORY_NAVIGATION, null);
+                } catch (CarNotConnectedException e) {
+                    // This can happen only during shutdown, ignore.
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {}
+    };
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        Log.i(TAG, "onCreateView");
+        mDisplayManager = getActivity().getSystemService(DisplayManager.class);
+        mDisplayManager.registerDisplayListener(mDisplayListener, new Handler());
+
+        // Inflate the layout for this fragment
+        View root = inflater.inflate(R.layout.fragment_navigation, container, false);
+
+        mSurfaceView = root.findViewById(R.id.nav_surface);
+        mSurfaceView.getHolder().addCallback(new Callback() {
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {
+                Log.i(TAG, "surfaceCreated, holder: " + holder);
+            }
+
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+                Log.i(TAG, "surfaceChanged, holder: " + holder + ", size:" + width + "x" + height
+                        + ", format:" + format);
+
+                //Create dummy unobscured area to report to navigation activity.
+                mUnobscuredBounds = new Rect(40, 0, width - 80, height - 40);
+
+                if (mVirtualDisplay == null) {
+                    mVirtualDisplay = createVirtualDisplay(holder.getSurface(), width, height);
+                } else {
+                    mVirtualDisplay.setSurface(holder.getSurface());
+                }
+            }
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {
+                Log.i(TAG, "surfaceDestroyed, holder: " + holder + ", detaching surface from"
+                        + " display, surface: " + holder.getSurface());
+                // detaching surface is similar to turning off the display
+                mVirtualDisplay.setSurface(null);
+            }
+        });
+
+        return root;
+    }
+
+    private VirtualDisplay createVirtualDisplay(Surface surface, int width, int height) {
+        Log.i(TAG, "createVirtualDisplay, surface: " + surface + ", width: " + width
+                + "x" + height);
+        return mDisplayManager.createVirtualDisplay("Cluster-App-VD", width, height, 160, surface,
+                DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy");
+    }
+
+    private SampleClusterServiceImpl getService() {
+        return ((MainClusterActivity) getActivity()).getService();
+    }
+
+    private int getVirtualDisplayId() {
+        return (mVirtualDisplay != null && mVirtualDisplay.getDisplay() != null)
+                ? mVirtualDisplay.getDisplay().getDisplayId() : Display.INVALID_DISPLAY;
+    }
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/PhoneFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/PhoneFragment.java
new file mode 100644
index 0000000..9930ec5
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/PhoneFragment.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class PhoneFragment extends Fragment {
+
+
+    public PhoneFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        return inflater.inflate(R.layout.fragment_phone, container, false);
+    }
+
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java
new file mode 100644
index 0000000..1457708
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static java.lang.Integer.parseInt;
+
+import android.app.ActivityOptions;
+import android.car.CarNotConnectedException;
+import android.car.cluster.ClusterActivityState;
+import android.car.cluster.renderer.InstrumentClusterRenderingService;
+import android.car.cluster.renderer.NavigationRenderer;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Log;
+import android.view.Display;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Dummy implementation of {@link SampleClusterServiceImpl} to log all interaction.
+ */
+public class SampleClusterServiceImpl extends InstrumentClusterRenderingService {
+
+    private static final String TAG = SampleClusterServiceImpl.class.getSimpleName();
+
+    private Listener mListener;
+    private final Binder mLocalBinder = new LocalBinder();
+    static final String LOCAL_BINDING_ACTION = "local";
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i(TAG, "onBind, intent: " + intent);
+        return (LOCAL_BINDING_ACTION.equals(intent.getAction()))
+                ? mLocalBinder : super.onBind(intent);
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i(TAG, "onCreate");
+
+        Display clusterDisplay = getInstrumentClusterDisplay(this);
+        if (clusterDisplay == null) {
+            Log.e(TAG, "Unable to find instrument cluster display");
+            return;
+        }
+
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(clusterDisplay.getDisplayId());
+        Intent intent = new Intent(this, MainClusterActivity.class);
+        intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent, options.toBundle());
+    }
+
+    @Override
+    protected void onKeyEvent(KeyEvent keyEvent) {
+        Log.i(TAG, "onKeyEvent, keyEvent: " + keyEvent + ", listener: " + mListener);
+        if (mListener != null) {
+            mListener.onKeyEvent(keyEvent);
+        }
+    }
+
+    void registerListener(Listener listener) {
+        mListener = listener;
+    }
+
+    void unregisterListener() {
+        mListener = null;
+    }
+
+    @Override
+    protected NavigationRenderer getNavigationRenderer() {
+        NavigationRenderer navigationRenderer = new NavigationRenderer() {
+            @Override
+            public CarNavigationInstrumentCluster getNavigationProperties() {
+                Log.i(TAG, "getNavigationProperties");
+                CarNavigationInstrumentCluster config =
+                        CarNavigationInstrumentCluster.createCluster(1000);
+                Log.i(TAG, "getNavigationProperties, returns: " + config);
+                return config;
+            }
+
+            @Override
+            public void onStartNavigation() {
+                Log.i(TAG, "onStartNavigation");
+            }
+
+            @Override
+            public void onStopNavigation() {
+                Log.i(TAG, "onStopNavigation");
+            }
+
+            @Override
+            public void onNextTurnChanged(int event, CharSequence eventName, int turnAngle,
+                    int turnNumber, Bitmap image, int turnSide) {
+                Log.i(TAG, "event: " + event + ", eventName: " + eventName +
+                        ", turnAngle: " + turnAngle + ", turnNumber: " + turnNumber +
+                        ", image: " + image + ", turnSide: " + turnSide);
+                mListener.onShowToast("Next turn: " + eventName);
+            }
+
+            @Override
+            public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds,
+                    int displayDistanceMillis, int displayDistanceUnit) {
+                Log.i(TAG, "onNextTurnDistanceChanged, distanceMeters: " + distanceMeters
+                        + ", timeSeconds: " + timeSeconds
+                        + ", displayDistanceMillis: " + displayDistanceMillis
+                        + ", displayDistanceUnit: " + displayDistanceUnit);
+                mListener.onShowToast("Next turn distance: " + distanceMeters + " meters.");
+            }
+        };
+
+        Log.i(TAG, "createNavigationRenderer, returns: " + navigationRenderer);
+        return navigationRenderer;
+    }
+
+    class LocalBinder extends Binder {
+        SampleClusterServiceImpl getService() {
+            // Return this instance of LocalService so clients can call public methods
+            return SampleClusterServiceImpl.this;
+        }
+    }
+
+    interface Listener {
+        void onKeyEvent(KeyEvent event);
+        void onShowToast(String text);
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (args != null && args.length > 0) {
+            execShellCommand(args);
+        }
+    }
+
+    private void doKeyEvent(int keyCode) {
+        Log.i(TAG, "doKeyEvent, keyCode: " + keyCode);
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+        KeyEvent event = obtainKeyEvent(keyCode, downTime, eventTime, KeyEvent.ACTION_DOWN);
+        onKeyEvent(event);
+
+        eventTime = SystemClock.uptimeMillis();
+        event = obtainKeyEvent(keyCode, downTime, eventTime, KeyEvent.ACTION_UP);
+        onKeyEvent(event);
+    }
+
+    private KeyEvent obtainKeyEvent(int keyCode, long downTime, long eventTime, int action) {
+        int scanCode = 0;
+        if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+            scanCode = 108;
+        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+            scanCode = 106;
+        }
+        return KeyEvent.obtain(
+                    downTime,
+                    eventTime,
+                    action,
+                    keyCode,
+                    0 /* repeat */,
+                    0 /* meta state */,
+                    0 /* deviceId*/,
+                    scanCode /* scancode */,
+                    KeyEvent.FLAG_FROM_SYSTEM /* flags */,
+                    InputDevice.SOURCE_KEYBOARD,
+                    null /* characters */);
+    }
+
+    private void execShellCommand(String[] args) {
+        Log.i(TAG, "execShellCommand, args: " + Arrays.toString(args));
+
+        String command = args[0];
+
+        switch (command) {
+            case "injectKey": {
+                if (args.length > 1) {
+                    doKeyEvent(parseInt(args[1]));
+                } else {
+                    Log.i(TAG, "Not enough arguments");
+                }
+                break;
+            }
+            case "destroyOverlayDisplay": {
+                Settings.Global.putString(getContentResolver(),
+                    Global.OVERLAY_DISPLAY_DEVICES, "");
+                break;
+            }
+
+            case "createOverlayDisplay": {
+                if (args.length > 1) {
+                    Settings.Global.putString(getContentResolver(),
+                            Global.OVERLAY_DISPLAY_DEVICES, args[1]);
+                } else {
+                    Log.i(TAG, "Not enough arguments, expected 2");
+                }
+                break;
+            }
+
+            case "setUnobscuredArea": {
+                if (args.length > 5) {
+                    Rect unobscuredArea = new Rect(parseInt(args[2]), parseInt(args[3]),
+                            parseInt(args[4]), parseInt(args[5]));
+                    try {
+                        setClusterActivityState(args[1],
+                                ClusterActivityState.create(true, unobscuredArea).toBundle());
+                    } catch (CarNotConnectedException e) {
+                        Log.i(TAG, "Failed to set activity state.", e);
+                    }
+                } else {
+                    Log.i(TAG, "wrong format, expected: category left top right bottom");
+                }
+            }
+        }
+    }
+
+    private static Display getInstrumentClusterDisplay(Context context) {
+        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+        Display[] displays = displayManager.getDisplays();
+
+        Log.d(TAG, "There are currently " + displays.length + " displays connected.");
+        for (Display display : displays) {
+            Log.d(TAG, "  " + display);
+        }
+
+        if (displays.length > 1) {
+            // TODO: assuming that secondary display is instrument cluster. Put this into settings?
+            return displays[1];
+        }
+        return null;
+    }
+
+}
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index 4d29276..2a96898 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -18,8 +18,8 @@
         package="com.google.android.car.kitchensink"
         android:sharedUserId="android.uid.system">
     <uses-sdk
-        android:minSdkVersion="22"
-        android:targetSdkVersion='23'/>
+        android:minSdkVersion="24"
+        android:targetSdkVersion='25'/>
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
@@ -31,6 +31,8 @@
     <uses-permission android:name="android.car.permission.CAR_CAMERA" />
     <uses-permission android:name="android.car.permission.CAR_NAVIGATION_MANAGER"/>
     <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
+    <uses-permission android:name="android.car.permission.VEHICLE_DYNAMICS_STATE"/>
+    <uses-permission android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"/>
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -88,12 +90,17 @@
                   android:launchMode="singleTop">
         </activity>
 
-        <receiver android:name=".bluetooth.MapReceiver"
-            android:permission="android.permission.READ_SMS">
+        <activity android:name=".cluster.FakeClusterNavigationActivity"
+                  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                  android:launchMode="singleInstance"
+                  android:resizeableActivity="true"
+                  android:allowEmbedded="true"
+                  android:permission="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL">
             <intent-filter>
-                <action android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"/>
-                <action android:name="android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS"/>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.car.cluster.NAVIGATION"/>
             </intent-filter>
-        </receiver>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
index 0547a8f..5dbefc1 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
@@ -57,4 +57,9 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/bluetooth_quiet_mode_enable"/>
+    <Button
+        android:id="@+id/bluetooth_hold_call"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/bluetooth_hold_call"/>
 </LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/fake_cluster_navigation_activity.xml b/tests/EmbeddedKitchenSinkApp/res/layout/fake_cluster_navigation_activity.xml
new file mode 100644
index 0000000..11f0a6b
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/fake_cluster_navigation_activity.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/app_bg">
+
+    <ImageView
+            android:id="@+id/unobscuredArea"
+            android:alpha="0.25"
+            android:background="@android:color/white"
+            android:layout_height="0dp"
+            android:layout_width="0dp"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
index 71c7fca..cb785a9 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
@@ -17,26 +17,25 @@
               android:orientation="vertical"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:layout_marginTop="160dp"
+              android:layout_marginTop="40dp"
               android:layout_marginStart="40dp">
 
     <LinearLayout
             android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
         <LinearLayout
                 android:orientation="horizontal"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content">
             <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
-                    android:text="@string/cluster_start" android:id="@+id/cluster_start_button"
-                    android:layout_column="0" android:textSize="32sp"/>
+                    android:text="@string/cluster_start" android:id="@+id/cluster_start_button"/>
             <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
-                    android:text="@string/cluster_turn_left" android:id="@+id/cluster_turn_left_button"
-                    android:layout_column="0" android:textSize="32sp"/>
+                    android:text="@string/cluster_turn_left" android:id="@+id/cluster_turn_left_button"/>
             <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
-                    android:text="@string/cluster_stop" android:id="@+id/cluster_stop_button"
-                    android:layout_column="0" android:textSize="32sp"/>
+                    android:text="@string/cluster_stop" android:id="@+id/cluster_stop_button"/>
+            <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+                    android:text="@string/cluster_start_activity" android:id="@+id/cluster_start_activity"/>
         </LinearLayout>
     </LinearLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
index 7554508..49497a2 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
 <!-- We use this container to place kitchen app fragments. It insets the fragment contents -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/kitchen_content"
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml b/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml
index e5b1ed2..590e7f1 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml
@@ -38,6 +38,12 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:text="@string/radio_close" />
+        <ToggleButton
+                android:id="@+id/togglebutton_mute_radio"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textOn="@string/radio_muted"
+                android:textOff="@string/radio_unmuted" />
     </LinearLayout>
     <LinearLayout
             android:layout_width="match_parent"
@@ -76,6 +82,33 @@
             android:layout_height="wrap_content"
             android:orientation="horizontal"
             android:layout_weight="1" >
+        <EditText
+                android:id="@+id/edittext_station_frequency"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:hint="@string/radio_enter_station_hint"
+                android:inputType="number" />
+        <Button
+                android:id="@+id/button_radio_tune_to_station"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/radio_tune_to_station" />
+        <Button
+                android:id="@+id/button_radio_step_up"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/radio_step_up" />
+        <Button
+                android:id="@+id/button_radio_step_down"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/radio_step_down" />
+    </LinearLayout>
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_weight="1" >
         <ToggleButton
                 android:id="@+id/button_band_selection"
                 android:layout_width="wrap_content"
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
deleted file mode 100644
index 25e6be6..0000000
--- a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
+++ /dev/null
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3
new file mode 100644
index 0000000..a1db0bc
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index e9214d9..fbba2a0 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -113,10 +113,11 @@
     <string name="open_kb_button">Hide/Show Input</string>
 
     <!-- instrument cluster -->
-    <string name="cluster_start">Start Nav</string>
-    <string name="cluster_turn_left">Turn left</string>
-    <string name="cluster_stop">Stop Nav</string>
+    <string name="cluster_start">Start metadata</string>
+    <string name="cluster_turn_left">Send turn-by-turn</string>
+    <string name="cluster_stop">Stop metadata</string>
     <string name="cluster_nav_app_context_loss">Navigation app context lost!</string>
+    <string name="cluster_start_activity">Start Nav Activity</string>
 
     <!--  input test -->
     <string name="volume_up">Volume +</string>
@@ -140,6 +141,8 @@
     <!-- radio test -->
     <string name="radio_open">Open</string>
     <string name="radio_close">Close</string>
+    <string name="radio_muted">Muted</string>
+    <string name="radio_unmuted">Unmuted</string>
     <string name="radio_get_radio_focus">Get Radio focus</string>
     <string name="radio_release_radio_focus">Release Radio focus</string>
     <string name="radio_get_focus">Get Audio focus</string>
@@ -147,6 +150,10 @@
     <string name="radio_next">Next</string>
     <string name="radio_prev">Previous</string>
     <string name="radio_scan_cancel">Cancel scan</string>
+    <string name="radio_tune_to_station">Tune to Station</string>
+    <string name="radio_step_up">Step Up</string>
+    <string name="radio_step_down">Step Down</string>
+    <string name="radio_enter_station_hint">Enter Station Frequency (kHz)</string>
     <string name="radio_get_program_info">getProgramInformation</string>
     <string name="radio_am">AM</string>
     <string name="radio_fm">FM</string>
@@ -156,24 +163,28 @@
     <string name="radio_artist_info">Artist info: %1$s</string>
     <string name="radio_na">N/A</string>
 
-    <!-- sensorts test -->
+    <!-- sensors test -->
     <string name="sensor_na">N/A</string>
 
     <string name="sensor_environment">Environment[%1$s]: temperature=%2$s, pressure=%3$s</string>
     <string name="sensor_night">Night[%1$s]: isNight=%2$s</string>
     <string name="sensor_gear">Gear[%1$s]: gear=%2$s</string>
-    <string name="sensor_parking_break">Parking break[%1$s]: isEngaged=%2$s</string>
-    <string name="sensor_fuel_level">Fuel level[%1$s]: lebel=%2$s, range=%3$s, lowFuelWarning=%4$s</string>
+    <string name="sensor_parking_brake">Parking brake[%1$s]: isEngaged=%2$s</string>
+    <string name="sensor_fuel_level">Fuel level[%1$s]: level=%2$s, range=%3$s, lowFuelWarning=%4$s</string>
     <string name="sensor_odometer">Odometer[%1$s]: kms=%2$s</string>
-    <string name="sensor_rpm">Rpm[%1$s]: rpm=%2$s</string>
+    <string name="sensor_rpm">RPM[%1$s]: rpm=%2$s</string>
     <string name="sensor_speed">Speed[%1$s]: speed=%2$s</string>
     <string name="sensor_driving_status">Driving status[%1$s]: status=%2$s [bin=%3$s]</string>
     <string name="sensor_compass">Compass[%1$s]: bear=%2$s, pitch=%3$s, roll=%4$s</string>
     <string name="sensor_accelerometer">Accelerometer[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
-    <string name="sensor_gyroscope">Gyroscpoe[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
+    <string name="sensor_gyroscope">Gyroscope[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
     <string name="sensor_location">Location[%1$s]: lat=%2$s, lon=%3$s, accuracy=%4$s, altitude=%5$s, speed=%6$s, bearing=%7$s</string>
     <string name="sensor_gps">GPS Satellites[%1$s]: inView: %2$s, inUse: %3$s. %4$s</string>
     <string name="sensor_single_gps_satellite">(%1$s): usedInFix: %2$s, prn: %3$s, snr: %4$s, azimuth: %5$s, elevation: %6$s</string>
+    <string name="sensor_wheel_ticks">Wheel Distance[%1$s]: reset=%2$s, FL=%3$s, FR=%4$s, RL=%5$s, RR=%6$s</string>
+    <string name="sensor_wheel_ticks_cfg">Wheel Distance Config: Wheels=%1$s, FL=%2$s, FR=%3$s, RL=%4$s, RR=%5$s</string>
+    <string name="sensor_abs_is_active">ABS[%1$s]: isActive=%2$s</string>
+    <string name="sensor_traction_control_is_active">Traction Control[%1$s]: isActive=%2$s</string>
 
     <string name="volume_test">Volume Test</string>
     <string name="volume_up_logical">Vol +</string>
@@ -201,6 +212,7 @@
     <string name="bluetooth_sco_disconnect">SCO disconnect</string>
     <string name="bluetooth_pick_device">Pick Device</string>
     <string name="bluetooth_quiet_mode_enable">Quiet Mode</string>
+    <string name="bluetooth_hold_call">Hold call</string>
 
     <!--Car Service Settings-->
     <string name="garage_title">Garage Mode</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
index db1816b..7dddd1f 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
@@ -256,9 +256,9 @@
         public void onPropertySet(VehiclePropValue value) {
             ArrayList<Integer> v = value.value.int32Values;
 
-            int stream = v.get(VehicleAudioVolumeIndex.INDEX_STREAM);
-            int volume = v.get(VehicleAudioVolumeIndex.INDEX_VOLUME);
-            int state = v.get(VehicleAudioVolumeIndex.INDEX_STATE);
+            int stream = v.get(VehicleAudioVolumeIndex.STREAM);
+            int volume = v.get(VehicleAudioVolumeIndex.VOLUME);
+            int state = v.get(VehicleAudioVolumeIndex.STATE);
 
             VehiclePropValue injectValue =
                     VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
@@ -272,7 +272,7 @@
 
         @Override
         public VehiclePropValue onPropertyGet(VehiclePropValue value) {
-            int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.INDEX_STREAM);
+            int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.STREAM);
             int volume = mCurrent.get(stream);
 
             return VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
index 618367d..74345aa 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
@@ -61,7 +61,8 @@
                 }
             } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
                 if (isPlaying()) {
-                    mPlayer.setVolume(0.5f, 0.5f);
+                    // Duck to 20% volume (which matches system ducking as of this date)
+                    mPlayer.setVolume(0.2f, 0.2f);
                 }
             } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT && mRepeat) {
                 if (isPlaying()) {
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
index dec0dc6..ce90651 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -175,7 +175,7 @@
                     //ignore for now
                 }
 
-                mMusicPlayer = new AudioPlayer(mContext, R.raw.john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio,
+                mMusicPlayer = new AudioPlayer(mContext, R.raw.well_worth_the_wait,
                         mMusicAudioAttrib);
                 mMusicPlayerShort = new AudioPlayer(mContext, R.raw.ring_classic_01,
                         mMusicAudioAttrib);
@@ -219,7 +219,9 @@
         mMediaPlay.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                mMusicPlayer.start(false, true, AudioManager.AUDIOFOCUS_GAIN);
+                boolean requestFocus = true;
+                boolean repeat = true;
+                mMusicPlayer.start(requestFocus, repeat, AudioManager.AUDIOFOCUS_GAIN);
             }
         });
         mMediaPlayOnce = (Button) view.findViewById(R.id.button_media_play_once);
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
index 30804fe..cb668c4 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
@@ -57,15 +57,17 @@
     Button mScoConnect;
     Button mScoDisconnect;
     Button mEnableQuietMode;
+    Button mHoldCall;
 
     BluetoothHeadsetClient mHfpClientProfile;
 
     // Intent for picking a Bluetooth device
-    public static final String DEVICE_PICKER_ACTION = "android.bluetooth.devicepicker.action.LAUNCH";
+    public static final String DEVICE_PICKER_ACTION =
+        "android.bluetooth.devicepicker.action.LAUNCH";
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-            @Nullable Bundle savedInstanceState) {
+        @Nullable Bundle savedInstanceState) {
         View v = inflater.inflate(R.layout.bluetooth_headset, container, false);
 
         mPickedDeviceText = (TextView) v.findViewById(R.id.bluetooth_device);
@@ -73,8 +75,9 @@
         mConnect = (Button) v.findViewById(R.id.bluetooth_headset_connect);
         mDisconnect = (Button) v.findViewById(R.id.bluetooth_headset_disconnect);
         mScoConnect = (Button) v.findViewById(R.id.bluetooth_sco_connect);
-        mScoDisconnect= (Button) v.findViewById(R.id.bluetooth_sco_disconnect);
+        mScoDisconnect = (Button) v.findViewById(R.id.bluetooth_sco_disconnect);
         mEnableQuietMode = (Button) v.findViewById(R.id.bluetooth_quiet_mode_enable);
+        mHoldCall = (Button) v.findViewById(R.id.bluetooth_hold_call);
 
         // Pick a bluetooth device
         mDevicePicker.setOnClickListener(new View.OnClickListener() {
@@ -123,6 +126,14 @@
                 mBluetoothAdapter.enableNoAutoConnect();
             }
         });
+
+        // Place the current call on hold
+        mHoldCall.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                holdCall();
+            }
+        });
         return v;
     }
 
@@ -192,6 +203,20 @@
         mHfpClientProfile.disconnectAudio(mPickedDevice);
     }
 
+    void holdCall() {
+        if (mPickedDevice == null) {
+            Log.w(TAG, "Device null when trying to put the call on hold!");
+            return;
+        }
+
+        if (mHfpClientProfile == null) {
+            Log.w(TAG, "HFP Profile proxy not available, cannot put the call on hold " +
+                mPickedDevice);
+            return;
+        }
+        mHfpClientProfile.holdCall(mPickedDevice);
+    }
+
     private final BroadcastReceiver mPickerReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java
deleted file mode 100644
index e0d1ca0..0000000
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java
+++ /dev/null
@@ -1,37 +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.google.android.car.kitchensink.bluetooth;
-
-import android.bluetooth.BluetoothMapClient;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import android.widget.Toast;
-
-public class MapReceiver extends BroadcastReceiver {
-    private static final String TAG = "CAR.BLUETOOTH.KS";
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        Log.d(TAG, "MAP onReceive");
-        String action = intent.getAction();
-        if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) {
-            Toast.makeText(context, intent.getStringExtra(android.content.Intent.EXTRA_TEXT),
-                    Toast.LENGTH_LONG).show();
-        }
-    }
-}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java
new file mode 100644
index 0000000..964d812
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.cluster;
+
+import android.app.Activity;
+import android.car.cluster.CarInstrumentClusterManager;
+import android.car.cluster.ClusterActivityState;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.car.Car;
+import android.support.car.CarConnectionCallback;
+import android.support.car.CarNotConnectedException;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import com.google.android.car.kitchensink.R;
+
+/**
+ * Fake navigation activity for instrument cluster.
+ */
+public class FakeClusterNavigationActivity
+        extends Activity
+        implements CarInstrumentClusterManager.Callback {
+
+    private final static String TAG = FakeClusterNavigationActivity.class.getSimpleName();
+
+    private Car mCarApi;
+    private CarInstrumentClusterManager mClusterManager;
+    private ImageView mUnobscuredArea;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentView(R.layout.fake_cluster_navigation_activity);
+        mUnobscuredArea = findViewById(R.id.unobscuredArea);
+
+        mCarApi = Car.createCar(this /* context */, new CarConnectionCallback() {
+
+            @Override
+            public void onConnected(Car car) {
+                onCarConnected(car);
+            }
+
+            @Override
+            public void onDisconnected(Car car) {
+                onCarDisconnected(car);
+            }
+        });
+        Log.i(TAG, "Connecting to car api...");
+        mCarApi.connect();
+    }
+
+
+    @Override
+    public void onClusterActivityStateChanged(String category, Bundle clusterActivityState) {
+        ClusterActivityState state = ClusterActivityState.fromBundle(clusterActivityState);
+        Log.i(TAG, "onClusterActivityStateChanged, category: " + category + ", state: " + state);
+
+        Rect unobscured = state.getUnobscuredBounds();
+        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+                unobscured.width(), unobscured.height());
+        lp.setMargins(unobscured.left, unobscured.top, 0, 0);
+        mUnobscuredArea.setLayoutParams(lp);
+    }
+
+    private void onCarConnected(Car car) {
+        Log.i(TAG, "onCarConnected, car: " + car);
+        try {
+            mClusterManager = (CarInstrumentClusterManager) car.getCarManager(
+                    android.car.Car.CAR_INSTRUMENT_CLUSTER_SERVICE);
+        } catch (CarNotConnectedException e) {
+            throw new IllegalStateException(e);
+        }
+
+        try {
+            Log.i(TAG, "registering callback...");
+            mClusterManager.registerCallback(CarInstrumentClusterManager.CATEGORY_NAVIGATION, this);
+            Log.i(TAG, "callback registered");
+        } catch (android.car.CarNotConnectedException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private void onCarDisconnected(Car car) {
+
+    }
+}
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
index 28e0a5d..cfae45f 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
@@ -16,18 +16,22 @@
 package com.google.android.car.kitchensink.cluster;
 
 import android.app.AlertDialog;
+import android.car.cluster.CarInstrumentClusterManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.car.Car;
 import android.support.car.CarAppFocusManager;
-import android.support.car.CarNotConnectedException;
 import android.support.car.CarConnectionCallback;
+import android.support.car.CarNotConnectedException;
 import android.support.car.navigation.CarNavigationStatusManager;
 import android.support.v4.app.Fragment;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Toast;
 
 import com.google.android.car.kitchensink.R;
 
@@ -37,30 +41,30 @@
 public class InstrumentClusterFragment extends Fragment {
     private static final String TAG = InstrumentClusterFragment.class.getSimpleName();
 
+    private static final int DISPLAY_IN_CLUSTER_PERMISSION_REQUEST = 1;
+
     private CarNavigationStatusManager mCarNavigationStatusManager;
     private CarAppFocusManager mCarAppFocusManager;
     private Car mCarApi;
 
-    private final CarConnectionCallback mCarConnectionCallback =
-            new CarConnectionCallback() {
-                @Override
-                public void onConnected(Car car) {
-                    Log.d(TAG, "Connected to Car Service");
-                    try {
-                        mCarNavigationStatusManager = (CarNavigationStatusManager) mCarApi.getCarManager(
-                                android.car.Car.CAR_NAVIGATION_SERVICE);
-                        mCarAppFocusManager =
-                                (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
-                    } catch (CarNotConnectedException e) {
-                        Log.e(TAG, "Car is not connected!", e);
-                    }
+    private final CarConnectionCallback mCarConnectionCallback = new CarConnectionCallback() {
+            @Override
+            public void onConnected(Car car) {
+                Log.d(TAG, "Connected to Car Service");
+                try {
+                    mCarNavigationStatusManager =
+                            mCarApi.getCarManager(CarNavigationStatusManager.class);
+                    mCarAppFocusManager = mCarApi.getCarManager(CarAppFocusManager.class);
+                } catch (CarNotConnectedException e) {
+                    Log.e(TAG, "Car is not connected!", e);
                 }
+            }
 
-                @Override
-                public void onDisconnected(Car car) {
-                    Log.d(TAG, "Disconnect from Car Service");
-                }
-            };
+            @Override
+            public void onDisconnected(Car car) {
+            Log.d(TAG, "Disconnect from Car Service");
+        }
+    };
 
     private void initCarApi() {
         if (mCarApi != null && mCarApi.isConnected()) {
@@ -80,6 +84,7 @@
 
         view.findViewById(R.id.cluster_start_button).setOnClickListener(v -> initCluster());
         view.findViewById(R.id.cluster_turn_left_button).setOnClickListener(v -> turnLeft());
+        view.findViewById(R.id.cluster_start_activity).setOnClickListener(v -> startNavActivity());
 
         return view;
     }
@@ -91,6 +96,31 @@
         super.onCreate(savedInstanceState);
     }
 
+    private void startNavActivity() {
+        CarInstrumentClusterManager clusterManager;
+        try {
+            clusterManager = (CarInstrumentClusterManager) mCarApi.getCarManager(
+                    android.car.Car.CAR_INSTRUMENT_CLUSTER_SERVICE);
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Failed to get CarInstrumentClusterManager", e);
+            Toast.makeText(getContext(), "Failed to get CarInstrumentClusterManager",
+                    Toast.LENGTH_LONG).show();
+            return;
+        }
+
+        // Implicit intent
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(CarInstrumentClusterManager.CATEGORY_NAVIGATION);
+        try {
+            clusterManager.startActivity(intent);
+        } catch (android.car.CarNotConnectedException e) {
+            Log.e(TAG, "Failed to startActivity in cluster", e);
+            Toast.makeText(getContext(), "Failed to start activity in cluster",
+                    Toast.LENGTH_LONG).show();
+            return;
+        }
+    }
+
     private void turnLeft() {
         try {
             mCarNavigationStatusManager
@@ -100,18 +130,20 @@
                     CarNavigationStatusManager.DISTANCE_METERS);
         } catch (CarNotConnectedException e) {
             e.printStackTrace();
-            initCarApi();  // This might happen due to inst cluster renderer crash.
         }
     }
 
     private void initCluster() {
         try {
-            mCarAppFocusManager.addFocusListener(new CarAppFocusManager.OnAppFocusChangedListener() {
-        @Override
-        public void onAppFocusChanged(CarAppFocusManager manager, int appType, boolean active) {
-            Log.d(TAG, "onAppFocusChanged, appType: " + appType + " active: " + active);
-        }
-    }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+            mCarAppFocusManager
+                    .addFocusListener(new CarAppFocusManager.OnAppFocusChangedListener() {
+                        @Override
+                        public void onAppFocusChanged(CarAppFocusManager manager, int appType,
+                                boolean active) {
+                            Log.d(TAG, "onAppFocusChanged, appType: " + appType + " active: "
+                                    + active);
+                        }
+                    }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Failed to register focus listener", e);
         }
@@ -126,6 +158,7 @@
                         .setMessage(R.string.cluster_nav_app_context_loss)
                         .show();
             }
+
             @Override
             public void onAppFocusOwnershipGranted(CarAppFocusManager manager, int focus) {
                 Log.w(TAG, "onAppFocusOwnershipGranted, focus: " + focus);
@@ -155,7 +188,33 @@
                     .sendNavigationStatus(CarNavigationStatusManager.STATUS_ACTIVE);
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Failed to set navigation status, reconnecting to the car", e);
-            initCarApi();  // This might happen due to inst cluster renderer crash.
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.i(TAG, "onResume!");
+        if (getActivity().checkSelfPermission(android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER)
+                != PackageManager.PERMISSION_GRANTED) {
+            Log.i(TAG, "Requesting: " + android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER);
+
+            requestPermissions(new String[] {android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER},
+                    DISPLAY_IN_CLUSTER_PERMISSION_REQUEST);
+        } else {
+            Log.i(TAG, "All required permissions granted");
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        if (DISPLAY_IN_CLUSTER_PERMISSION_REQUEST == requestCode) {
+            for (int i = 0; i < permissions.length; i++) {
+                boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
+                Log.i(TAG, "onRequestPermissionsResult, requestCode: " + requestCode
+                        + ", permission: " + permissions[i] + ", granted: " + granted);
+            }
         }
     }
 }
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
index 71deee8..b024bfd 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
@@ -18,12 +18,11 @@
 
 import android.annotation.Nullable;
 import android.car.Car;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
-import android.car.hardware.CarDiagnosticManager.OnDiagnosticEventListener;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener;
 import android.graphics.Color;
 import android.os.Bundle;
-import android.os.Handler;
 import android.support.car.hardware.CarSensorManager;
 import android.support.v4.app.Fragment;
 import android.util.Log;
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
index 3f5ef86..756ac0b 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
@@ -167,12 +167,9 @@
                 }
             };
 
-    public HvacTestFragment() {
-        setHvacManager( ((KitchenSinkActivity)getActivity()).getHvacManager() );
-    }
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
+        mCarHvacManager = ((KitchenSinkActivity)getActivity()).getHvacManager();
         super.onCreate(savedInstanceState);
         try {
             mCarHvacManager.registerCallback(mHvacCallback);
@@ -262,11 +259,6 @@
         return v;
     }
 
-    public void setHvacManager(CarHvacManager hvacManager) {
-        Log.d(TAG, "setHvacManager()");
-        mCarHvacManager = hvacManager;
-    }
-
     private void configureOutsideTemp(View v, CarPropertyConfig prop) {
         // Do nothing
     }
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java
index baba61b..0214791 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java
@@ -41,6 +41,7 @@
 import android.widget.Button;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
 import android.widget.TextView;
 import android.widget.ToggleButton;
 
@@ -92,7 +93,7 @@
             }
             mArtist = metadata.getString(RadioMetadata.METADATA_KEY_ARTIST);
             mSong = metadata.getString(RadioMetadata.METADATA_KEY_TITLE);
-            mStation = metadata.getString(RadioMetadata.METADATA_KEY_RDS_PI);
+            mStation = metadata.getString(RadioMetadata.METADATA_KEY_RDS_PS);
             updateMessages();
         }
 
@@ -117,6 +118,11 @@
     private Button mRadioPrev;
     private Button mRadioScanCancel;
     private Button mRadioGetProgramInfo;
+    private Button mRadioTuneToStation;
+    private Button mRadioStepUp;
+    private Button mRadioStepDown;
+    private EditText mStationFrequency;
+    private ToggleButton mToggleMuteRadio;
     private ToggleButton mRadioBand;
     private TextView mStationInfo;
     private TextView mChannelInfo;
@@ -166,6 +172,14 @@
         mRadioPrev = (Button) view.findViewById(R.id.button_radio_prev);
         mRadioScanCancel = (Button) view.findViewById(R.id.button_radio_scan_cancel);
         mRadioGetProgramInfo = (Button) view.findViewById(R.id.button_radio_get_program_info);
+        mRadioTuneToStation = (Button) view.findViewById(R.id.button_radio_tune_to_station);
+        mRadioStepUp = (Button) view.findViewById(R.id.button_radio_step_up);
+        mRadioStepDown = (Button) view.findViewById(R.id.button_radio_step_down);
+
+        mStationFrequency = (EditText) view.findViewById(R.id.edittext_station_frequency);
+
+        mToggleMuteRadio = (ToggleButton) view.findViewById(R.id.togglebutton_mute_radio);
+        mToggleMuteRadio.setChecked(true);
         mRadioBand = (ToggleButton) view.findViewById(R.id.button_band_selection);
 
         mStationInfo = (TextView) view.findViewById(R.id.radio_station_info);
@@ -211,8 +225,7 @@
             }
         });
         mCar.connect();
-        mAudioManager = (AudioManager) getContext().getSystemService(
-                Context.AUDIO_SERVICE);
+        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
         initializeRadio();
     }
 
@@ -240,11 +253,11 @@
                 Log.d(TAG, "loading band: " + band.toString());
             }
 
-            if (mFmDescriptor == null && band.getType() == RadioManager.BAND_FM) {
+            if (mFmDescriptor == null && band.isFmBand()) {
                 mFmDescriptor = (RadioManager.FmBandDescriptor) band;
             }
 
-            if (mAmDescriptor == null && band.getType() == RadioManager.BAND_AM) {
+            if (mAmDescriptor == null && band.isAmBand()) {
                 mAmDescriptor = (RadioManager.AmBandDescriptor) band;
             }
         }
@@ -273,6 +286,17 @@
             @Override
             public void onClick(View v) {
                 handleRadioEnd();
+                mToggleMuteRadio.setChecked(true);
+                updateStates();
+            }
+        });
+        mToggleMuteRadio.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (DBG) {
+                    Log.i(TAG, "Toggle mute radio");
+                }
+                mRadioTuner.setMute(!mRadioTuner.getMute());
                 updateStates();
             }
         });
@@ -362,6 +386,49 @@
                 updateStates();
             }
         });
+        mRadioTuneToStation.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (DBG) {
+                    Log.i(TAG, "Tuning to station");
+                }
+                String station = mStationFrequency.getText().toString().trim();
+                if (mRadioTuner != null && !(station.equals(""))) {
+                    mRadioTuner.tune(Integer.parseInt(station), 0);
+                }
+                resetMessages();
+                updateMessages();
+                updateStates();
+            }
+        });
+        mRadioStepUp.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (DBG) {
+                    Log.i(TAG, "Step up");
+                }
+                if (mRadioTuner != null) {
+                    mRadioTuner.step(RadioTuner.DIRECTION_UP, false);
+                }
+                resetMessages();
+                updateMessages();
+                updateStates();
+            }
+        });
+        mRadioStepDown.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (DBG) {
+                    Log.i(TAG, "Step down");
+                }
+                if (mRadioTuner != null) {
+                    mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
+                }
+                resetMessages();
+                updateMessages();
+                updateStates();
+            }
+        });
         mRadioGetProgramInfo.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -394,6 +461,7 @@
     private void updateStates() {
         mOpenRadio.setEnabled(mRadioTuner == null);
         mCloseRadio.setEnabled(mRadioTuner != null);
+        mToggleMuteRadio.setEnabled(mRadioTuner != null);
         mGetRadioFocus.setEnabled(!mHasRadioFocus);
         mReleaseRadioFocus.setEnabled(mHasRadioFocus);
         mGetFocus.setEnabled(!mHasSecondaryFocus);
@@ -402,6 +470,10 @@
         mRadioPrev.setEnabled(mRadioTuner != null);
         mRadioBand.setEnabled(mRadioTuner != null);
         mRadioScanCancel.setEnabled(mRadioTuner != null);
+        mRadioTuneToStation.setEnabled(mRadioTuner != null);
+        mRadioStepUp.setEnabled(mRadioTuner != null);
+        mRadioStepDown.setEnabled(mRadioTuner != null);
+        mStationFrequency.setEnabled(mRadioTuner != null);
         mRadioGetProgramInfo.setEnabled(mRadioTuner != null);
     }
 
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
index 262e473..d502c75 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
@@ -24,6 +24,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.car.CarNotConnectedException;
+import android.support.car.hardware.CarSensorConfig;
 import android.support.car.hardware.CarSensorEvent;
 import android.support.car.hardware.CarSensorManager;
 import android.support.v4.app.Fragment;
@@ -58,7 +59,8 @@
         Manifest.permission.ACCESS_COARSE_LOCATION,
         Car.PERMISSION_MILEAGE,
         Car.PERMISSION_FUEL,
-        Car.PERMISSION_SPEED
+        Car.PERMISSION_SPEED,
+        Car.PERMISSION_VEHICLE_DYNAMICS_STATE
     };
 
     private final CarSensorManager.OnSensorChangedListener mOnSensorChangedListener =
@@ -219,7 +221,7 @@
                                 getTimestamp(event), level, range, lowFuelWarning));
                         break;
                     case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
-                        summary.add(getContext().getString(R.string.sensor_parking_break,
+                        summary.add(getContext().getString(R.string.sensor_parking_brake,
                                 getTimestamp(event),
                                 event == null ? mNaString :
                                 event.getParkingBrakeData().isEngaged));
@@ -271,6 +273,45 @@
                     case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
                         summary.add(getGyroscopeString(event));
                         break;
+                    case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
+                        if(event != null) {
+                            CarSensorEvent.CarWheelTickDistanceData d =
+                                event.getCarWheelTickDistanceData();
+                            summary.add(getContext().getString(R.string.sensor_wheel_ticks,
+                                getTimestamp(event), d.sensorResetCount, d.frontLeftWheelDistanceMm,
+                                d.frontRightWheelDistanceMm, d.rearLeftWheelDistanceMm,
+                                d.rearRightWheelDistanceMm));
+                        } else {
+                            summary.add(getContext().getString(R.string.sensor_wheel_ticks,
+                                getTimestamp(event), mNaString, mNaString, mNaString, mNaString,
+                                mNaString));
+                        }
+                        // Get the config data
+                        try {
+                            CarSensorConfig c = mSensorManager.getSensorConfig(
+                                CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
+                            summary.add(getContext().getString(R.string.sensor_wheel_ticks_cfg,
+                                c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS),
+                                c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK),
+                                c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK),
+                                c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK),
+                                c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK)));
+                        } catch (CarNotConnectedException e) {
+                            Log.e(TAG, "Car not connected or not supported", e);
+                        }
+                        break;
+                    case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE:
+                        summary.add(getContext().getString(R.string.sensor_abs_is_active,
+                            getTimestamp(event), event == null ? mNaString :
+                            event.getCarAbsActiveData().absIsActive));
+                        break;
+
+                    case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE:
+                        summary.add(
+                            getContext().getString(R.string.sensor_traction_control_is_active,
+                            getTimestamp(event), event == null ? mNaString :
+                            event.getCarTractionControlActiveData().tractionControlIsActive));
+                        break;
                     default:
                         // Should never happen.
                         Log.w(TAG, "Unrecognized event type: " + i);
diff --git a/tests/VmsPublisherClientSample/Android.mk b/tests/VmsPublisherClientSample/Android.mk
deleted file mode 100644
index 2aa6c40..0000000
--- a/tests/VmsPublisherClientSample/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := VmsPublisherClientSample
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_CERTIFICATE := testkey
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_JAVA_LIBRARIES += android.car
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/VmsPublisherClientSample/AndroidManifest.xml b/tests/VmsPublisherClientSample/AndroidManifest.xml
deleted file mode 100644
index fdc1a31..0000000
--- a/tests/VmsPublisherClientSample/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.google.android.car.vms.publisher">
-
-    <uses-permission android:name="android.car.permission.VMS_PUBLISHER" />
-    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
-    <uses-permission android:name="android.permission.CAMERA"/>
-
-    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion='25'/>
-
-    <application android:label="@string/app_name"
-                 android:icon="@mipmap/ic_launcher"
-                 android:directBootAware="true">
-        <service android:name=".VmsPublisherClientSampleService"
-                 android:exported="true"
-                 android:singleUser="true">
-        </service>
-    </application>
-</manifest>
diff --git a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
deleted file mode 100644
index 08d37cd..0000000
--- a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.car.vms.publisher;
-
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsPublisherClientService;
-import android.car.vms.VmsSubscriptionState;
-import android.os.Handler;
-import android.os.Message;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * This service is launched during the initialization of the VMS publisher service.
- * Once onVmsPublisherServiceReady is invoked, it starts publishing a single byte every second.
- */
-public class VmsPublisherClientSampleService extends VmsPublisherClientService {
-    public static final int PUBLISH_EVENT = 0;
-    public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0);
-
-    private byte mCounter = 0;
-    private AtomicBoolean mInitialized = new AtomicBoolean(false);
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == PUBLISH_EVENT && mInitialized.get()) {
-                periodicPublish();
-            }
-        }
-    };
-
-    /**
-     * Notifies that the publisher services are ready to be used: {@link #publish(VmsLayer, byte[])}
-     * and {@link #getSubscriptions()}.
-     */
-    @Override
-    public void onVmsPublisherServiceReady() {
-        VmsSubscriptionState subscriptionState = getSubscriptions();
-        onVmsSubscriptionChange(subscriptionState);
-    }
-
-    @Override
-    public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
-        if (mInitialized.compareAndSet(false, true)) {
-            for (VmsLayer layer : subscriptionState.getLayers()) {
-                if (layer.equals(TEST_LAYER)) {
-                    mHandler.sendEmptyMessage(PUBLISH_EVENT);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mInitialized.set(false);
-        mHandler.removeMessages(PUBLISH_EVENT);
-    }
-
-    private void periodicPublish() {
-        publish(TEST_LAYER, new byte[]{mCounter});
-        ++mCounter;
-        mHandler.sendEmptyMessageDelayed(PUBLISH_EVENT, 1000);
-    }
-}
diff --git a/tests/VmsSubscriberClientSample/res/layout/activity_main.xml b/tests/VmsSubscriberClientSample/res/layout/activity_main.xml
deleted file mode 100644
index ce05a4d..0000000
--- a/tests/VmsSubscriberClientSample/res/layout/activity_main.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/activity_main"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:paddingTop="@dimen/activity_vertical_margin"
-    android:paddingBottom="@dimen/activity_vertical_margin"
-    android:paddingLeft="@dimen/activity_horizontal_margin"
-    android:paddingRight="@dimen/activity_horizontal_margin"
-    tools:context="vms.apps.android.google.com.java.myapplication.MainActivity">
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:text=""
-        android:id="@+id/textview"/>
-</RelativeLayout>
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-hdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index cde69bc..0000000
--- a/tests/VmsSubscriberClientSample/res/mipmap-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-mdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index c133a0c..0000000
--- a/tests/VmsSubscriberClientSample/res/mipmap-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-xhdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index bfa42f0..0000000
--- a/tests/VmsSubscriberClientSample/res/mipmap-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-xxhdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 324e72c..0000000
--- a/tests/VmsSubscriberClientSample/res/mipmap-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-xxxhdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index aee44e1..0000000
--- a/tests/VmsSubscriberClientSample/res/mipmap-xxxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/values-w820dp/dimens.xml b/tests/VmsSubscriberClientSample/res/values-w820dp/dimens.xml
deleted file mode 100644
index 308a194..0000000
--- a/tests/VmsSubscriberClientSample/res/values-w820dp/dimens.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<resources>
-  <!-- Example customization of dimensions originally defined in res/values/dimens.xml
-         (such as screen margins) for screens with more than 820dp of available width. This
-         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
-  <dimen name="activity_horizontal_margin">64dp</dimen>
-</resources>
diff --git a/tests/VmsSubscriberClientSample/res/values/colors.xml b/tests/VmsSubscriberClientSample/res/values/colors.xml
deleted file mode 100644
index 5a077b3..0000000
--- a/tests/VmsSubscriberClientSample/res/values/colors.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-  <color name="colorPrimary">#3F51B5</color>
-  <color name="colorPrimaryDark">#303F9F</color>
-  <color name="colorAccent">#FF4081</color>
-</resources>
diff --git a/tests/VmsSubscriberClientSample/res/values/dimens.xml b/tests/VmsSubscriberClientSample/res/values/dimens.xml
deleted file mode 100644
index acf94cc..0000000
--- a/tests/VmsSubscriberClientSample/res/values/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<resources>
-  <!-- Default screen margins, per the Android Design guidelines. -->
-  <dimen name="activity_horizontal_margin">16dp</dimen>
-  <dimen name="activity_vertical_margin">16dp</dimen>
-</resources>
diff --git a/tests/VmsSubscriberClientSample/res/values/strings.xml b/tests/VmsSubscriberClientSample/res/values/strings.xml
deleted file mode 100644
index 24df55e..0000000
--- a/tests/VmsSubscriberClientSample/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
-  <string name="app_name">VmsSubscriberClientSample</string>
-</resources>
diff --git a/tests/VmsSubscriberClientSample/res/values/styles.xml b/tests/VmsSubscriberClientSample/res/values/styles.xml
deleted file mode 100644
index a7a0615..0000000
--- a/tests/VmsSubscriberClientSample/res/values/styles.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<resources>
-
-  <!-- Base application theme. -->
-  <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
-    <!-- Customize your theme here. -->
-  </style>
-
-</resources>
diff --git a/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml b/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml
deleted file mode 100644
index b10ddd0..0000000
--- a/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<automotiveApp>
-    <uses name="service" />
-    <uses name="projection" />
-    <uses name="activity" class="com.google.android.car.vms.subscriber.VmsSubscriberClientSampleActivity" />
-</automotiveApp>
diff --git a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
deleted file mode 100644
index fe32ab9..0000000
--- a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.car.vms.subscriber;
-
-import android.app.Activity;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsSubscriberManager;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.support.car.Car;
-import android.support.car.CarConnectionCallback;
-import android.util.Log;
-import android.widget.TextView;
-import java.util.List;
-
-/**
- * Connects to the Car service during onCreate. CarConnectionCallback.onConnected is invoked when
- * the connection is ready. Then, it subscribes to a VMS layer/version and updates the TextView when
- * a message is received.
- */
-public class VmsSubscriberClientSampleActivity extends Activity {
-    private static final String TAG = "VmsSampleActivity";
-    // The layer id and version should match the ones defined in
-    // com.google.android.car.vms.publisher.VmsPublisherClientSampleService
-    public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0);
-
-    private Car mCarApi;
-    private TextView mTextView;
-    private VmsSubscriberManager mVmsSubscriberManager;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-        mTextView = (TextView) findViewById(R.id.textview);
-        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
-            mCarApi = Car.createCar(this, mCarConnectionCallback);
-            mCarApi.connect();
-        } else {
-            Log.d(TAG, "No automotive feature.");
-        }
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        if (mCarApi != null) {
-            mCarApi.disconnect();
-        }
-        Log.i(TAG, "onDestroy");
-    }
-
-    private final CarConnectionCallback mCarConnectionCallback = new CarConnectionCallback() {
-        @Override
-        public void onConnected(Car car) {
-            Log.d(TAG, "Connected to Car Service");
-            mVmsSubscriberManager = getVmsSubscriberManager();
-            configureSubscriptions(mVmsSubscriberManager);
-        }
-
-        @Override
-        public void onDisconnected(Car car) {
-            Log.d(TAG, "Disconnect from Car Service");
-        }
-
-        private VmsSubscriberManager getVmsSubscriberManager() {
-            try {
-                return (VmsSubscriberManager) mCarApi.getCarManager(
-                        android.car.Car.VMS_SUBSCRIBER_SERVICE);
-            } catch (android.support.car.CarNotConnectedException e) {
-                Log.e(TAG, "Car is not connected!", e);
-            }
-            return null;
-        }
-
-        private void configureSubscriptions(VmsSubscriberManager vmsSubscriberManager) {
-            try {
-                vmsSubscriberManager.setListener(mListener);
-                vmsSubscriberManager.subscribe(TEST_LAYER);
-            } catch (android.car.CarNotConnectedException e) {
-                Log.e(TAG, "Car is not connected!", e);
-            }
-        }
-    };
-
-    private final VmsSubscriberManager.VmsSubscriberClientListener mListener =
-            new VmsSubscriberManager.VmsSubscriberClientListener() {
-                @Override
-                public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
-                    mTextView.setText(String.valueOf(payload[0]));
-                }
-
-                @Override
-                public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
-                    mTextView.setText(String.valueOf(availableLayers));
-                }
-
-                @Override
-                public void onCarDisconnected() {
-
-                }
-            };
-}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
index 21fb5e0..8783341 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
@@ -17,8 +17,8 @@
 package android.car.apitest;
 
 import android.car.Car;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
 import android.content.ComponentName;
 import android.content.ServiceConnection;
 import android.os.IBinder;
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java
index 9cf21e5..3a94b39 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java
@@ -19,7 +19,9 @@
 import android.car.Car;
 import android.car.CarInfoManager;
 import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
 
+@SmallTest
 public class CarInfoManagerTest extends CarApiTestBase {
 
     private CarInfoManager mInfoManager;
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java
index 953a4a0..31543bd 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java
@@ -19,8 +19,10 @@
 import android.car.CarAppFocusManager;
 import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
 import android.car.navigation.CarNavigationStatusManager;
+import android.os.Bundle;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
+import com.google.android.collect.Lists;
 
 /**
  * Unit tests for {@link CarNavigationStatusManager}
@@ -79,8 +81,19 @@
         assertTrue(mCarAppFocusManager.isOwningFocus(ownershipCallback,
                 CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
 
+        Log.i(TAG, "Instrument cluster: " + mCarNavigationManager.getInstrumentClusterInfo());
+
         // TODO: we should use mocked HAL to be able to verify this, right now just make sure that
         // it is not crashing and logcat has appropriate traces.
         mCarNavigationManager.sendNavigationStatus(1);
+
+        Bundle bundle = new Bundle();
+        bundle.putInt("BUNDLE_INTEGER_VALUE", 1234);
+        bundle.putFloat("BUNDLE_FLOAT_VALUE", 12.3456f);
+        bundle.putStringArrayList("BUNDLE_ARRAY_OF_STRINGS",
+                Lists.newArrayList("Value A", "Value B", "Value Z"));
+
+        mCarNavigationManager.sendEvent
+                (CarNavigationStatusManager.EVENT_TYPE_NEXT_MANEUVER_INFO, bundle);
     }
 }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java
index 973815b..889a60a 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java
@@ -68,14 +68,14 @@
     }
 
     public void testSetUnsetListeners() throws Exception {
-        mManager.regsiterProjectionListener(
+        mManager.registerProjectionListener(
                 mListener, CarProjectionManager.PROJECTION_VOICE_SEARCH);
-        mManager.unregsiterProjectionListener();
+        mManager.unregisterProjectionListener();
     }
 
     public void testRegisterListenersHandleBadInput() throws Exception {
         try {
-            mManager.regsiterProjectionListener(null, CarProjectionManager.PROJECTION_VOICE_SEARCH);
+            mManager.registerProjectionListener(null, CarProjectionManager.PROJECTION_VOICE_SEARCH);
             fail();
         } catch (IllegalArgumentException e) {
             // expected.
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java b/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java
index 592a3f6..add6acc 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java
@@ -18,10 +18,12 @@
 
 import android.car.hardware.CarPropertyConfig;
 import android.graphics.Point;
+import android.test.suitebuilder.annotation.MediumTest;
 
 /**
  * Unit tests for {@link CarPropertyConfig}
  */
+@MediumTest
 public class CarPropertyConfigTest extends CarPropertyTestBase {
 
     public void testCarPropertyConfigBuilder() {
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java b/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
index 0613f5f..a8f3f90 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
@@ -18,10 +18,12 @@
 
 import android.car.hardware.CarPropertyValue;
 import android.graphics.Point;
+import android.test.suitebuilder.annotation.MediumTest;
 
 /**
  * Unit tests for {@link CarPropertyValue}
  */
+@MediumTest
 public class CarPropertyValueTest extends CarPropertyConfigTest {
 
     public void testSimpleFloatValue() {
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
index 5b2aa5a..1972c48 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
@@ -23,6 +23,7 @@
 import android.support.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
 import android.support.car.navigation.CarNavigationInstrumentCluster;
 import android.support.car.navigation.CarNavigationStatusManager;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -30,6 +31,7 @@
 /**
  * Unit tests for {@link android.support.car.navigation.CarNavigationStatusManager}
  */
+@MediumTest
 public class CarNavigationStatusManagerTest extends CarApiTestBase {
 
     private CarNavigationStatusManager mCarNavigationStatusManager;
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
index eb1e927..c60496b 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
@@ -22,6 +22,7 @@
 import android.support.car.hardware.CarSensorEvent;
 import android.support.car.hardware.CarSensorManager;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import java.lang.reflect.Field;
 import java.util.HashMap;
@@ -31,7 +32,7 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-
+@MediumTest
 public class CarSensorManagerTest extends AndroidTestCase {
     private static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
 
@@ -109,22 +110,24 @@
         }
         StringBuilder builder = new StringBuilder();
         boolean failed = false;
-        for (Field supprotCarSensorType : supportCarSensorTypes) {
+        for (Field supportCarSensorType : supportCarSensorTypes) {
             Field androidCarSensorType = androidCarSensorTypeToField.get(
-                    supprotCarSensorType.getInt(null));
-            assertNotNull("Sensor type:" + supprotCarSensorType.getName() +
+                    supportCarSensorType.getInt(null));
+            assertNotNull("Sensor type:" + supportCarSensorType.getName() +
                     " not defined in android.car", androidCarSensorType);
-            if (supprotCarSensorType.getName().equals(androidCarSensorType.getName())) {
+            if (supportCarSensorType.getName().equals(androidCarSensorType.getName())) {
                 // match ok
             } else if (androidCarSensorType.getName().startsWith("SENSOR_TYPE_RESERVED")) {
                 // not used in android.car, ok
+            } else if (supportCarSensorType.getName().startsWith("SENSOR_TYPE_RESERVED")) {
+                // used in android.car but reserved in support.car
             } else {
                 failed = true;
-                builder.append("android.support sensor has name:" + supprotCarSensorType.getName() +
+                builder.append("android.support sensor has name:" + supportCarSensorType.getName() +
                         " while android.car sensor has name:" + androidCarSensorType.getName() +
                         "\n");
             }
-            androidCarSensorTypeToField.remove(supprotCarSensorType.getInt(null));
+            androidCarSensorTypeToField.remove(supportCarSensorType.getInt(null));
         }
         assertFalse(builder.toString(), failed);
         assertTrue("android Car sensor has additional types defined:" + androidCarSensorTypeToField,
diff --git a/tests/carservice_test/Android.mk b/tests/carservice_test/Android.mk
index 105a73a..4be5354 100644
--- a/tests/carservice_test/Android.mk
+++ b/tests/carservice_test/Android.mk
@@ -40,7 +40,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES += car-service-lib-for-test \
                                vehicle-hal-support-lib \
                                car-systemtest \
-                               android-support-test
+                               android-support-test \
+                               android.hardware.automotive.vehicle-V2.0-java-static
 
 LOCAL_JAVA_LIBRARIES := android.car android.test.runner
 
diff --git a/tests/carservice_test/AndroidManifest.xml b/tests/carservice_test/AndroidManifest.xml
index 6f7ba34..6a1e2bf 100644
--- a/tests/carservice_test/AndroidManifest.xml
+++ b/tests/carservice_test/AndroidManifest.xml
@@ -43,9 +43,5 @@
             android:process="com.android.car.carservicetest.activityC"/>
         <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$BlockingActivity"
             android:taskAffinity="com.android.car.carservicetest.block"/>
-        <service android:name=".SimpleVmsPublisherClientService"
-                 android:exported="true"
-        />
-        <service android:name=".VmsPublisherClientMockService" android:exported="true" />
     </application>
 </manifest>
diff --git a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
index 6363589..7f4aeac 100644
--- a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
+++ b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
@@ -99,6 +99,7 @@
         int contexts = v.get(VehicleAudioRoutingPolicyIndex.CONTEXTS);
         // check if all contexts are allowed ones.
         assertTrue((contexts & ~(
+                VehicleAudioContextFlag.RINGTONE_FLAG |
                 VehicleAudioContextFlag.ALARM_FLAG |
                 VehicleAudioContextFlag.CALL_FLAG |
                 VehicleAudioContextFlag.MUSIC_FLAG |
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java b/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java
new file mode 100644
index 0000000..2ed2658
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.test;
+
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Validates that diagnostic constants in CarService and Vehicle HAL have the same value
+ * This is an important assumption to validate because we do not perform any mapping between
+ * the two layers, instead relying on the constants on both sides having identical values.
+ */
+@MediumTest
+public class CarDiagnosticConstantsTest extends TestCase {
+    static final String TAG = CarDiagnosticConstantsTest.class.getSimpleName();
+
+    static class MismatchException extends Exception {
+        private static String dumpClass(Class<?> clazz) {
+            StringBuilder builder = new StringBuilder(clazz.getName() + "{\n");
+            Arrays.stream(clazz.getFields()).forEach((Field field) -> {
+                builder.append('\t').append(field.toString()).append('\n');
+            });
+            return builder.append('}').toString();
+        }
+
+        private static void logClasses(Class<?> clazz1, Class<?> clazz2) {
+            Log.d(TAG, "MismatchException. class1: " + dumpClass(clazz1));
+            Log.d(TAG, "MismatchException. class2: " + dumpClass(clazz2));
+        }
+
+        MismatchException(String message) {
+            super(message);
+        }
+
+        static MismatchException fieldValueMismatch(Class<?> clazz1, Class<?> clazz2, String name,
+                int value1, int value2) {
+            logClasses(clazz1, clazz2);
+            return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
+                " field " + name  + " had different values " + value1 + " vs. " + value2);
+        }
+
+        static MismatchException fieldsOnlyInClass1(Class<?> clazz1, Class<?> clazz2,
+                Map<String, Integer> fields) {
+            logClasses(clazz1, clazz2);
+            return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
+                " some fields were only found in the first class:\n" +
+                fields.keySet().stream().reduce("",
+                    (String s, String t) -> s + "\n" + t));
+        }
+
+        static MismatchException fieldOnlyInClass2(Class<?> clazz1, Class<?> clazz2, String field) {
+            logClasses(clazz1, clazz2);
+            return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
+                " field " + field + " was not found in both classes");
+        }
+    }
+
+    static boolean isPublicStaticFinalInt(Field field) {
+        final int modifiers = field.getModifiers();
+        final boolean isPublic = (modifiers & Modifier.PUBLIC) == Modifier.PUBLIC;
+        final boolean isStatic = (modifiers & Modifier.STATIC) == Modifier.STATIC;
+        final boolean isFinal = (modifiers & Modifier.FINAL) == Modifier.FINAL;
+        if (isPublic && isStatic && isFinal) {
+            return field.getType() == int.class;
+        }
+        return false;
+    }
+
+    static void validateMatch(Class<?> clazz1, Class<?> clazz2) throws Exception {
+        Map<String, Integer> fields = new HashMap<>();
+
+        // add all the fields in the first class to a map
+        Arrays.stream(clazz1.getFields()).filter(
+            CarDiagnosticConstantsTest::isPublicStaticFinalInt).forEach( (Field field) -> {
+                final String name = field.getName();
+                try {
+                    fields.put(name, field.getInt(null));
+                } catch (IllegalAccessException e) {
+                    // this will practically never happen because we checked that it is a
+                    // public static final field before reading from it
+                    Log.wtf(TAG, String.format("attempt to access field %s threw exception",
+                        field.toString()), e);
+                }
+            });
+
+        // check for all fields in the second class, and remove matches from the map
+        for (Field field2 : clazz2.getFields()) {
+            if (isPublicStaticFinalInt(field2)) {
+                final String name = field2.getName();
+                if (fields.containsKey(name)) {
+                    try {
+                        final int value2 = field2.getInt(null);
+                        final int value1 = fields.getOrDefault(name, value2+1);
+                        if (value2 != value1) {
+                            throw MismatchException.fieldValueMismatch(clazz1, clazz2,
+                                field2.getName(), value1, value2);
+                        }
+                        fields.remove(name);
+                    } catch (IllegalAccessException e) {
+                        // this will practically never happen because we checked that it is a
+                        // public static final field before reading from it
+                        Log.wtf(TAG, String.format("attempt to access field %s threw exception",
+                            field2.toString()), e);
+                        throw e;
+                    }
+                } else {
+                    throw MismatchException.fieldOnlyInClass2(clazz1, clazz2, name);
+                }
+            }
+        }
+
+        // if anything is left, we didn't find some fields in the second class
+        if (!fields.isEmpty()) {
+            throw MismatchException.fieldsOnlyInClass1(clazz1, clazz2, fields);
+        }
+    }
+
+    public void testFuelSystemStatus() throws Exception {
+        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelSystemStatus.class,
+            android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.class);
+    }
+
+    public void testFuelType() throws Exception {
+        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelType.class,
+            android.car.diagnostic.CarDiagnosticEvent.FuelType.class);
+    }
+
+    public void testSecondaryAirStatus() throws Exception {
+        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SecondaryAirStatus.class,
+            android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.class);
+    }
+
+    public void testIgnitionMonitors() throws Exception {
+        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CommonIgnitionMonitors.class,
+            android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.class);
+
+        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CompressionIgnitionMonitors.class,
+            android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.class);
+
+        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SparkIgnitionMonitors.class,
+            android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.class);
+    }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
index d316ba6..b77a844 100644
--- a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
@@ -19,24 +19,23 @@
 import static java.lang.Integer.toHexString;
 
 import android.car.Car;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticEvent.FuelSystemStatus;
-import android.car.hardware.CarDiagnosticEvent.FuelType;
-import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.CommonIgnitionMonitors;
-import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.CompressionIgnitionMonitors;
-import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.SparkIgnitionMonitors;
-import android.car.hardware.CarDiagnosticEvent.SecondaryAirStatus;
-import android.car.hardware.CarDiagnosticManager;
-import android.car.hardware.CarDiagnosticSensorIndices.Obd2FloatSensorIndex;
-import android.car.hardware.CarDiagnosticSensorIndices.Obd2IntegerSensorIndex;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus;
+import android.car.diagnostic.CarDiagnosticEvent.FuelType;
+import android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors;
+import android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors;
+import android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors;
+import android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.diagnostic.FloatSensorIndex;
+import android.car.diagnostic.IntegerSensorIndex;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.SystemClock;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.JsonReader;
 import android.util.JsonWriter;
 import android.util.Log;
-import com.android.car.internal.FeatureConfiguration;
 import com.android.car.vehiclehal.DiagnosticEventBuilder;
 import com.android.car.vehiclehal.DiagnosticJson;
 import com.android.car.vehiclehal.VehiclePropValueBuilder;
@@ -205,23 +204,23 @@
 
     @Override
     protected void setUp() throws Exception {
-        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
+        mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
         mLiveFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS,
+                IntegerSensorIndex.FUEL_SYSTEM_STATUS,
                 FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION);
         mLiveFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
-        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
-        mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
-        mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED, 12.5f);
+                IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
+        mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
+        mLiveFrameEventBuilder.addFloatSensor(FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
+        mLiveFrameEventBuilder.addFloatSensor(FloatSensorIndex.VEHICLE_SPEED, 12.5f);
 
-        mFreezeFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
+        mFreezeFrameEventBuilder.addIntSensor(IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
         mFreezeFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
-        mFreezeFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
+                IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
+        mFreezeFrameEventBuilder.addIntSensor(IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
         mFreezeFrameEventBuilder.addFloatSensor(
-                Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
-        mFreezeFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED, 12.5f);
+                FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
+        mFreezeFrameEventBuilder.addFloatSensor(FloatSensorIndex.VEHICLE_SPEED, 12.5f);
         mFreezeFrameEventBuilder.setDTC(DTC);
 
         super.setUp();
@@ -242,26 +241,26 @@
         assertEquals(
                 5000,
                 liveFrame
-                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+                        .getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
                         .intValue());
         assertEquals(
                 30,
                 liveFrame
-                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE)
+                        .getSystemIntegerSensor(IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE)
                         .intValue());
         assertEquals(
                 2,
                 liveFrame
-                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE)
+                        .getSystemIntegerSensor(IntegerSensorIndex.CONTROL_MODULE_VOLTAGE)
                         .intValue());
         assertEquals(
                 0.125f,
                 liveFrame
-                        .getSystemFloatSensor(Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD)
+                        .getSystemFloatSensor(FloatSensorIndex.CALCULATED_ENGINE_LOAD)
                         .floatValue());
         assertEquals(
                 12.5f,
-                liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED).floatValue());
+                liveFrame.getSystemFloatSensor(FloatSensorIndex.VEHICLE_SPEED).floatValue());
     }
 
     public void testLiveFrameEvent() throws Exception {
@@ -274,7 +273,7 @@
         listener.reset();
         long time = SystemClock.elapsedRealtimeNanos();
         mLiveFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5100);
+                IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5100);
 
         getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(time));
         assertTrue(listener.waitForEvent(time));
@@ -284,7 +283,7 @@
         assertEquals(
                 5100,
                 liveFrame
-                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+                        .getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
                         .intValue());
     }
 
@@ -303,23 +302,23 @@
 
         assertNull(
                 liveFrame.getSystemIntegerSensor(
-                        Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE));
+                        IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE));
         assertEquals(
                 -1,
                 liveFrame.getSystemIntegerSensor(
-                        Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE, -1));
+                        IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE, -1));
 
-        assertNull(liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE));
+        assertNull(liveFrame.getSystemFloatSensor(FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE));
         assertEquals(
                 0.25f,
-                liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE, 0.25f));
+                liveFrame.getSystemFloatSensor(FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE, 0.25f));
 
-        assertNull(liveFrame.getVendorIntegerSensor(Obd2IntegerSensorIndex.VENDOR_START));
-        assertEquals(-1, liveFrame.getVendorIntegerSensor(Obd2IntegerSensorIndex.VENDOR_START, -1));
+        assertNull(liveFrame.getVendorIntegerSensor(IntegerSensorIndex.VENDOR_START));
+        assertEquals(-1, liveFrame.getVendorIntegerSensor(IntegerSensorIndex.VENDOR_START, -1));
 
-        assertNull(liveFrame.getVendorFloatSensor(Obd2FloatSensorIndex.VENDOR_START));
+        assertNull(liveFrame.getVendorFloatSensor(FloatSensorIndex.VENDOR_START));
         assertEquals(
-                0.25f, liveFrame.getVendorFloatSensor(Obd2FloatSensorIndex.VENDOR_START, 0.25f));
+                0.25f, liveFrame.getVendorFloatSensor(FloatSensorIndex.VENDOR_START, 0.25f));
     }
 
     public void testFuelSystemStatus() throws Exception {
@@ -338,7 +337,7 @@
         assertEquals(
                 FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION,
                 liveFrame
-                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS)
+                        .getSystemIntegerSensor(IntegerSensorIndex.FUEL_SYSTEM_STATUS)
                         .intValue());
         assertEquals(
                 FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION,
@@ -353,7 +352,7 @@
                 android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
 
         mLiveFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
+                IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
                 SecondaryAirStatus.FROM_OUTSIDE_OR_OFF);
         long timestamp = SystemClock.elapsedRealtimeNanos();
         getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -367,7 +366,7 @@
                 SecondaryAirStatus.FROM_OUTSIDE_OR_OFF,
                 liveFrame
                         .getSystemIntegerSensor(
-                                Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS)
+                                IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS)
                         .intValue());
         assertEquals(
                 SecondaryAirStatus.FROM_OUTSIDE_OR_OFF,
@@ -388,9 +387,9 @@
         final int compressionMonitorsValue =
                 (0x1 << 2) | (0x1 << 3) | (0x1 << 6) | (0x1 << 12) | (0x1 << 13);
 
-        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 0);
+        mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 0);
         mLiveFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, sparkMonitorsValue);
+                IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, sparkMonitorsValue);
 
         long timestamp = SystemClock.elapsedRealtimeNanos();
         getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -431,9 +430,9 @@
         assertFalse(sparkIgnitionMonitors.catalyst.available);
         assertFalse(sparkIgnitionMonitors.catalyst.incomplete);
 
-        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 1);
+        mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 1);
         mLiveFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, compressionMonitorsValue);
+                IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, compressionMonitorsValue);
 
         timestamp += 1000;
         getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -479,7 +478,7 @@
                 android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
 
         mLiveFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.FUEL_TYPE, FuelType.BIFUEL_RUNNING_LPG);
+                IntegerSensorIndex.FUEL_TYPE, FuelType.BIFUEL_RUNNING_LPG);
         long timestamp = SystemClock.elapsedRealtimeNanos();
         getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
 
@@ -490,7 +489,7 @@
 
         assertEquals(
                 FuelType.BIFUEL_RUNNING_LPG,
-                liveFrame.getSystemIntegerSensor(Obd2IntegerSensorIndex.FUEL_TYPE).intValue());
+                liveFrame.getSystemIntegerSensor(IntegerSensorIndex.FUEL_TYPE).intValue());
         assertEquals(FuelType.BIFUEL_RUNNING_LPG, liveFrame.getFuelType().intValue());
     }
 
@@ -501,8 +500,8 @@
                 CarDiagnosticManager.FRAME_TYPE_LIVE,
                 android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
 
-        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE, 74);
-        mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE, 0.125f);
+        mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.ENGINE_OIL_TEMPERATURE, 74);
+        mLiveFrameEventBuilder.addFloatSensor(FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE, 0.125f);
 
         long timestamp = SystemClock.elapsedRealtimeNanos();
         getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -515,11 +514,11 @@
         assertEquals(
                 74,
                 liveFrame
-                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
+                        .getSystemIntegerSensor(IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
                         .intValue());
         assertEquals(
                 0.125f,
-                liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
+                liveFrame.getSystemFloatSensor(FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
 
         StringWriter stringWriter = new StringWriter();
         JsonWriter jsonWriter = new JsonWriter(stringWriter);
@@ -535,11 +534,11 @@
                 74,
                 diagnosticJson
                         .intValues
-                        .get(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
+                        .get(IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
                         .intValue());
         assertEquals(
                 0.125f,
-                diagnosticJson.floatValues.get(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
+                diagnosticJson.floatValues.get(FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
     }
 
     public void testMultipleListeners() throws Exception {
@@ -576,11 +575,11 @@
 
         assertEquals(
                 5000,
-                event1.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+                event1.getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
                         .intValue());
         assertEquals(
                 5000,
-                event2.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+                event2.getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
                         .intValue());
 
         listener1.reset();
@@ -602,7 +601,7 @@
 
         assertEquals(
                 5000,
-                event2.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+                event2.getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
                         .intValue());
     }
 
@@ -624,7 +623,7 @@
         assertEquals(DTC, freezeFrame.dtc);
 
         mFreezeFrameEventBuilder.addIntSensor(
-                Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE, 22);
+                IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE, 22);
         injectedEvent = mFreezeFrameProperties.addNewEvent(mFreezeFrameEventBuilder);
         getMockedVehicleHal().injectEvent(injectedEvent);
         assertTrue(listener.waitForEvent(injectedEvent.timestamp));
@@ -640,7 +639,7 @@
         assertEquals(
                 22,
                 freezeFrame
-                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE)
+                        .getSystemIntegerSensor(IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE)
                         .intValue());
     }
 
@@ -730,9 +729,9 @@
 
     public void testIsSupportedApiCalls() throws Exception {
         assertTrue(mCarDiagnosticManager.isLiveFrameSupported());
-        assertTrue(mCarDiagnosticManager.isFreezeFrameSupported());
-        assertTrue(mCarDiagnosticManager.isFreezeFrameTimestampSupported());
-        assertTrue(mCarDiagnosticManager.isFreezeFrameClearSupported());
+        assertTrue(mCarDiagnosticManager.isFreezeFrameNotificationSupported());
+        assertTrue(mCarDiagnosticManager.isGetFreezeFrameSupported());
+        assertTrue(mCarDiagnosticManager.isClearFreezeFramesSupported());
     }
 
     class Listener implements CarDiagnosticManager.OnDiagnosticEventListener {
diff --git a/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
index c3f6857..2cdbbb0 100644
--- a/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
@@ -18,9 +18,11 @@
 import android.car.Car;
 import android.car.CarInfoManager;
 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.car.vehiclehal.VehiclePropValueBuilder;
 
+@MediumTest
 public class CarInfoManagerTest extends MockedCarTestBase {
     private static final String MAKE_NAME = "ANDROID";
 
diff --git a/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
index 64f5fb8..adf288c 100644
--- a/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
@@ -68,7 +68,7 @@
     }
 
     public void testShortPressListener() throws Exception {
-        mManager.regsiterProjectionListener(
+        mManager.registerProjectionListener(
                 mListener,
                 CarProjectionManager.PROJECTION_VOICE_SEARCH);
         assertEquals(0, mAvailable.availablePermits());
@@ -79,7 +79,7 @@
     }
 
     public void testLongPressListener() throws Exception {
-        mManager.regsiterProjectionListener(
+        mManager.registerProjectionListener(
                 mListener,
                 CarProjectionManager.PROJECTION_LONG_PRESS_VOICE_SEARCH);
         assertEquals(0, mLongAvailable.availablePermits());
@@ -90,7 +90,7 @@
     }
 
     public void testMixedPressListener() throws Exception {
-        mManager.regsiterProjectionListener(
+        mManager.registerProjectionListener(
                 mListener,
                 CarProjectionManager.PROJECTION_LONG_PRESS_VOICE_SEARCH
                 | CarProjectionManager.PROJECTION_VOICE_SEARCH);
diff --git a/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
index ec367b8..fa33e56 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
@@ -29,6 +29,7 @@
 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
 import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -44,6 +45,7 @@
 /**
  * Tests for {@link CarVendorExtensionManager}
  */
+@MediumTest
 public class CarVendorExtensionManagerTest extends MockedCarTestBase {
 
     private static final String TAG = CarVendorExtensionManager.class.getSimpleName();
@@ -159,7 +161,7 @@
 
     public void testLargeByteArrayProperty() throws Exception {
         // Allocate array of byte which is greater than binder transaction buffer limitation.
-        byte[] expectedData = new byte[3 * MILLION];
+        byte[] expectedData = new byte[2 * MILLION];
 
         new Random(SystemClock.elapsedRealtimeNanos())
             .nextBytes(expectedData);
@@ -252,7 +254,8 @@
             result.areaId = requestedPropValue.areaId;
 
             if (requestedPropValue.prop == CUSTOM_BYTES_PROP_ID_2 && mBytes != null) {
-                Log.d(TAG, "Returning byte array property, value: " + Arrays.toString(mBytes));
+                Log.d(TAG, "Returning byte array property, value size " + mBytes.length);
+                result.value.bytes.ensureCapacity(mBytes.length);
                 for (byte b : mBytes) {
                     result.value.bytes.add(b);
                 }
diff --git a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
index df2b532..2e92a59 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
@@ -17,12 +17,11 @@
 
 import static com.android.car.test.AudioTestUtils.doRequestFocus;
 
-import com.google.android.collect.Lists;
-
 import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.media.CarAudioManager;
 import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
 import android.hardware.automotive.vehicle.V2_0.VehicleAudioExtFocusFlag;
 import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusState;
 import android.hardware.automotive.vehicle.V2_0.VehicleAudioVolumeIndex;
@@ -35,19 +34,23 @@
 import android.media.IVolumeController;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseIntArray;
 import android.view.KeyEvent;
 
+import com.android.car.VolumeUtils;
 import com.android.car.vehiclehal.VehiclePropValueBuilder;
 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
 import com.android.internal.annotations.GuardedBy;
 
+import com.google.android.collect.Lists;
+
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
+@MediumTest
 public class CarVolumeServiceTest extends MockedCarTestBase {
     private static final String TAG = CarVolumeServiceTest.class.getSimpleName();
 
@@ -79,24 +82,65 @@
                 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
             }
         });
+    }
 
+    private SingleChannelVolumeHandler setupExternalVolumeEmulation(boolean supportAudioContext)
+            throws Exception {
         List<Integer> maxs = new ArrayList<>();
-        maxs.add(MAX_VOL);
-        maxs.add(MAX_VOL);
-
-        // TODO: add tests for audio context supported cases.
-        startVolumeEmulation(0 /*supported audio context*/, maxs);
+        int supportedAudioContext = 0;
+        if (!supportAudioContext) {
+            // set up 2 physical streams
+            maxs.add(MAX_VOL);
+            maxs.add(MAX_VOL);
+        } else {
+            // add supported contexts
+            int[] contexts = VolumeUtils.CAR_AUDIO_CONTEXT;
+            for (int context : contexts) {
+                supportedAudioContext |= context;
+                maxs.add(MAX_VOL);
+            }
+        }
+        SingleChannelVolumeHandler handler =
+                startVolumeEmulation(supportedAudioContext, maxs);
         mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
+        return handler;
+    }
+
+    public void testUnknownVolumeChange() throws Exception {
+        SingleChannelVolumeHandler volumeHandler = setupExternalVolumeEmulation(true);
+        VolumeController volumeController = new VolumeController();
+        mCarAudioManager.setVolumeController(volumeController);
+        mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 2, 0);
+        // give focus to music, now current context becomes VehicleAudioContextFlag.MUSIC_FLAG
+        CarAudioFocusTest.AudioFocusListener listenerMusic =
+                new CarAudioFocusTest.AudioFocusListener();
+        int res = doRequestFocus(mAudioManager, listenerMusic,
+                AudioManager.STREAM_MUSIC,
+                AudioManager.AUDIOFOCUS_GAIN);
+        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+        mAudioFocusPropertyHandler.sendAudioFocusState(
+                VehicleAudioFocusState.STATE_GAIN,
+                request[1],
+                VehicleAudioExtFocusFlag.NONE_FLAG);
+
+        // let vehicle hal report volume change from unknown context, we should map it to the
+        // current context (music).
+        volumeHandler.injectVolumeEvent(VehicleAudioContextFlag.UNKNOWN_FLAG, 3);
+        // now music volume should be recorded as 3.
+        volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, 3));
     }
 
     public void testVolumeLimits() throws Exception {
+        setupExternalVolumeEmulation(false);
         for (int stream : LOGICAL_STREAMS) {
             assertEquals(MAX_VOL, mCarAudioManager.getStreamMaxVolume(stream));
         }
     }
 
-    public void testVolumeSet() {
+    public void testVolumeSet() throws Exception {
         try {
+            setupExternalVolumeEmulation(false);
             int callVol = 10;
             int musicVol = 15;
             mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0);
@@ -117,6 +161,7 @@
 
     public void testSuppressVolumeUI() {
         try {
+            setupExternalVolumeEmulation(false);
             VolumeController volumeController = new VolumeController();
             mCarAudioManager.setVolumeController(volumeController);
 
@@ -164,6 +209,7 @@
 
     public void testVolumeKeys() throws Exception {
         try {
+            setupExternalVolumeEmulation(false);
             int musicVol = 10;
             mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0);
             int callVol = 12;
@@ -270,12 +316,21 @@
             }
         }
 
+        public void injectVolumeEvent(int context, int volume) {
+            getMockedVehicleHal().injectEvent(
+                    VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
+                            .setTimestamp(SystemClock.elapsedRealtimeNanos())
+                            .addIntValue(context, volume, 0)
+                            .build());
+        }
+
         @Override
         public void onPropertySet(VehiclePropValue value) {
             ArrayList<Integer> v = value.value.int32Values;
-            int stream = v.get(VehicleAudioVolumeIndex.INDEX_STREAM);
-            int volume = v.get(VehicleAudioVolumeIndex.INDEX_VOLUME);
-            int state = v.get(VehicleAudioVolumeIndex.INDEX_STATE);
+            int stream = v.get(VehicleAudioVolumeIndex.STREAM);
+            int volume = v.get(VehicleAudioVolumeIndex.VOLUME);
+            int state = v.get(VehicleAudioVolumeIndex.STATE);
+            Log.d(TAG, "state " + state);
 
             mCurrent.put(stream, volume);
             getMockedVehicleHal().injectEvent(
@@ -287,7 +342,7 @@
 
         @Override
         public VehiclePropValue onPropertyGet(VehiclePropValue value) {
-            int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.INDEX_STREAM);
+            int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.STREAM);
             int volume = mCurrent.get(stream);
             return VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
                     .setTimestamp(SystemClock.elapsedRealtimeNanos())
@@ -356,7 +411,8 @@
                 }
             };
 
-    private void startVolumeEmulation(int supportedAudioVolumeContext, List<Integer> maxs) {
+    private SingleChannelVolumeHandler startVolumeEmulation(int supportedAudioVolumeContext,
+            List<Integer> maxs) {
         SingleChannelVolumeHandler singleChannelVolumeHandler =
                 new SingleChannelVolumeHandler(maxs);
         int zones = (1<<maxs.size()) - 1;
@@ -388,6 +444,7 @@
                 .setConfigArray(Lists.newArrayList(0));
 
         reinitializeMockedHal();
+        return singleChannelVolumeHandler;
     }
 
     private void sendVolumeKey(boolean volUp) {
diff --git a/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java b/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java
deleted file mode 100644
index c8badf2..0000000
--- a/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.test;
-
-import android.car.annotation.FutureFeature;
-import android.car.vms.VmsPublisherClientService;
-import android.car.vms.VmsSubscriptionState;
-
-/**
- * This service is launched during the tests in VmsPublisherClientServiceTest.
- */
-@FutureFeature
-public class SimpleVmsPublisherClientService extends VmsPublisherClientService {
-    @Override
-    public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
-
-    }
-
-    @Override
-    public void onVmsPublisherServiceReady() {
-        // Publish a property that is going to be verified in the test.
-        publish(VmsPublisherClientServiceTest.MOCK_PUBLISHER_LAYER,
-                VmsPublisherClientServiceTest.PAYLOAD);
-    }
-}
diff --git a/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java b/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
index 90598eb..3345605 100644
--- a/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
@@ -23,6 +23,7 @@
 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
 import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.car.SystemActivityMonitoringService;
 import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
@@ -34,6 +35,7 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
+@MediumTest
 public class SystemActivityMonitoringServiceTest extends MockedCarTestBase {
     private static final long TIMEOUT_MS = 3000;
     private static final long POLL_INTERVAL_MS = 50;
@@ -52,6 +54,11 @@
         // blocking activity.
         mDrivingStatusHandler.setDrivingStatusRestricted(drivingStatusRestricted);
 
+        // Due to asynchronous nature of Car Service initialization, if we won't wait we may inject
+        // an event while SensorHalService is not subscribed yet.
+        assertTrue(getMockedVehicleHal()
+                .waitForSubscriber(VehicleProperty.DRIVING_STATUS, TIMEOUT_MS));
+
         VehiclePropValue injectValue =
                 VehiclePropValueBuilder.newBuilder(VehicleProperty.DRIVING_STATUS)
                         .setTimestamp(SystemClock.elapsedRealtimeNanos())
diff --git a/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java b/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java
deleted file mode 100644
index 240598a..0000000
--- a/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.test;
-
-import static org.junit.Assume.assumeTrue;
-
-import android.car.VehicleAreaType;
-import android.car.annotation.FutureFeature;
-import android.car.vms.VmsLayer;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-import android.hardware.automotive.vehicle.V2_1.VmsSubscriptionResponseFormat;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.car.vehiclehal.VehiclePropValueBuilder;
-import com.android.car.vehiclehal.test.MockedVehicleHal;
-import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@FutureFeature
-@MediumTest
-public class VmsHalServiceSubscriptionEventTest extends MockedCarTestBase {
-    private static final String TAG = "VmsHalServiceTest";
-
-    private HalHandler mHalHandler;
-    private MockedVehicleHal mHal;
-    // Used to block until the HAL property is updated in HalHandler.onPropertySet.
-    private Semaphore mHalHandlerSemaphore;
-
-    @Override
-    protected synchronized void configureMockedHal() {
-        mHalHandler = new HalHandler();
-        addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
-                .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
-                .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.setUp();
-        mHal = getMockedVehicleHal();
-        mHalHandlerSemaphore = new Semaphore(0);
-    }
-
-    @Override
-    protected synchronized void tearDown() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.tearDown();
-    }
-
-    public void testEmptySubscriptions() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        List<VmsLayer> layers = new ArrayList<>();
-        subscriptionTestLogic(layers);
-    }
-
-    public void testOneSubscription() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        List<VmsLayer> layers = Arrays.asList(new VmsLayer(8, 3));
-        subscriptionTestLogic(layers);
-    }
-
-    public void testManySubscriptions() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        List<VmsLayer> layers = Arrays.asList(
-                new VmsLayer(8, 3),
-                new VmsLayer(5, 1),
-                new VmsLayer(3, 9),
-                new VmsLayer(2, 7),
-                new VmsLayer(9, 3));
-        subscriptionTestLogic(layers);
-    }
-
-    /**
-     * First, it subscribes to the given layers. Then it validates that a subscription request
-     * responds with the same layers.
-     */
-    private void subscriptionTestLogic(List<VmsLayer> layers) throws Exception {
-        for (VmsLayer layer : layers) {
-            subscribeViaHal(layer);
-        }
-        // Send subscription request.
-        mHal.injectEvent(createHalSubscriptionRequest());
-        // Wait for response.
-        assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        // Validate response.
-        ArrayList<Integer> v = mHalHandler.getValues();
-        int messageType = v.get(VmsSubscriptionResponseFormat.VMS_MESSAGE_TYPE);
-        int sequenceNumber = v.get(VmsSubscriptionResponseFormat.SEQUENCE_NUMBER);
-        int numberLayers = v.get(VmsSubscriptionResponseFormat.NUMBER_OF_LAYERS);
-        assertEquals(VmsMessageType.SUBSCRIPTION_RESPONSE, messageType);
-        assertEquals(layers.size(), sequenceNumber);
-        assertEquals(layers.size(), numberLayers);
-        List<VmsLayer> receivedLayers = new ArrayList<>();
-        int start = VmsSubscriptionResponseFormat.FIRST_LAYER;
-        int end = VmsSubscriptionResponseFormat.FIRST_LAYER + 2 * numberLayers;
-        while (start < end) {
-            int id = v.get(start++);
-            int version = v.get(start++);
-            receivedLayers.add(new VmsLayer(id, version));
-        }
-        assertEquals(new HashSet<>(layers), new HashSet<>(receivedLayers));
-    }
-
-    /**
-     * Subscribes to a layer, waits for the event to propagate back to the HAL layer and validates
-     * the propagated message.
-     */
-    private void subscribeViaHal(VmsLayer layer) throws Exception {
-        // Send subscribe request.
-        mHal.injectEvent(createHalSubscribeRequest(layer));
-        // Wait for response.
-        assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        // Validate response.
-        ArrayList<Integer> v = mHalHandler.getValues();
-        int messsageType = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
-        int layerId = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
-        int layerVersion = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
-        assertEquals(VmsMessageType.SUBSCRIBE, messsageType);
-        assertEquals(layer.getId(), layerId);
-        assertEquals(layer.getVersion(), layerVersion);
-    }
-
-    private VehiclePropValue createHalSubscribeRequest(VmsLayer layer) {
-        return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .addIntValue(VmsMessageType.SUBSCRIBE)
-                .addIntValue(layer.getId())
-                .addIntValue(layer.getVersion())
-                .build();
-    }
-
-    private VehiclePropValue createHalSubscriptionRequest() {
-        return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .addIntValue(VmsMessageType.SUBSCRIPTION_REQUEST)
-                .build();
-    }
-
-    private class HalHandler implements VehicleHalPropertyHandler {
-        private ArrayList<Integer> mValues;
-
-        @Override
-        public synchronized void onPropertySet(VehiclePropValue value) {
-            mValues = value.value.int32Values;
-            mHalHandlerSemaphore.release();
-        }
-
-        public ArrayList<Integer> getValues() {
-            return mValues;
-        }
-    }
-}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
deleted file mode 100644
index d8e344b..0000000
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.test;
-
-import android.car.annotation.FutureFeature;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsLayerDependency;
-import android.car.vms.VmsLayersOffering;
-import android.car.vms.VmsPublisherClientService;
-import android.car.vms.VmsSubscriptionState;
-import android.util.Log;
-import java.util.List;
-import java.util.ArrayList;
-
-/**
- * This service is launched during the tests in VmsPublisherSubscriberTest. It publishes a property
- * that is going to be verified in the test.
- *
- * Note that the subscriber can subscribe before the publisher finishes initialization. To cover
- * both potential scenarios, this service publishes the test message in onVmsSubscriptionChange
- * and in onVmsPublisherServiceReady. See comments below.
- */
-@FutureFeature
-public class VmsPublisherClientMockService extends VmsPublisherClientService {
-    private static final String TAG = "VmsPublisherClientMockService";
-
-    @Override
-    public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
-        // Case when the publisher finished initialization before the subscription request.
-        initializeMockPublisher(subscriptionState);
-    }
-
-    @Override
-    public void onVmsPublisherServiceReady() {
-        // Case when the subscription request was sent before the publisher was ready.
-        VmsSubscriptionState subscriptionState = getSubscriptions();
-        initializeMockPublisher(subscriptionState);
-    }
-
-    private void initializeMockPublisher(VmsSubscriptionState subscriptionState) {
-        Log.d(TAG, "Initializing Mock publisher");
-        getPublisherStaticId(VmsPublisherSubscriberTest.PAYLOAD);
-        publishIfNeeded(subscriptionState);
-        declareOffering(subscriptionState);
-    }
-
-    private void publishIfNeeded(VmsSubscriptionState subscriptionState) {
-        for (VmsLayer layer : subscriptionState.getLayers()) {
-            if (layer.equals(VmsPublisherSubscriberTest.LAYER)) {
-                publish(VmsPublisherSubscriberTest.LAYER, VmsPublisherSubscriberTest.PAYLOAD);
-            }
-        }
-    }
-
-    private void declareOffering(VmsSubscriptionState subscriptionState) {
-        List<VmsLayerDependency> dependencies = new ArrayList<>();
-        for( VmsLayer layer : subscriptionState.getLayers()) {
-            dependencies.add(new VmsLayerDependency(layer));
-        }
-        VmsLayersOffering offering = new VmsLayersOffering(dependencies);
-        setLayersOffering(offering);
-    }
-}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
deleted file mode 100644
index c22f63a..0000000
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.test;
-
-import android.annotation.ArrayRes;
-import android.car.VehicleAreaType;
-import android.car.annotation.FutureFeature;
-import android.car.vms.VmsLayer;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import com.android.car.R;
-import com.android.car.vehiclehal.VehiclePropValueBuilder;
-import com.android.car.vehiclehal.test.MockedVehicleHal;
-import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@FutureFeature
-@MediumTest
-public class VmsPublisherClientServiceTest extends MockedCarTestBase {
-    private static final String TAG = "VmsPublisherTest";
-    private static final int MOCK_PUBLISHER_LAYER_ID = 12;
-    private static final int MOCK_PUBLISHER_LAYER_VERSION = 34;
-    public static final VmsLayer MOCK_PUBLISHER_LAYER = new VmsLayer(MOCK_PUBLISHER_LAYER_ID,
-            MOCK_PUBLISHER_LAYER_VERSION);
-    public static final byte[] PAYLOAD = new byte[]{1, 1, 2, 3, 5, 8, 13};
-
-    private HalHandler mHalHandler;
-    // Used to block until the HAL property is updated in HalHandler.onPropertySet.
-    private Semaphore mHalHandlerSemaphore;
-
-    @Override
-    protected synchronized void configureMockedHal() {
-        mHalHandler = new HalHandler();
-        addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
-                .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
-                .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
-    }
-
-    /**
-     * Creates a context with the resource vmsPublisherClients overridden. The overridden value
-     * contains the name of the test service defined also in this test package.
-     */
-    @Override
-    protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
-        Context context = getContext()
-                .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
-        Resources resources = new Resources(context.getAssets(),
-                context.getResources().getDisplayMetrics(),
-                context.getResources().getConfiguration()) {
-            @Override
-            public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
-                if (id == R.array.vmsPublisherClients) {
-                    return new String[]{"com.android.car.test/.SimpleVmsPublisherClientService"};
-                }
-                return super.getStringArray(id);
-            }
-        };
-        ContextWrapper wrapper = new ContextWrapper(context) {
-            @Override
-            public Resources getResources() {
-                return resources;
-            }
-        };
-        return wrapper;
-    }
-
-    private VehiclePropValue getHalSubscriptionRequest() {
-        return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-            .addIntValue(VmsMessageType.SUBSCRIBE)
-            .addIntValue(MOCK_PUBLISHER_LAYER_ID)
-            .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
-            .build();
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        /**
-         * First init the semaphore, setUp will start a series of events that will ultimately
-         * update the HAL layer and release this semaphore.
-         */
-        mHalHandlerSemaphore = new Semaphore(0);
-        super.setUp();
-
-        // Inject a subscribe event which simulates the HAL is subscribed to the Mock Publisher.
-        MockedVehicleHal mHal = getMockedVehicleHal();
-        mHal.injectEvent(getHalSubscriptionRequest());
-    }
-
-    @Override
-    protected synchronized void tearDown() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.tearDown();
-    }
-
-    /**
-     * The method setUp initializes all the Car services, including the VmsPublisherService.
-     * The VmsPublisherService will start and configure its list of clients. This list was
-     * overridden in the method getCarServiceContext.
-     * Therefore, only SimpleVmsPublisherClientService will be started.
-     * The service SimpleVmsPublisherClientService will publish one message, which is validated in
-     * this test.
-     */
-    public void testPublish() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        //TODO: This test is using minial synchronisation between clients.
-        //      If more complexity is added this may result in publisher
-        //      publishing before the subscriber subscribed, in which case
-        //      the semaphore will not be released.
-        assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        VehiclePropValue.RawValue rawValue = mHalHandler.getValue().value;
-        int messageType = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
-        int layerId = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
-        int layerVersion = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
-        byte[] payload = new byte[rawValue.bytes.size()];
-        for (int i = 0; i < rawValue.bytes.size(); ++i) {
-            payload[i] = rawValue.bytes.get(i);
-        }
-        assertEquals(VmsMessageType.DATA, messageType);
-        assertEquals(MOCK_PUBLISHER_LAYER_ID, layerId);
-        assertEquals(MOCK_PUBLISHER_LAYER_VERSION, layerVersion);
-        assertTrue(Arrays.equals(PAYLOAD, payload));
-    }
-
-    private class HalHandler implements VehicleHalPropertyHandler {
-        private VehiclePropValue mValue;
-
-        @Override
-        public synchronized void onPropertySet(VehiclePropValue value) {
-            mValue = value;
-
-            // If this is the data message release the semaphone so the test can continue.
-            ArrayList<Integer> int32Values = value.value.int32Values;
-            if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
-                    VmsMessageType.DATA) {
-                mHalHandlerSemaphore.release();
-            }
-        }
-
-        @Override
-        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
-            return mValue != null ? mValue : value;
-        }
-
-        @Override
-        public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
-            Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
-        }
-
-        @Override
-        public synchronized void onPropertyUnsubscribe(int property) {
-            Log.d(TAG, "onPropertyUnSubscribe property " + property);
-        }
-
-        public VehiclePropValue getValue() {
-            return mValue;
-        }
-    }
-}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
deleted file mode 100644
index 739f5d0..0000000
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.test;
-
-import android.annotation.ArrayRes;
-import android.car.VehicleAreaType;
-import android.car.annotation.FutureFeature;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-
-import com.android.car.R;
-import com.android.car.vehiclehal.VehiclePropValueBuilder;
-import com.android.car.vehiclehal.test.MockedVehicleHal;
-import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
-
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@FutureFeature
-public class VmsPublisherPermissionsTest extends MockedCarTestBase {
-    private static final String TAG = "VmsPublisherTest";
-    private static final int MOCK_PUBLISHER_LAYER_ID = 0;
-    private static final int MOCK_PUBLISHER_LAYER_VERSION = 0;
-
-    private HalHandler mHalHandler;
-    // Used to block until the HAL property is updated in HalHandler.onPropertySet.
-    private Semaphore mHalHandlerSemaphore;
-
-    @Override
-    protected synchronized void configureMockedHal() {
-        mHalHandler = new HalHandler();
-        addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
-                .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
-                .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
-    }
-
-    /**
-     * Creates a context with the resource vmsPublisherClients overridden. The overridden value
-     * contains the name of the test service defined also in this test package.
-     */
-    @Override
-    protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
-        Context context = getContext()
-                .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
-        Resources resources = new Resources(context.getAssets(),
-                context.getResources().getDisplayMetrics(),
-                context.getResources().getConfiguration()) {
-            @Override
-            public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
-                if (id == R.array.vmsPublisherClients) {
-                    return new String[]{
-                            "com.google.android.car.vms.publisher/"
-                                    + ".VmsPublisherClientSampleService"};
-                } else if (id == R.array.vmsSafePermissions) {
-                    return new String[]{"android.permission.ACCESS_FINE_LOCATION"};
-                }
-                return super.getStringArray(id);
-            }
-        };
-        ContextWrapper wrapper = new ContextWrapper(context) {
-            @Override
-            public Resources getResources() {
-                return resources;
-            }
-        };
-        return wrapper;
-    }
-
-    private VehiclePropValue getHalSubscriptionRequest() {
-        return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .addIntValue(VmsMessageType.SUBSCRIBE)
-                .addIntValue(MOCK_PUBLISHER_LAYER_ID)
-                .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
-                .build();
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        /**
-         * First init the semaphore, setUp will start a series of events that will ultimately
-         * update the HAL layer and release this semaphore.
-         */
-        mHalHandlerSemaphore = new Semaphore(0);
-        super.setUp();
-
-        // Inject a subscribe event which simulates the HAL is subscribed to the Sample Publisher.
-        MockedVehicleHal mHal = getMockedVehicleHal();
-        mHal.injectEvent(getHalSubscriptionRequest());
-    }
-
-    @Override
-    protected synchronized void tearDown() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.tearDown();
-    }
-
-    /**
-     * The method setUp initializes all the Car services, including the VmsPublisherService.
-     * The VmsPublisherService will start and configure its list of clients. This list was
-     * overridden in the method getCarServiceContext.
-     * Therefore, only VmsPublisherClientSampleService will be started.
-     * The service VmsPublisherClientSampleService will publish one message, which is validated in
-     * this test.
-     */
-    public void testPermissions() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        // At this point the client initialization finished. Let's validate the permissions.
-        // The VMS service is only allowed to grant ACCESS_FINE_LOCATION but not CAMERA.
-        assertTrue(
-                getContext().getPackageManager().checkPermission(
-                        "android.permission.ACCESS_FINE_LOCATION",
-                        "com.google.android.car.vms.publisher")
-                        == PackageManager.PERMISSION_GRANTED);
-        assertFalse(getContext().getPackageManager().checkPermission(
-                "android.permission.CAMERA", "com.google.android.car.vms.publisher")
-                == PackageManager.PERMISSION_GRANTED);
-    }
-
-    private class HalHandler implements VehicleHalPropertyHandler {
-        @Override
-        public synchronized void onPropertySet(VehiclePropValue value) {
-            // If this is the data message release the semaphore so the test can continue.
-            ArrayList<Integer> int32Values = value.value.int32Values;
-            if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
-                    VmsMessageType.DATA) {
-                mHalHandlerSemaphore.release();
-            }
-        }
-    }
-}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
deleted file mode 100644
index 3b3a94f..0000000
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.test;
-
-import android.annotation.ArrayRes;
-import android.car.Car;
-import android.car.VehicleAreaType;
-import android.car.annotation.FutureFeature;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsSubscriberManager;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import com.android.car.vehiclehal.test.MockedVehicleHal;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@FutureFeature
-public class VmsPublisherSubscriberTest extends MockedCarTestBase {
-    private static final int LAYER_ID = 88;
-    private static final int LAYER_VERSION = 19;
-    private static final int EXPECTED_PUBLISHER_ID = 0;
-    private static final String TAG = "VmsPubSubTest";
-
-    public static final VmsLayer LAYER = new VmsLayer(LAYER_ID, LAYER_VERSION);
-    public static final byte[] PAYLOAD = new byte[]{2, 3, 5, 7, 11, 13, 17};
-    private static final List<VmsLayer> AVAILABLE_LAYERS = new ArrayList<>(Arrays.asList(LAYER));
-
-    private HalHandler mHalHandler;
-    // Used to block until a value is propagated to the TestListener.onVmsMessageReceived.
-    private Semaphore mSubscriberSemaphore;
-    private Semaphore mAvailabilitySemaphore;
-
-    @Override
-    protected synchronized void configureMockedHal() {
-        mHalHandler = new HalHandler();
-        addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
-                .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
-                .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
-    }
-
-    /**
-     * Creates a context with the resource vmsPublisherClients overridden. The overridden value
-     * contains the name of the test service defined also in this test package.
-     */
-    @Override
-    protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
-        Context context = getContext()
-                .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
-        Resources resources = new Resources(context.getAssets(),
-                context.getResources().getDisplayMetrics(),
-                context.getResources().getConfiguration()) {
-            @Override
-            public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
-                if (id == com.android.car.R.array.vmsPublisherClients) {
-                    return new String[]{"com.android.car.test/.VmsPublisherClientMockService"};
-                }
-                return super.getStringArray(id);
-            }
-        };
-        ContextWrapper wrapper = new ContextWrapper(context) {
-            @Override
-            public Resources getResources() {
-                return resources;
-            }
-        };
-        return wrapper;
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.setUp();
-        mSubscriberSemaphore = new Semaphore(0);
-        mAvailabilitySemaphore = new Semaphore(0);
-    }
-
-    @Override
-    protected synchronized void tearDown() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.tearDown();
-    }
-
-    /**
-     * The method setUp initializes all the Car services, including the VmsPublisherService.
-     * The VmsPublisherService will start and configure its list of clients. This list was
-     * overridden in the method getCarServiceContext. Therefore, only VmsPublisherClientMockService
-     * will be started. This test method subscribes to a layer and triggers
-     * VmsPublisherClientMockService.onVmsSubscriptionChange. In turn, the mock service will publish
-     * a message, which is validated in this test.
-     */
-    public void testPublisherToSubscriber() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
-                Car.VMS_SUBSCRIBER_SERVICE);
-        TestListener listener = new TestListener();
-        vmsSubscriberManager.setListener(listener);
-        vmsSubscriberManager.subscribe(LAYER);
-
-        assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        assertEquals(LAYER, listener.getLayer());
-        assertTrue(Arrays.equals(PAYLOAD, listener.getPayload()));
-    }
-
-    /**
-     * The Mock service will get a publisher ID by sending its information when it will get
-     * ServiceReady as well as on SubscriptionChange. Since clients are not notified when
-     * publishers are assigned IDs, this test waits until the availability is changed which indicates
-     * that the Mock service has gotten its ServiceReady and publisherId.
-     */
-    public void testPublisherInfo() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
-                Car.VMS_SUBSCRIBER_SERVICE);
-        // Subscribe to layer as a way to make sure the mock client completed setting the information.
-        TestListener listener = new TestListener();
-        vmsSubscriberManager.setListener(listener);
-        vmsSubscriberManager.subscribe(LAYER);
-
-        assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-
-        byte[] info = vmsSubscriberManager.getPublisherInfo(EXPECTED_PUBLISHER_ID);
-        assertTrue(Arrays.equals(PAYLOAD, info));
-    }
-
-    /**
-     * The Mock service offers all the subscribed layers as available layers, so in this
-     * test the listener subscribes to a layer and verifies that it gets the notification that it
-     * is available.
-     */
-    public void testAvailability() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
-            Car.VMS_SUBSCRIBER_SERVICE);
-        TestListener listener = new TestListener();
-        vmsSubscriberManager.setListener(listener);
-        vmsSubscriberManager.subscribe(LAYER);
-
-        assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        assertEquals(AVAILABLE_LAYERS, listener.getAvailalbeLayers());
-    }
-
-    private class HalHandler implements MockedVehicleHal.VehicleHalPropertyHandler {
-    }
-
-    private class TestListener implements VmsSubscriberManager.VmsSubscriberClientListener {
-        private VmsLayer mLayer;
-        private byte[] mPayload;
-        private List<VmsLayer> mAvailableLayers;
-
-        @Override
-        public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
-            assertEquals(LAYER, layer);
-            assertTrue(Arrays.equals(PAYLOAD, payload));
-            mLayer = layer;
-            mPayload = payload;
-            mSubscriberSemaphore.release();
-        }
-
-        @Override
-        public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
-            assertEquals(AVAILABLE_LAYERS, availableLayers);
-            mAvailableLayers = availableLayers;
-            mAvailabilitySemaphore.release();
-        }
-
-        @Override
-        public void onCarDisconnected() {
-
-        }
-
-        public VmsLayer getLayer() {
-            return mLayer;
-        }
-
-        public byte[] getPayload() {
-            return mPayload;
-        }
-
-        public List<VmsLayer> getAvailalbeLayers() {
-            return mAvailableLayers;
-        }
-    }
-}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java b/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
deleted file mode 100644
index 063d5cf..0000000
--- a/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.test;
-
-import android.car.Car;
-import android.car.VehicleAreaType;
-import android.car.annotation.FutureFeature;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsSubscriberManager;
-import android.car.vms.VmsSubscriberManager.VmsSubscriberClientListener;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-import android.os.SystemClock;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-import com.android.car.vehiclehal.VehiclePropValueBuilder;
-import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@FutureFeature
-@MediumTest
-public class VmsSubscriberManagerTest extends MockedCarTestBase {
-    private static final String TAG = "VmsSubscriberManagerTest";
-    private static final int SUBSCRIPTION_LAYER_ID = 2;
-    private static final int SUBSCRIPTION_LAYER_VERSION = 3;
-    private static final VmsLayer SUBSCRIPTION_LAYER = new VmsLayer(SUBSCRIPTION_LAYER_ID,
-            SUBSCRIPTION_LAYER_VERSION);
-
-    private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_1 = 4;
-    private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1 = 5;
-    private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_1 =
-        new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_1, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1);
-
-    private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_2 = 6;
-    private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2 = 7;
-    private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_2 =
-        new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_2, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2);
-
-    private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_ID = 100;
-    private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION = 200;
-
-
-    private HalHandler mHalHandler;
-    // Used to block until the HAL property is updated in HalHandler.onPropertySet.
-    private Semaphore mHalHandlerSemaphore;
-    // Used to block until a value is propagated to the TestListener.onVmsMessageReceived.
-    private Semaphore mSubscriberSemaphore;
-
-    @Override
-    protected synchronized void configureMockedHal() {
-        mHalHandler = new HalHandler();
-        addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
-                .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
-                .setAccess(VehiclePropertyAccess.READ_WRITE)
-                .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.setUp();
-        mSubscriberSemaphore = new Semaphore(0);
-        mHalHandlerSemaphore = new Semaphore(0);
-    }
-
-    @Override
-    protected synchronized void tearDown() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        super.tearDown();
-    }
-
-    // Test injecting a value in the HAL and verifying it propagates to a subscriber.
-    public void testSubscribe() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
-                Car.VMS_SUBSCRIBER_SERVICE);
-        TestListener listener = new TestListener();
-        vmsSubscriberManager.setListener(listener);
-        vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
-
-        // Inject a value and wait for its callback in TestListener.onVmsMessageReceived.
-        VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-                .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
-                .setTimestamp(SystemClock.elapsedRealtimeNanos())
-                .build();
-        v.value.int32Values.add(VmsMessageType.DATA); // MessageType
-        v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
-        v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
-        v.value.bytes.add((byte) 0xa);
-        v.value.bytes.add((byte) 0xb);
-        assertEquals(0, mSubscriberSemaphore.availablePermits());
-
-        getMockedVehicleHal().injectEvent(v);
-        assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        assertEquals(SUBSCRIPTION_LAYER, listener.getLayer());
-        byte[] expectedPayload = {(byte) 0xa, (byte) 0xb};
-        assertTrue(Arrays.equals(expectedPayload, listener.getPayload()));
-    }
-
-
-    // Test injecting a value in the HAL and verifying it propagates to a subscriber.
-    public void testSubscribeAll() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
-            Car.VMS_SUBSCRIBER_SERVICE);
-        TestListener listener = new TestListener();
-        vmsSubscriberManager.setListener(listener);
-        vmsSubscriberManager.subscribeAll();
-
-        // Inject a value and wait for its callback in TestListener.onVmsMessageReceived.
-        VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-            .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
-            .setTimestamp(SystemClock.elapsedRealtimeNanos())
-            .build();
-        v.value.int32Values.add(VmsMessageType.DATA); // MessageType
-        v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
-        v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
-        v.value.bytes.add((byte) 0xa);
-        v.value.bytes.add((byte) 0xb);
-        assertEquals(0, mSubscriberSemaphore.availablePermits());
-
-        getMockedVehicleHal().injectEvent(v);
-        assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        assertEquals(SUBSCRIPTION_LAYER, listener.getLayer());
-        byte[] expectedPayload = {(byte) 0xa, (byte) 0xb};
-        assertTrue(Arrays.equals(expectedPayload, listener.getPayload()));
-    }
-
-
-    // Test injecting a value in the HAL and verifying it propagates to a subscriber.
-    public void testSimpleAvailableLayers() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
-            Car.VMS_SUBSCRIBER_SERVICE);
-        TestListener listener = new TestListener();
-        vmsSubscriberManager.setListener(listener);
-        vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
-
-        // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange.
-        VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-            .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
-            .setTimestamp(SystemClock.elapsedRealtimeNanos())
-            .build();
-        /*
-        Offering:
-        Layer  | Dependency
-        ====================
-        (2, 3) | {}
-
-        Expected availability:
-        {(2, 3)}
-         */
-        v.value.int32Values.addAll(
-            Arrays.asList(
-                VmsMessageType.OFFERING, // MessageType
-                1, // Number of offered layers
-
-                SUBSCRIPTION_LAYER_ID,
-                SUBSCRIPTION_LAYER_VERSION,
-                0 // number of dependencies for layer
-            )
-        );
-
-        assertEquals(0, mSubscriberSemaphore.availablePermits());
-
-        List<VmsLayer> expectedAvailableLayers = new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER));
-
-        getMockedVehicleHal().injectEvent(v);
-        assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        assertEquals(expectedAvailableLayers, listener.getAvailableLayers());
-    }
-
-    // Test injecting a value in the HAL and verifying it propagates to a subscriber.
-    public void testComplexAvailableLayers() throws Exception {
-        if (!VmsTestUtils.canRunTest(TAG)) return;
-        VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
-            Car.VMS_SUBSCRIBER_SERVICE);
-        TestListener listener = new TestListener();
-        vmsSubscriberManager.setListener(listener);
-        vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
-
-        // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange.
-        VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
-            .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
-            .setTimestamp(SystemClock.elapsedRealtimeNanos())
-            .build();
-        /*
-        Offering:
-        Layer  | Dependency
-        ====================
-        (2, 3) | {}
-        (4, 5) | {(2, 3)}
-        (6, 7) | {(2, 3), (4, 5)}
-        (6, 7) | {(100, 200)}
-
-        Expected availability:
-        {(2, 3), (4, 5), (6, 7)}
-         */
-
-        v.value.int32Values.addAll(
-            Arrays.asList(
-                VmsMessageType.OFFERING, // MessageType
-                4, // Number of offered layers
-
-                SUBSCRIPTION_LAYER_ID,
-                SUBSCRIPTION_LAYER_VERSION,
-                0, // number of dependencies for layer
-
-                SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
-                SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
-                1, // number of dependencies for layer
-                SUBSCRIPTION_LAYER_ID,
-                SUBSCRIPTION_LAYER_VERSION,
-
-                SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
-                SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
-                2, // number of dependencies for layer
-                SUBSCRIPTION_LAYER_ID,
-                SUBSCRIPTION_LAYER_VERSION,
-                SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
-                SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
-
-                SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
-                SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
-                1, // number of dependencies for layer
-                SUBSCRIPTION_UNSUPPORTED_LAYER_ID,
-                SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION
-            )
-        );
-
-        assertEquals(0, mSubscriberSemaphore.availablePermits());
-
-        List<VmsLayer> expectedAvailableLayers =
-            new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER,
-                SUBSCRIPTION_DEPENDANT_LAYER_1,
-                SUBSCRIPTION_DEPENDANT_LAYER_2));
-
-        getMockedVehicleHal().injectEvent(v);
-        assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
-        assertEquals(expectedAvailableLayers, listener.getAvailableLayers());
-    }
-
-    private class HalHandler implements VehicleHalPropertyHandler {
-        private VehiclePropValue mValue;
-
-        @Override
-        public synchronized void onPropertySet(VehiclePropValue value) {
-            mValue = value;
-            mHalHandlerSemaphore.release();
-        }
-
-        @Override
-        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
-            return mValue != null ? mValue : value;
-        }
-
-        @Override
-        public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
-            Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
-        }
-
-        @Override
-        public synchronized void onPropertyUnsubscribe(int property) {
-            Log.d(TAG, "onPropertyUnSubscribe property " + property);
-        }
-
-        public VehiclePropValue getValue() {
-            return mValue;
-        }
-    }
-
-
-    private class TestListener implements VmsSubscriberClientListener{
-        private VmsLayer mLayer;
-        private byte[] mPayload;
-        private List<VmsLayer> mAvailableLayers = new ArrayList<>();
-
-        @Override
-        public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
-            Log.d(TAG, "onVmsMessageReceived: layer: " + layer + " Payload: " + payload);
-            mLayer = layer;
-            mPayload = payload;
-            mSubscriberSemaphore.release();
-        }
-
-        @Override
-        public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
-            Log.d(TAG, "onLayersAvailabilityChange: Layers: " + availableLayers);
-            mAvailableLayers.addAll(availableLayers);
-            mSubscriberSemaphore.release();
-        }
-
-        @Override
-        public void onCarDisconnected() {
-
-        }
-
-        public VmsLayer getLayer() {
-            return mLayer;
-        }
-
-        public byte[] getPayload() {
-            return mPayload;
-        }
-
-        public List<VmsLayer> getAvailableLayers() {
-            return mAvailableLayers;
-        }
-    }
-}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsTestUtils.java b/tests/carservice_test/src/com/android/car/test/VmsTestUtils.java
deleted file mode 100644
index 2f3af52..0000000
--- a/tests/carservice_test/src/com/android/car/test/VmsTestUtils.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.android.car.test;
-
-import android.car.annotation.FutureFeature;
-import android.util.Log;
-
-import com.android.car.internal.FeatureConfiguration;
-
-@FutureFeature
-public class VmsTestUtils {
-    public static boolean canRunTest(String tag) {
-        if (!FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
-            Log.i(tag, "Skipping test because ENABLE_VEHICLE_MAP_SERVICE = false");
-        }
-        return FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE;
-    }
-}
diff --git a/tests/carservice_unit_test/Android.mk b/tests/carservice_unit_test/Android.mk
index 4c57c6e..1ec2008 100644
--- a/tests/carservice_unit_test/Android.mk
+++ b/tests/carservice_unit_test/Android.mk
@@ -34,6 +34,6 @@
 LOCAL_INSTRUMENTATION_FOR := CarService
 
 LOCAL_JAVA_LIBRARIES := android.car android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test android-support-test mockito-target-minus-junit4
 
 include $(BUILD_PACKAGE)
diff --git a/tests/carservice_unit_test/AndroidManifest.xml b/tests/carservice_unit_test/AndroidManifest.xml
index 36b86e7..0f67275 100644
--- a/tests/carservice_unit_test/AndroidManifest.xml
+++ b/tests/carservice_unit_test/AndroidManifest.xml
@@ -18,7 +18,7 @@
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
         package="com.android.car.carservice_unittest"
         android:sharedUserId="android.uid.system" >
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.car"
             android:label="Unit Tests for Car APIs"/>
 
diff --git a/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java b/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java
new file mode 100644
index 0000000..85ec54f
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothA2dpSink;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothProfile;
+import android.car.ICarUserService;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.cabin.CarCabinManager;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.CarBluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+
+import static org.mockito.Mockito.*;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for the {@link BluetoothDeviceConnectionPolicy}.
+ * Isolate and test the policy's functionality - test if it finds the right device(s) per profile to
+ * connect to when triggered by an appropriate event.
+ *
+ * The following services are mocked:
+ * 1. {@link CarBluetoothUserService} - connect requests to the Bluetooth stack are stubbed out
+ * and connection results can be injected (imitating results from the stack)
+ * 2. {@link CarCabinService} & {@link CarSensorService} - Fake vehicle events are injected to the
+ * policy's Broadcast Receiver.
+ */
+public class BluetoothAutoConnectPolicyTest extends AndroidTestCase {
+    private BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicyTest;
+    private BluetoothAdapter mBluetoothAdapter;
+    private BroadcastReceiver mReceiver;
+    private BluetoothDeviceConnectionPolicy.CarPropertyListener mCabinEventListener;
+    private Handler mMainHandler;
+    private Context mockContext;
+    // Mock of Services that the policy interacts with
+    private CarCabinService mockCarCabinService;
+    private CarSensorService mockCarSensorService;
+    private CarBluetoothUserService mockBluetoothUserService;
+    private PerUserCarServiceHelper mockPerUserCarServiceHelper;
+    private ICarUserService mockPerUserCarService;
+    private CarBluetoothService mockCarBluetoothService;
+    // Timeouts
+    private static final int CONNECTION_STATE_CHANGE_TIME = 200; //ms
+    private static final int CONNECTION_REQUEST_TIMEOUT = 10000;//ms
+    private static final int WAIT_FOR_COMPLETION_TIME = 3000;//ms
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mMainHandler = new Handler(Looper.getMainLooper());
+        makeMockServices();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mBluetoothDeviceConnectionPolicyTest.release();
+        super.tearDown();
+    }
+    /****************************************** Utility methods **********************************/
+
+    /**
+     * Pair the given device to the Car on all supported Bluetooth profiles.  This is just adding a
+     * (fake) BluetoothDevice to the policy's records, which is what a real Bluetooth pairing would
+     * do.
+     */
+    private void pairDevice(BluetoothDevice device) {
+        for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+            pairDeviceOnProfile(device, profile);
+        }
+    }
+
+    /**
+     * Pair a device on the given profile.  Note that this is not real Bluetooth Pairing.  This is
+     * just adding a (fake) BluetoothDevice to the policy's records, which is what a real Bluetooth
+     * pairing would have done.
+     *
+     * @param device  - Bluetooth device
+     * @param profile - Profile to pair on
+     */
+    private void pairDeviceOnProfile(BluetoothDevice device, Integer profile) {
+        sendFakeConnectionStateChangeOnProfile(device, profile, true);
+    }
+
+    /**
+     * Connect or Disconnect the list of devices.
+     *
+     * @param deviceList - list of Bluetooth devices
+     * @param connect    - true - connect, false - disconnect
+     */
+    private void connectDevices(List<BluetoothDevice> deviceList, boolean connect) {
+        for (BluetoothDevice device : deviceList) {
+            sendFakeConnectionStateChange(device, connect);
+        }
+    }
+
+    /**
+     * Inject a fake connection state changed intent on all profiles to the policy's
+     * Broadcast Receiver
+     *
+     * @param device  - Bluetooth device
+     * @param connect - connection success or failure
+     */
+    private void sendFakeConnectionStateChange(BluetoothDevice device, boolean connect) {
+        for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+            sendFakeConnectionStateChangeOnProfile(device, profile, connect);
+        }
+    }
+
+    /**
+     * Inject a fake connection state changed intent to the policy's Broadcast Receiver
+     *
+     * @param device  - Bluetooth Device
+     * @param profile - Bluetooth Profile
+     * @param connect - connection Success or Failure
+     */
+    private void sendFakeConnectionStateChangeOnProfile(BluetoothDevice device, Integer profile,
+            boolean connect) {
+        assertNotNull(mReceiver);
+        Intent connectionIntent = createBluetoothConnectionStateChangedIntent(profile, device,
+                connect);
+        mReceiver.onReceive(null, connectionIntent);
+    }
+
+    /**
+     * Utility function to create a Connection State Changed Intent for the given profile and device
+     *
+     * @param profile - Bluetooth profile
+     * @param device  - Bluetooth Device
+     * @param connect - Connection Success or Failure
+     * @return - Connection State Changed Intent with the filled up EXTRAs
+     */
+    private Intent createBluetoothConnectionStateChangedIntent(int profile, BluetoothDevice device,
+            boolean connect) {
+        Intent connectionIntent;
+        switch (profile) {
+            case BluetoothProfile.A2DP_SINK:
+                connectionIntent = new Intent(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
+                break;
+            case BluetoothProfile.HEADSET_CLIENT:
+                connectionIntent = new Intent(
+                        BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
+                break;
+            case BluetoothProfile.MAP_CLIENT:
+                connectionIntent = new Intent(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+                break;
+            case BluetoothProfile.PBAP_CLIENT:
+                connectionIntent = new Intent(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
+                break;
+            default:
+                return null;
+        }
+        connectionIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        if (connect) {
+            connectionIntent.putExtra(BluetoothProfile.EXTRA_STATE,
+                    BluetoothProfile.STATE_CONNECTED);
+        } else {
+            connectionIntent.putExtra(BluetoothProfile.EXTRA_STATE,
+                    BluetoothProfile.STATE_DISCONNECTED);
+        }
+        return connectionIntent;
+    }
+
+    /**
+     * Trigger a fake vehicle Event by injecting directly into the policy's Event Listener.
+     * Note that a Cabin Event (Door unlock) is used here.  The default policy has a Cabin Event
+     * listener.
+     * The event can be changed to what is appropriate as long as there is a corresponding listener
+     * implemented in the policy
+     */
+    private void triggerFakeVehicleEvent() throws RemoteException {
+        assertNotNull(mCabinEventListener);
+        CarPropertyValue<Boolean> value = new CarPropertyValue<>(CarCabinManager.ID_DOOR_LOCK,
+                false);
+        CarPropertyEvent event = new CarPropertyEvent(
+                CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
+        mCabinEventListener.onEvent(event);
+    }
+
+    /**
+     * Put all the mock creations in one place.  To be called from setup()
+     */
+    private void makeMockServices() {
+        mockContext = mock(Context.class);
+        mockCarCabinService = mock(CarCabinService.class);
+        mockCarSensorService = mock(CarSensorService.class);
+        mockPerUserCarServiceHelper = mock(PerUserCarServiceHelper.class);
+        mockPerUserCarService = mock(ICarUserService.class);
+        mockCarBluetoothService = mock(CarBluetoothService.class);
+        mockBluetoothUserService = mock(CarBluetoothUserService.class,
+                Mockito.withSettings().verboseLogging());
+    }
+
+    /**
+     * Mock response to a connection request on a specific device.
+     *
+     * @param device    - Bluetooth device to mock availability
+     * @param available - result to return when a connection is requested
+     */
+    private void mockDeviceAvailability(@Nullable BluetoothDevice device, boolean available)
+            throws Exception {
+        if (mBluetoothDeviceConnectionPolicyTest != null) {
+            for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+                Mockito.doAnswer(createDeviceAvailabilityAnswer(available)).when(
+                        mockBluetoothUserService).
+                        bluetoothConnectToProfile(profile, device);
+            }
+        }
+    }
+
+    private Answer<Void> createDeviceAvailabilityAnswer(final boolean available) {
+        final Answer<Void> answer = new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
+                if (invocationOnMock.getArguments().length < 2) {
+                    return null;
+                }
+                final int profile = (int) invocationOnMock.getArguments()[0];
+                final BluetoothDevice device = (BluetoothDevice) invocationOnMock.getArguments()[1];
+                // Sanity check
+                if (device == null) {
+                    return null;
+                }
+                mMainHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        mReceiver.onReceive(null,
+                                createBluetoothConnectionStateChangedIntent(profile, device,
+                                        available));
+                    }
+                }, CONNECTION_STATE_CHANGE_TIME);
+                return null;
+            }
+        };
+        return answer;
+    }
+
+    /**
+     * Utility method called from the beginning of every test to create and init the policy
+     */
+    private void createAndSetupBluetoothPolicy() throws Exception {
+        // Return the mock Bluetooth User Service when asked for
+        when(mockBluetoothUserService.isBluetoothConnectionProxyAvailable(
+                Matchers.anyInt())).thenReturn(true);
+        when(mockPerUserCarService.getBluetoothUserService()).thenReturn(mockBluetoothUserService);
+
+        mBluetoothDeviceConnectionPolicyTest = BluetoothDeviceConnectionPolicy.create(mockContext,
+                mockCarCabinService, mockCarSensorService, mockPerUserCarServiceHelper,
+                mockCarBluetoothService);
+        mBluetoothDeviceConnectionPolicyTest.setAllowReadWriteToSettings(false);
+        mBluetoothDeviceConnectionPolicyTest.init();
+
+        mReceiver = mBluetoothDeviceConnectionPolicyTest.getBluetoothBroadcastReceiver();
+        assertNotNull(mReceiver);
+        BluetoothDeviceConnectionPolicy.UserServiceConnectionCallback serviceConnectionCallback =
+                mBluetoothDeviceConnectionPolicyTest.getServiceCallback();
+        assertNotNull(serviceConnectionCallback);
+        mCabinEventListener = mBluetoothDeviceConnectionPolicyTest.getCarPropertyListener();
+        assertNotNull(mCabinEventListener);
+
+        serviceConnectionCallback.onServiceConnected(mockPerUserCarService);
+    }
+
+    /**
+     * Utility method called from the end of every test to cleanup and release the policy
+     */
+    private Intent createBluetoothBondStateChangedIntent(BluetoothDevice device, boolean bonded) {
+        // Unbond the device
+        Intent bondStateIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        if (!bonded) {
+            bondStateIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+        } else {
+            bondStateIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED);
+        }
+        bondStateIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        return bondStateIntent;
+    }
+
+    private void tagDeviceForAllProfiles(BluetoothDevice device, int priority) {
+        // tag device as primary in all profiles
+        for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+            tagDeviceForProfile(device, profile, priority);
+        }
+    }
+
+    private void tagDeviceForProfile(BluetoothDevice device, Integer profile, int priority) {
+        mBluetoothDeviceConnectionPolicyTest.tagDeviceWithPriority(device, profile, priority);
+    }
+
+
+    /************************************** Test Methods *****************************************/
+    /**
+     * Basic test -
+     * 1. Pair one device to the car on all profiles.
+     * 2. Disconnect the device
+     * 3. Inject a fake vehicle event
+     * 4. Verify that we get connection requests on all the profiles with that paired device
+     */
+    @Test
+    public void testAutoConnectOneDevice() throws Exception {
+        createAndSetupBluetoothPolicy();
+        // Tell the policy a new device connected - this mimics pairing
+        BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+        mockDeviceAvailability(device1, true);
+        // Pair (and Connect) device1 on all the Bluetooth profiles.
+        pairDevice(device1);
+        // Disconnect so we can test Autoconnect by sending a vehicle event
+        sendFakeConnectionStateChange(device1, false);
+
+        // At this point DEADBEEF0001 is paired but disconnected to the vehicle
+        // Now, trigger a connection and check if we connected to DEADBEEF0001 on all profiles
+        triggerFakeVehicleEvent();
+        // Verify that on all profiles, device1 connected
+        for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+            verify(mockBluetoothUserService,
+                    Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                    profile, device1);
+        }
+
+        // Before we cleanup wait for the last Connection Status change from mockDeviceAvailability
+        // is broadcast to the policy.
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+        // Inject an Unbond event to the policy
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+    }
+
+    /**
+     * Multi device test
+     * 1. Pair 4 different devices 2 on HFP and PBAP (since they allow 2 connections) and 1 each on
+     * A2DP and MAP
+     * 2. Disconnect all devices
+     * 3. Inject a fake vehicle event.
+     * 4. Verify that the right devices connect on the right profiles ( the snapshot recreated)
+     */
+    @Test
+    public void testAutoConnectMultiDevice() throws Exception {
+        createAndSetupBluetoothPolicy();
+        BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+        BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+        BluetoothDevice device3 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:03");
+        BluetoothDevice device4 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:04");
+        BluetoothDevice[] testDevices = new BluetoothDevice[]{device4, device3, device2, device1};
+
+        for (BluetoothDevice device : testDevices) {
+            mockDeviceAvailability(device, true);
+        }
+        // Pair 4 different devices on the 4 profiles. HFP and PBAP are connected on the same
+        // device(s)
+        pairDeviceOnProfile(device1, BluetoothProfile.HEADSET_CLIENT);
+        pairDeviceOnProfile(device1, BluetoothProfile.PBAP_CLIENT);
+        pairDeviceOnProfile(device2, BluetoothProfile.HEADSET_CLIENT);
+        pairDeviceOnProfile(device2, BluetoothProfile.PBAP_CLIENT);
+        pairDeviceOnProfile(device3, BluetoothProfile.A2DP_SINK);
+        pairDeviceOnProfile(device4, BluetoothProfile.MAP_CLIENT);
+
+        // Disconnect all the 4 devices on the respective connected profiles
+        sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.HEADSET_CLIENT, false);
+        sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.PBAP_CLIENT, false);
+        sendFakeConnectionStateChangeOnProfile(device2, BluetoothProfile.HEADSET_CLIENT, false);
+        sendFakeConnectionStateChangeOnProfile(device2, BluetoothProfile.PBAP_CLIENT, false);
+        sendFakeConnectionStateChangeOnProfile(device3, BluetoothProfile.A2DP_SINK, false);
+        sendFakeConnectionStateChangeOnProfile(device4, BluetoothProfile.MAP_CLIENT, false);
+
+        triggerFakeVehicleEvent();
+
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.HEADSET_CLIENT, device1);
+
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.PBAP_CLIENT, device1);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.HEADSET_CLIENT, device2);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.PBAP_CLIENT, device2);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.A2DP_SINK, device3);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.MAP_CLIENT, device4);
+
+        // Before we cleanup wait for the last Connection Status change from is broadcast to the
+        // policy.
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+        // Inject an Unbond event to the policy
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device3, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device4, false));
+    }
+
+    /**
+     * Test setting a device as a primary device.  A primary device, if present, will be the first
+     * device that the policy will try to connect regardless of which device connected last.
+     * 1. Pair 4 devices.
+     * 2. Leave them disconnected.
+     * 3. Tag one device as Primary.
+     * 4. Send a connection trigger.
+     * 5. Verify if the tagged device connected.
+     * 6. Disconnect and tag another device.
+     * 7. Send a Connection trigger.
+     * 8. Verify if the newly tagged device connected.
+     */
+    @Test
+    public void testAutoConnectSetPrimaryPriority() throws Exception {
+        createAndSetupBluetoothPolicy();
+        BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+        BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+        BluetoothDevice device3 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:03");
+        BluetoothDevice device4 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:04");
+        BluetoothDevice[] testDevices = new BluetoothDevice[]{device4, device3, device2, device1};
+
+        // pair all 4 devices on all 4 profiles
+        for (BluetoothDevice device : testDevices) {
+            mockDeviceAvailability(device, true);
+            pairDevice(device);
+            // Disconnect the device.  We want to test auto connection, so the state we want
+            // to be at the end of this loop is paired and disconnected
+            for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+                sendFakeConnectionStateChangeOnProfile(device, profile, false);
+            }
+        }
+        // Device Order for all profiles will be {device1, device2, device3, device4} in the order
+        // of who connected last.
+        // Randomly pick device3 as the primary device
+        tagDeviceForAllProfiles(device3,
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0);
+        // Device order should be {device3, device1, device2, device4} now
+        // Now when we trigger an auto connect, device 3 should connect on all profiles
+        triggerFakeVehicleEvent();
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.HEADSET_CLIENT, device3);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.PBAP_CLIENT, device3);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.A2DP_SINK, device3);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.MAP_CLIENT, device3);
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+
+        // Change primary device to device4
+        tagDeviceForAllProfiles(device4,
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0);
+        //Disconnect on all 4 profiles. device3 is connected on all profiles. device1 on HFP & PBAP
+        sendFakeConnectionStateChange(device3, false);
+        sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.HEADSET_CLIENT, false);
+        sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.PBAP_CLIENT, false);
+
+        // Device Order should be {device4, device3, device1, device2}
+        triggerFakeVehicleEvent();
+
+        // Check if device4 connects now
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.HEADSET_CLIENT, device4);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.PBAP_CLIENT, device4);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.A2DP_SINK, device4);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.MAP_CLIENT, device4);
+
+        // Before we cleanup wait for the last Connection Status change is broadcast to the policy.
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+        // Inject an Unbond event to the policy
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device3, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device4, false));
+    }
+
+    /**
+     * Test setting a device with Secondary priority.
+     * Tag a device as primary and another as secondary.
+     * Choose a profile that supports only one active connection - A2DP for example.
+     * Mock all devices to be available for connection - connection requests are successful
+     * Trigger connection.
+     * Secondary device should not have connected on A2DP (since Primary is available)
+     * Change Primary device to unavailable.
+     * Trigger Connection.
+     * Secondary device should now be connected on A2DP (since Primary is not available)
+     */
+    @Test
+    public void testAutoConnectSetSecondaryPriority() throws Exception {
+        createAndSetupBluetoothPolicy();
+        BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+        BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+        BluetoothDevice device3 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:03");
+        BluetoothDevice device4 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:04");
+        BluetoothDevice[] testDevices = new BluetoothDevice[]{device4, device3, device2, device1};
+
+        // Mark all devices to respond successfully for a connection request & Pair them
+        for (BluetoothDevice device : testDevices) {
+            mockDeviceAvailability(device, true);
+            pairDevice(device);
+        }
+        connectDevices(Arrays.asList(testDevices), false);
+        // Device order at this point {device1, device2, device3, device4} in the order of last
+        // connected first
+
+        // Tag device3 as Primary device and device4 as Secondary device.
+        tagDeviceForAllProfiles(device3,
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0);
+        tagDeviceForAllProfiles(device4,
+                CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1);
+        // Device order at this point {device3, device4, device1, device2}
+        // Test 1:
+        //  a) When a connection event triggers, no connection attempt should have been made on
+        // device4 for A2DPS_SINK profile because:
+        //      1. A2DP_SINK supports only one active connection.
+        //      2. Device1 is the first device in the order and it's available for connection
+        //  b) Connection attempt should have been made on HFP since HFP supports 2 connections and
+        // device4 is the second device.
+        triggerFakeVehicleEvent();
+        // Mockito.never() doesn't have a timeout option, hence the sleep here.
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+        verify(mockBluetoothUserService, Mockito.never()).bluetoothConnectToProfile(
+                BluetoothProfile.A2DP_SINK, device4);
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.HEADSET_CLIENT, device4);
+
+        // Disconnect devices again
+        connectDevices(Arrays.asList(testDevices), false);
+        // Mock primaryDevice to be unavailable
+        mockDeviceAvailability(device3, false);
+        triggerFakeVehicleEvent();
+        // Now a connection attempt should be made on device4 for A2DP_SINK for the same reasons as
+        // above
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.A2DP_SINK, device4);
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device3, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device4, false));
+    }
+
+    /**
+     * When 2 devices are paired and one is connected, test if the second device when brought in
+     * range connects when a vehicle event occurs.
+     * 1. Pair 2 devices.
+     * 2. Keep one connected.
+     * 3. Bring the other device.
+     * 4. Send a vehicle event.
+     * 5. Test if the second device connects (in addition to the already connected first device)
+     * @throws Exception
+     */
+    @Test
+    public void testMultiDeviceConnectWithOneConnected() throws Exception {
+        createAndSetupBluetoothPolicy();
+        BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+        BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+        BluetoothDevice[] testDevices = new BluetoothDevice[]{device2, device1};
+
+        // Pair both devices and leave them disconnected.
+        for (BluetoothDevice device : testDevices) {
+            mockDeviceAvailability(device, true);
+            pairDevice(device);
+            sendFakeConnectionStateChange(device, false);
+        }
+
+        // Mock the second device to be unavailable for connection.
+        mockDeviceAvailability(device2, false);
+        triggerFakeVehicleEvent();
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+        // At this point device1 should have connected and device2 disconnected, since it is mocked
+        // to be out of range (unavailable)
+        // Now bring device2 in range (mock it to be available)
+        mockDeviceAvailability(device2, true);
+        triggerFakeVehicleEvent();
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+
+        // Now device2 should be connected on the HFP, but not on A2DP (since it supports only
+        // 1 connection)
+        // There should have been 2 connection attempts on the device2 - the first one unsuccessful
+        // due to its unavailability and the second one successful.
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(2)).bluetoothConnectToProfile(
+                BluetoothProfile.HEADSET_CLIENT, device2);
+        // There should be only 1 connection attempt on device1 - since it is available to connect
+        // from the beginning.  The first connection attempt on the first vehicle event should have
+        // been successful. For the second vehicle event, we should not have tried to connect on
+        // device1 - this tests if we try to connect on already connected devices.
+        verify(mockBluetoothUserService,
+                Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+                BluetoothProfile.HEADSET_CLIENT, device1);
+        verify(mockBluetoothUserService, Mockito.never()).bluetoothConnectToProfile(
+                BluetoothProfile.A2DP_SINK, device2);
+        Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+        mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
deleted file mode 100644
index d6fd68d..0000000
--- a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsLayerDependency;
-import android.car.vms.VmsLayersOffering;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-@SmallTest
-public class VmsLayersAvailabilityTest extends AndroidTestCase {
-
-    private static final VmsLayer LAYER_X = new VmsLayer(1, 2);
-    private static final VmsLayer LAYER_Y = new VmsLayer(3, 4);
-    private static final VmsLayer LAYER_Z = new VmsLayer(5, 6);
-
-    private static final VmsLayerDependency X_DEPENDS_ON_Y =
-        new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_Y)));
-
-    private static final VmsLayerDependency X_DEPENDS_ON_Z =
-        new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_Z)));
-
-    private static final VmsLayerDependency Y_DEPENDS_ON_Z =
-        new VmsLayerDependency(LAYER_Y, new HashSet<VmsLayer>(Arrays.asList(LAYER_Z)));
-
-    private static final VmsLayerDependency Y_DEPENDS_ON_X =
-        new VmsLayerDependency(LAYER_Y, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
-
-    private static final VmsLayerDependency Z_DEPENDS_ON_X =
-        new VmsLayerDependency(LAYER_Z, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
-
-    private static final VmsLayerDependency Z_DEPENDS_ON_NOTHING =
-        new VmsLayerDependency(LAYER_Z);
-
-    private static final VmsLayerDependency X_DEPENDS_ON_SELF =
-        new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
-
-    private Set<VmsLayersOffering> mOfferings;
-    private  VmsLayersAvailability mLayersAvailability;
-
-    @Override
-    protected void setUp() throws Exception {
-        mLayersAvailability = new VmsLayersAvailability();
-        mOfferings = new HashSet<>();
-        super.setUp();
-    }
-
-    public void testNoOffering() {
-        assertTrue(mLayersAvailability.getAvailableLayers().isEmpty());
-    }
-
-    public void testEmptyOffering() {
-        mLayersAvailability.setPublishersOffering(Collections.EMPTY_LIST);
-        assertTrue(mLayersAvailability.getAvailableLayers().isEmpty());
-    }
-
-    public void testSingleLayerNoDeps() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-        expectedAvailableLayers.add(LAYER_X);
-
-        VmsLayersOffering offering =
-            new VmsLayersOffering(Arrays.asList(new VmsLayerDependency(LAYER_X)));
-
-        mOfferings.add(offering);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers, mLayersAvailability.getAvailableLayers());
-    }
-
-    public void testChainOfDependenciesSatisfied() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-        expectedAvailableLayers.add(LAYER_X);
-        expectedAvailableLayers.add(LAYER_Y);
-        expectedAvailableLayers.add(LAYER_Z);
-
-        VmsLayersOffering offering =
-            new VmsLayersOffering(Arrays.asList(
-                X_DEPENDS_ON_Y,
-                Y_DEPENDS_ON_Z,
-                Z_DEPENDS_ON_NOTHING));
-
-        mOfferings.add(offering);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-    }
-
-    public void testChainOfDependenciesSatisfiedTwoOfferings() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-        expectedAvailableLayers.add(LAYER_X);
-        expectedAvailableLayers.add(LAYER_Y);
-        expectedAvailableLayers.add(LAYER_Z);
-
-        VmsLayersOffering offering1 =
-            new VmsLayersOffering(Arrays.asList(
-                X_DEPENDS_ON_Y,
-                Y_DEPENDS_ON_Z));
-
-        VmsLayersOffering offering2 =
-            new VmsLayersOffering(Arrays.asList(
-                Z_DEPENDS_ON_NOTHING));
-
-        mOfferings.add(offering1);
-        mOfferings.add(offering2);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-    }
-
-    public void testChainOfDependencieNotSatisfied() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-        VmsLayersOffering offering =new VmsLayersOffering(Arrays.asList(
-            X_DEPENDS_ON_Y,
-            Y_DEPENDS_ON_Z));
-
-        mOfferings.add(offering);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-
-        Set<VmsLayer> expectedUnavailableLayers = new HashSet<>();
-        expectedUnavailableLayers.add(LAYER_X);
-        expectedUnavailableLayers.add(LAYER_Y);
-
-        assertEquals(expectedUnavailableLayers ,
-            new HashSet<VmsLayer>(mLayersAvailability.getUnavailableLayers()));
-    }
-
-    public void testOneOfMultipleDependencySatisfied() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-        expectedAvailableLayers.add(LAYER_X);
-        expectedAvailableLayers.add(LAYER_Z);
-
-        VmsLayersOffering offering =
-            new VmsLayersOffering(Arrays.asList(
-                X_DEPENDS_ON_Y,
-                X_DEPENDS_ON_Z,
-                Z_DEPENDS_ON_NOTHING));
-
-        mOfferings.add(offering);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-    }
-
-    public void testCyclicDependency() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-
-        VmsLayersOffering offering =
-            new VmsLayersOffering(Arrays.asList(
-                X_DEPENDS_ON_Y,
-                Y_DEPENDS_ON_Z,
-                Z_DEPENDS_ON_X));
-
-        mOfferings.add(offering);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-    }
-
-    public void testAlmostCyclicDependency() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-        expectedAvailableLayers.add(LAYER_X);
-        expectedAvailableLayers.add(LAYER_Y);
-        expectedAvailableLayers.add(LAYER_Z);
-
-        VmsLayersOffering offering1 =
-            new VmsLayersOffering(Arrays.asList(
-                X_DEPENDS_ON_Y,
-                Z_DEPENDS_ON_NOTHING));
-
-        VmsLayersOffering offering2 =
-            new VmsLayersOffering(Arrays.asList(
-                Y_DEPENDS_ON_Z,
-                Z_DEPENDS_ON_X));
-
-        mOfferings.add(offering1);
-        mOfferings.add(offering2);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-    }
-
-    public void testCyclicDependencyAndLayerWithoutDependency() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-        expectedAvailableLayers.add(LAYER_Z);
-
-        VmsLayersOffering offering1 =
-            new VmsLayersOffering(Arrays.asList(
-                X_DEPENDS_ON_Y,
-                Z_DEPENDS_ON_NOTHING));
-
-        VmsLayersOffering offering2 =
-            new VmsLayersOffering(Arrays.asList(
-                Y_DEPENDS_ON_X));
-
-        mOfferings.add(offering1);
-        mOfferings.add(offering2);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-
-
-        Set<VmsLayer> expectedUnavailableLayers = new HashSet<>();
-        expectedUnavailableLayers.add(LAYER_Y);
-        expectedUnavailableLayers.add(LAYER_X);
-
-        assertEquals(expectedUnavailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getUnavailableLayers()));
-    }
-
-    public void testSelfDependency() throws Exception {
-        Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
-
-        VmsLayersOffering offering =
-            new VmsLayersOffering(Arrays.asList(
-                X_DEPENDS_ON_SELF));
-
-        mOfferings.add(offering);
-        mLayersAvailability.setPublishersOffering(mOfferings);
-
-        assertEquals(expectedAvailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
-
-        Set<VmsLayer> expectedUnavailableLayers = new HashSet<>();
-        expectedUnavailableLayers.add(LAYER_X);
-
-        assertEquals(expectedUnavailableLayers,
-            new HashSet<VmsLayer>(mLayersAvailability.getUnavailableLayers()));
-    }
-}
\ No newline at end of file
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java
deleted file mode 100644
index 2b75012..0000000
--- a/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-import android.car.annotation.FutureFeature;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Arrays;
-import java.util.Map;
-
-@FutureFeature
-@SmallTest
-public class VmsPublishersInfoTest extends AndroidTestCase {
-    public static final byte[] MOCK_INFO_0 = new byte[]{2, 3, 5, 7, 11, 13, 17};
-    public static final byte[] SAME_MOCK_INFO_0 = new byte[]{2, 3, 5, 7, 11, 13, 17};
-    public static final byte[] MOCK_INFO_1 = new byte[]{2, 3, 5, 7, 11, 13, 17, 19};
-
-    private VmsPublishersInfo mVmsPublishersInfo;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mVmsPublishersInfo = new VmsPublishersInfo();
-    }
-
-    // Test one info sanity
-    public void testSingleInfo() throws Exception {
-        int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
-        assertEquals(0, id);
-
-        byte[] info = mVmsPublishersInfo.getPublisherInfo(id);
-        assertTrue(Arrays.equals(MOCK_INFO_0, info));
-    }
-
-    // Test one info sanity - wrong ID fails.
-    public void testSingleInfoWrongId() throws Exception {
-        int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
-        assertEquals(0, id);
-
-        try {
-            byte[] info = mVmsPublishersInfo.getPublisherInfo(id + 1);
-        }
-        catch (NullPointerException e) {
-            return;
-        }
-        fail();
-    }
-
-    // Test two infos.
-    public void testTwoInfos() throws Exception {
-        int id0 = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
-        int id1 = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_1);
-        assertEquals(0, id0);
-        assertEquals(1, id1);
-
-        byte[] info0 = mVmsPublishersInfo.getPublisherInfo(id0);
-        byte[] info1 = mVmsPublishersInfo.getPublisherInfo(id1);
-        assertTrue(Arrays.equals(MOCK_INFO_0, info0));
-        assertTrue(Arrays.equals(MOCK_INFO_1, info1));
-    }
-
-    // Test same info twice get the same ID.
-    public void testSingleInfoInsertedTwice() throws Exception {
-        int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
-        assertEquals(0, id);
-
-        int sameId = mVmsPublishersInfo.getIdForInfo(SAME_MOCK_INFO_0);
-        assertEquals(sameId, id);
-    }
-}
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java b/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
deleted file mode 100644
index 0ff1c85..0000000
--- a/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car;
-
-import android.car.vms.IVmsSubscriberClient;
-import android.car.vms.VmsLayer;
-import android.car.vms.VmsSubscriptionState;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@SmallTest
-public class VmsRoutingTest extends AndroidTestCase {
-    private static VmsLayer LAYER_WITH_SUBSCRIPTION_1= new VmsLayer(1, 2);
-    private static VmsLayer LAYER_WITH_SUBSCRIPTION_2= new VmsLayer(1, 3);
-    private static VmsLayer LAYER_WITHOUT_SUBSCRIPTION= new VmsLayer(1, 4);
-    private VmsRouting mRouting;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mRouting = new VmsRouting();
-    }
-
-    public void testAddingSubscribersAndHalLayersNoOverlap() throws Exception {
-        // Add a subscription to a layer.
-        MockVmsListener listener = new MockVmsListener();
-        mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
-
-        // Add a HAL subscription.
-        mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
-
-        // Verify expected subscriptions are in routing manager.
-        Set<VmsLayer> expectedSubscriptions = new HashSet<>();
-        expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_1);
-        expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
-        VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
-        assertEquals(2, subscriptionState.getSequenceNumber());
-        assertEquals(expectedSubscriptions, new HashSet<>(subscriptionState.getLayers()));
-
-        // Verify there is only a single listener.
-        assertEquals(1,
-            mRouting.getListeners(LAYER_WITH_SUBSCRIPTION_1).size());
-    }
-
-    public void testAddingSubscribersAndHalLayersWithOverlap() throws Exception {
-        // Add a subscription to a layer.
-        MockVmsListener listener = new MockVmsListener();
-        mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
-        mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_2);
-
-        // Add a HAL subscription to a layer there is already another subscriber for.
-        mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
-
-        // Verify expected subscriptions are in routing manager.
-        Set<VmsLayer> expectedSubscriptions = new HashSet<>();
-        expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_1);
-        expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
-        VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
-        assertEquals(3, subscriptionState.getSequenceNumber());
-        assertEquals(expectedSubscriptions, new HashSet<>(subscriptionState.getLayers()));
-    }
-
-    public void testAddingAndRemovingLayers() throws Exception {
-        // Add a subscription to a layer.
-        MockVmsListener listener = new MockVmsListener();
-        mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
-
-        // Add a HAL subscription.
-        mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
-
-        // Remove a subscription to a layer.
-        mRouting.removeSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
-
-        // Update the HAL subscription
-        mRouting.removeHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
-
-        // Verify there are no subscribers in the routing manager.
-        VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
-        assertEquals(4, subscriptionState.getSequenceNumber());
-        assertTrue(subscriptionState.getLayers().isEmpty());
-    }
-
-    public void testAddingBothTypesOfSubscribers() throws Exception {
-        // Add a subscription to a layer.
-        MockVmsListener listenerForLayer = new MockVmsListener();
-        mRouting.addSubscription(listenerForLayer, LAYER_WITH_SUBSCRIPTION_1);
-
-        // Add a subscription without a layer.
-        MockVmsListener listenerWithoutLayer = new MockVmsListener();
-        mRouting.addSubscription(listenerWithoutLayer );
-
-        // Verify 2 subscribers for the layer.
-        assertEquals(2,
-            mRouting.getListeners(LAYER_WITH_SUBSCRIPTION_1).size());
-
-        // Add the listener with layer as also a listener without layer
-        mRouting.addSubscription(listenerForLayer);
-
-        // The number of listeners for the layer should remain the same as before.
-        assertEquals(2,
-            mRouting.getListeners(LAYER_WITH_SUBSCRIPTION_1).size());
-    }
-
-    public void testOnlyRelevantSubscribers() throws Exception {
-        // Add a subscription to a layer.
-        MockVmsListener listenerForLayer = new MockVmsListener();
-        mRouting.addSubscription(listenerForLayer, LAYER_WITH_SUBSCRIPTION_1);
-
-        // Add a subscription without a layer.
-        MockVmsListener listenerWithoutLayer = new MockVmsListener();
-        mRouting.addSubscription(listenerWithoutLayer);
-
-        // Verify that only the subscriber without layer is returned.
-        Set<MockVmsListener> expectedListeneres = new HashSet<MockVmsListener>();
-        expectedListeneres.add(listenerWithoutLayer);
-        assertEquals(expectedListeneres,
-            mRouting.getListeners(LAYER_WITHOUT_SUBSCRIPTION));
-    }
-
-    class MockVmsListener extends IVmsSubscriberClient.Stub {
-        @Override
-        public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {}
-
-        @Override
-        public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {}
-    }
-}
\ No newline at end of file
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
index 20919a1..ee79618 100644
--- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
+++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
@@ -16,7 +16,7 @@
 
 package com.android.car.obd2.test;
 
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_FREEZE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_FREEZE_FRAME;
 import static com.android.car.obd2.test.Utils.concatIntArrays;
 import static com.android.car.obd2.test.Utils.stringsToIntArray;
 import static org.junit.Assert.*;
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
index ba3dbb8..a72e777 100644
--- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
+++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
@@ -16,7 +16,7 @@
 
 package com.android.car.obd2.test;
 
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_LIVE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_LIVE_FRAME;
 import static com.android.car.obd2.test.Utils.concatIntArrays;
 import static com.android.car.obd2.test.Utils.stringsToIntArray;
 import static org.junit.Assert.*;
diff --git a/tests/vehiclehal_test/Android.mk b/tests/vehiclehal_test/Android.mk
index a22ee1b..cbc6ff1 100644
--- a/tests/vehiclehal_test/Android.mk
+++ b/tests/vehiclehal_test/Android.mk
@@ -34,8 +34,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES += vehicle-hal-support-lib \
                                android-support-test \
                                android.hidl.base-V1.0-java \
-                               android.hardware.automotive.vehicle-V2.0-java \
-                               android.hardware.automotive.vehicle-V2.1-java
+                               android.hardware.automotive.vehicle-V2.0-java
 
 LOCAL_JAVA_LIBRARIES := android.car android.test.runner
 
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java
index 23b9dbe..0d6048d 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java
@@ -25,7 +25,7 @@
 import android.hardware.automotive.vehicle.V2_0.IVehicle;
 import android.hardware.automotive.vehicle.V2_0.StatusCode;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.RemoteException;
 import android.util.Log;
 
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java
index 8e14db3..25f2454 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java
@@ -25,7 +25,7 @@
 import android.hardware.automotive.vehicle.V2_0.IVehicle;
 import android.hardware.automotive.vehicle.V2_0.StatusCode;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.RemoteException;
 import android.util.Log;
 
diff --git a/tools/bootanalyze/bootanalyze.py b/tools/bootanalyze/bootanalyze.py
index 480c9d4..b878358 100755
--- a/tools/bootanalyze/bootanalyze.py
+++ b/tools/bootanalyze/bootanalyze.py
@@ -30,6 +30,7 @@
 import subprocess
 import sys
 import time
+import threading
 import yaml
 
 from datetime import datetime, date
@@ -92,10 +93,12 @@
   if args.iterate > 1 and args.bootchart:
     run_adb_shell_cmd_as_root('touch /data/bootchart/enabled')
 
-  search_events = {key: re.compile(pattern)
+  search_events_pattern = {key: re.compile(pattern)
                    for key, pattern in cfg['events'].iteritems()}
-  timing_events = {key: re.compile(pattern)
+  timing_events_pattern = {key: re.compile(pattern)
                    for key, pattern in cfg['timings'].iteritems()}
+  shutdown_events_pattern = {key: re.compile(pattern)
+                   for key, pattern in cfg['shutdown_events'].iteritems()}
 
   data_points = {}
   kernel_timing_points = collections.OrderedDict()
@@ -103,6 +106,8 @@
   boottime_points = collections.OrderedDict()
   boot_chart_file_name_prefix = "bootchart-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
   systrace_file_name_prefix = "systrace-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
+  shutdown_event_all = collections.OrderedDict()
+  shutdown_timing_event_all = collections.OrderedDict()
   for it in range(0, args.iterate):
     if args.iterate > 1:
       print "Run: {0}".format(it)
@@ -112,9 +117,24 @@
     boottime_events = None
     while attempt <= MAX_RETRIES and processing_data is None:
       attempt += 1
-      processing_data, kernel_timings, logcat_timings, boottime_events = iterate(
-        args, search_events, timing_events, cfg, error_time, components_to_monitor)
-
+      processing_data, kernel_timings, logcat_timings, boottime_events, shutdown_events,\
+          shutdown_timing_events = iterate(\
+        args, search_events_pattern, timing_events_pattern, shutdown_events_pattern, cfg,\
+        error_time, components_to_monitor)
+    if shutdown_events:
+      for k, v in shutdown_events.iteritems():
+        events = shutdown_event_all.get(k)
+        if not events:
+          events = []
+          shutdown_event_all[k] = events
+        events.append(v)
+    if shutdown_timing_events:
+      for k, v in shutdown_timing_events.iteritems():
+        events = shutdown_timing_event_all.get(k)
+        if not events:
+          events = []
+          shutdown_timing_event_all[k] = events
+        events.append(v)
     if not processing_data or not boottime_events:
       # Processing error
       print "Failed to collect valid samples for run {0}".format(it)
@@ -151,6 +171,24 @@
 
   if args.iterate > 1:
     print "-----------------"
+    print "\nshutdown events after {0} runs".format(args.iterate)
+    print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs")
+    for item in shutdown_event_all.items():
+      num_runs = len(item[1])
+      print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format(
+          item[0], sum(item[1])/num_runs, stddev(item[1]),\
+          "*time taken" if item[0].startswith("init.") else "",\
+          num_runs if num_runs != args.iterate else "")
+    print "\nshutdown timing events after {0} runs".format(args.iterate)
+    print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs")
+    for item in shutdown_timing_event_all.items():
+      num_runs = len(item[1])
+      print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format(
+          item[0], sum(item[1])/num_runs, stddev(item[1]),\
+          "*time taken" if item[0].startswith("init.") else "",\
+          num_runs if num_runs != args.iterate else "")
+
+    print "-----------------"
     print "ro.boottime.* after {0} runs".format(args.iterate)
     print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs")
     for item in boottime_points.items():
@@ -243,19 +281,41 @@
         item[0], item[1])
     print "-----------------"
 
-def iterate(args, search_events, timings, cfg, error_time, components_to_monitor):
-  if args.reboot:
-    reboot(args.serial, args.stressfs != '', args.permissive, args.adb_reboot)
+def handle_reboot_log(capture_log_on_error, shutdown_events_pattern, components_to_monitor):
+  shutdown_events, shutdown_timing_events = collect_logcat_for_shutdown(capture_log_on_error,\
+		shutdown_events_pattern, components_to_monitor)
+  print "\nshutdown events: time"
+  for item in shutdown_events.items():
+    print '{0:30}: {1:<7.5}'.format(item[0], item[1])
+  print "\nshutdown timing events: time"
+  for item in shutdown_timing_events.items():
+    print '{0:30}: {1:<7.5}'.format(item[0], item[1])
+  return shutdown_events, shutdown_timing_events
 
-  dmesg_events, kernel_timing_events = collect_events(search_events, ADB_CMD +\
-                                                      ' shell su root dmesg -w', timings,\
+def iterate(args, search_events_pattern, timings_pattern, shutdown_events_pattern, cfg, error_time,\
+    components_to_monitor):
+  shutdown_events = None
+  shutdown_timing_events = None
+  if args.reboot:
+    # sleep to make sure that logcat reader is reading before adb is gone by reboot. ugly but make
+    # impl simple.
+    t = threading.Thread(target = lambda : (time.sleep(2), reboot(args.serial, args.stressfs != '',\
+        args.permissive, args.adb_reboot)))
+    t.start()
+    shutdown_events, shutdown_timing_events = handle_reboot_log(True, shutdown_events_pattern,\
+        components_to_monitor)
+    t.join()
+
+  dmesg_events, kernel_timing_events = collect_events(search_events_pattern, ADB_CMD +\
+                                                      ' shell su root dmesg -w', timings_pattern,\
                                                       [ KERNEL_BOOT_COMPLETE ], True)
 
   logcat_stop_events = [ LOGCAT_BOOT_COMPLETE, KERNEL_BOOT_COMPLETE, LAUNCHER_START]
   if args.fs_check:
     logcat_stop_events.append("FsStat")
   logcat_events, logcat_timing_events = collect_events(
-    search_events, ADB_CMD + ' logcat -b all -v epoch', timings, logcat_stop_events, False)
+    search_events_pattern, ADB_CMD + ' logcat -b all -v epoch', timings_pattern,\
+    logcat_stop_events, False)
   logcat_event_time = extract_time(
     logcat_events, TIME_LOGCAT, float);
   logcat_original_time = extract_time(
@@ -338,8 +398,8 @@
   print "-----------------"
 
   if args.timings:
-    kernel_timing_points = generate_timing_points(kernel_timing_events, timings)
-    logcat_timing_points = generate_timing_points(logcat_timing_events, timings)
+    kernel_timing_points = generate_timing_points(kernel_timing_events, timings_pattern)
+    logcat_timing_points = generate_timing_points(logcat_timing_events, timings_pattern)
     dump_timing_points("Kernel", kernel_timing_points)
     dump_timing_points("Logcat", logcat_timing_points)
 
@@ -401,7 +461,8 @@
       if (fs_stat_val & ~0x17) != 0:
         capture_bugreport("fs_stat_" + fs_stat, events[LOGCAT_BOOT_COMPLETE])
 
-  return data_points, kernel_timing_points, logcat_timing_points, boottime_events
+  return data_points, kernel_timing_points, logcat_timing_points, boottime_events, shutdown_events,\
+      shutdown_timing_events
 
 def debug(string):
   if DEBUG:
@@ -503,6 +564,64 @@
     existing_event = events.get(new_name)
   return new_name
 
+def collect_logcat_for_shutdown(capture_log_on_error, shutdown_events_pattern,\
+    log_capture_conditions):
+  events = collections.OrderedDict()
+  # shutdown does not have timing_events but calculated from checking Xyz - XyzDone / XyzTimeout
+  timing_events = collections.OrderedDict()
+  process = subprocess.Popen(ADB_CMD + ' logcat -b all -v epoch', shell=True,
+                             stdout=subprocess.PIPE);
+  lines = []
+  capture_log = False
+  shutdown_start_time = 0
+  while (True):
+    line = process.stdout.readline().lstrip().rstrip()
+    if not line:
+      break
+    lines.append(line)
+    event = get_boot_event(line, shutdown_events_pattern);
+    if not event:
+      continue
+    time = extract_a_time(line, TIME_LOGCAT, float)
+    if not time:
+      print "cannot get time from: " + line
+      continue
+    if shutdown_start_time == 0:
+      shutdown_start_time = time
+    time = time - shutdown_start_time
+    events[event] = time
+    time_limit1 = log_capture_conditions.get(event)
+    if time_limit1 and time_limit1 <= time:
+      capture_log = True
+    pair_event = None
+    if event.endswith('Done'):
+	  pair_event = event[:-4]
+    elif event.endswith('Timeout'):
+      pair_event = event[:-7]
+      if capture_log_on_error:
+        capture_log = True
+    if not pair_event:
+      continue
+    start_time = events.get(pair_event)
+    if not start_time:
+      print "No start event for " + event
+      continue
+    time_spent = time - start_time
+    timing_event_name = pair_event + "Duration"
+    timing_events[timing_event_name] = time_spent
+    time_limit2 = log_capture_conditions.get(timing_event_name)
+    if time_limit2 and time_limit2 <= time_spent:
+      capture_log = True
+
+  if capture_log:
+    now = datetime.now()
+    log_file = ("shutdownlog-error-%s.txt") % (now.strftime("%Y-%m-%d-%H-%M-%S"))
+    print "Shutdown error, capture log to %s" % (log_file)
+    with open(log_file, 'w') as f:
+      f.write('\n'.join(lines))
+  return events, timing_events
+
+
 def collect_events(search_events, command, timings, stop_events, disable_timing_after_zygote):
   events = collections.OrderedDict()
   timing_events = {}
diff --git a/tools/bootanalyze/bugreport_anayze.py b/tools/bootanalyze/bugreport_anayze.py
new file mode 100644
index 0000000..2575ebf
--- /dev/null
+++ b/tools/bootanalyze/bugreport_anayze.py
@@ -0,0 +1,386 @@
+#!/usr/bin/python
+
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Tool to analyze boot-up time from bugreport."""
+
+import argparse
+import collections
+import datetime
+import math
+import operator
+import os
+import re
+import select
+import subprocess
+import sys
+import time
+import threading
+import yaml
+
+from datetime import datetime, date
+
+DBG = True
+
+LOG_START_PATTERN = r"""\-\-\-\-\-\-\s(.*)\s\-\-\-\-\-\-"""
+LOG_END_PATTERN = r"""\-\-\-\-\-\-\s\S.*\s\-\-\-\-\-\-"""
+
+KERNEL_LOG_TITLE = "KERNEL LOG"
+SYSYEM_LOG_TITLE = "SYSTEM LOG"
+LAST_KMSG_TITLE = "LAST KMSG"
+LAST_LOGCAT_TITLE = "LAST LOGCAT"
+
+SYSTEM_PROPS_TITLE = "SYSTEM PROPERTIES"
+
+TIME_DMESG = "\[\s*(\d+\.\d+)\]"
+TIME_LOGCAT = "(\d+)\-(\d+)\s(\d+):(\d+):(\d+\.\d+)"
+
+NATIVE_CRASH_START_PATTERN = "I\sDEBUG\s+:\s\*\*\*\s\*\*\*"
+NATIVE_CRASH_PATTERN = "I\sDEBUG\s+:"
+JAVA_CRASH_START_PATTERN = "E\sAndroidRuntime:\sFATAL\sEXCEPTION"
+JAVA_CRASH_PATTERN = "E\sAndroidRuntime:\s"
+
+EPOCH = datetime.utcfromtimestamp(0)
+
+def init_arguments():
+  parser = argparse.ArgumentParser(description='Measures boot time from bugreport.')
+  parser.add_argument('-c', '--config', dest='config',
+                      default='config.yaml', type=argparse.FileType('r'),
+                      help='config file for the tool')
+  parser.add_argument('bugreport_file', nargs=1, help='bugreport txt file',
+                       type=argparse.FileType('r'))
+  parser.add_argument('-n', '--iterate', dest='iterate', type=int, default=1,
+                      help='number of time to repeat the measurement', )
+  return parser.parse_args()
+
+# Event per each reboot, for distinghishing current boot from last boot
+class Events:
+  def __init__(self):
+    self.events = collections.OrderedDict() #K: keyword, V:time in ms
+    self.timings = collections.OrderedDict()
+    self.shutdown_events = collections.OrderedDict()
+    self.java_crash = collections.OrderedDict() #K:time, V:list of crash infos, each entry per line
+    self.native_crash = collections.OrderedDict()
+
+  def reset_events_time(self, delta):
+    new_events = collections.OrderedDict()
+    for entry in self.events.iteritems():
+      new_events[entry[0]] = entry[1] - delta
+    self.events = new_events
+    if len(self.native_crash) > 0:
+      new_crash = collections.OrderedDict()
+      for entry in self.native_crash.iteritems():
+        new_crash[entry[0] - delta] = entry[1]
+      self.native_crash = new_crash
+    if len(self.java_crash) > 0:
+      new_crash = collections.OrderedDict()
+      for entry in self.java_crash.iteritems():
+        new_crash[entry[0] - delta] = entry[1]
+      self.java_crash = new_crash
+
+  def reset_shutdown_events_time(self):
+    if len(self.shutdown_events) == 0:
+      return
+    time_offset = 0
+    new_events = collections.OrderedDict()
+    for entry in self.shutdown_events.iteritems():
+      if time_offset == 0:
+        time_offset = entry[1]
+      new_events[entry[0]] = entry[1] - time_offset
+    self.shutdown_events = new_events
+
+  def dump_dict(self, d):
+    for entry in d.iteritems():
+        print ' {0:30}: {1}'.format(entry[0], entry[1])
+
+  def dump_crash(self, time, stack):
+    print "    Crash time:", time, " stack:"
+    print ' '.join(stack)
+
+  def dump(self):
+    if len(self.events) > 0:
+      print "\n***Events:"
+      self.dump_dict(self.events)
+    if len(self.timings) > 0:
+      print "\n***Timings top 20"
+      timings_sorted = sorted(self.timings.items(), key = lambda item: item[1], reverse=True)
+      nums_to_dump = min(20, len(timings_sorted))
+      for i in range(nums_to_dump):
+          print ' {0:30}: {1}'.format(timings_sorted[i][0], timings_sorted[i][1])
+      print "\n***Timings:"
+      self.dump_dict(self.timings)
+    if len(self.shutdown_events) > 0:
+      print "\n***Shutdown Events (time relative to the begining of shutdown) :"
+      self.dump_dict(self.shutdown_events)
+    if len(self.native_crash) > 0:
+      print "\n***Native crash founds:", len(self.native_crash)
+      for entry in self.native_crash.iteritems():
+        self.dump_crash(entry[0], entry[1])
+    if len(self.java_crash) > 0:
+      print "\n***Java crash founds:", len(self.java_crash)
+      for entry in self.java_crash.iteritems():
+        self.dump_crash(entry[0], entry[1])
+
+class Parser:
+  def __init__(self, config_file, bugreport_file):
+    self.re_log_start = re.compile(LOG_START_PATTERN)
+    self.re_log_end = re.compile(LOG_END_PATTERN)
+    self.f = bugreport_file
+    cfg = yaml.load(config_file)
+    self.event_patterns = {key: re.compile(pattern)
+                         for key, pattern in cfg['events'].iteritems()}
+    self.timing_patterns = {key: re.compile(pattern)
+                         for key, pattern in cfg['timings'].iteritems()}
+    self.shutdown_event_patterns = {key: re.compile(pattern)
+                           for key, pattern in cfg['shutdown_events'].iteritems()}
+    self.current_boot_kernel = Events()
+    self.current_boot_logcat = Events()
+    self.last_boot_kernel = Events()
+    self.last_boot_logcat = Events()
+    self.boottime_props = collections.OrderedDict() # K:prop, V:boot time, added in boot time order
+    self.bootloader_time = 0
+    self.re_time_dmesg = re.compile(TIME_DMESG)
+    self.re_time_logcat = re.compile(TIME_LOGCAT)
+    self.re_native_crash_start = re.compile(NATIVE_CRASH_START_PATTERN)
+    self.re_native_crash = re.compile(NATIVE_CRASH_PATTERN)
+    self.re_java_crash_start = re.compile(JAVA_CRASH_START_PATTERN)
+    self.re_java_crash = re.compile(JAVA_CRASH_PATTERN)
+
+  def match_an_event(self, event_patterns, line):
+    for event_key, event_pattern in event_patterns.iteritems():
+      m = event_pattern.search(line)
+      if m:
+        return event_key, m
+    return None, None
+
+  def get_event_time(self, line, is_kernel):
+    if is_kernel:
+      m = self.re_time_dmesg.search(line)
+      if not m:
+        print "Cannot get time from log:", line
+        return -1
+      return int(float(m.group(1)) * 1000)
+    else:
+      m = self.re_time_logcat.search(line)
+      if not m:
+          print "Cannot get time from log:", line
+          return -1
+      mm = int(m.group(1))
+      dd = int(m.group(2))
+      hh = int(m.group(3))
+      min = int(m.group(4))
+      usecs = int(float(m.group(5)) * 1000000)
+      secs = usecs / 1000000
+      usecs = usecs - 1000000 * secs
+      dt = datetime(2017, mm, dd, hh, min, secs, usecs)
+      return int((dt - EPOCH).total_seconds() * 1000)
+
+  def queue_crash(self, event, crash_time, crash_stacks, is_native_crash):
+    stacks = list(crash_stacks)
+    if is_native_crash:
+      event.native_crash[crash_time] = stacks
+    else:
+      event.java_crash[crash_time] = stacks
+
+  def check_crash(self, event, orig_line):
+    line = orig_line
+    crash_time = 0
+    crash_stacks = []
+    is_native_crash = True
+    while len(line) > 0:
+      m = self.re_native_crash_start.search(line)
+      if m:
+        if len(crash_stacks) > 0:
+          self.queue_crash(event, crash_time, crash_stacks, is_native_crash)
+          crash_stacks = []
+        is_native_crash = True
+        crash_stacks.append(line)
+        crash_time = self.get_event_time(line, False)
+        line = self.f.readline()
+        continue
+      m = self.re_native_crash.search(line)
+      if m:
+        crash_stacks.append(line)
+        line = self.f.readline()
+        continue
+      m = self.re_java_crash_start.search(line)
+      if m:
+        if len(crash_stacks) > 0:
+          self.queue_crash(event, crash_time, crash_stacks, is_native_crash)
+          crash_stacks = []
+        is_native_crash = False
+        crash_stacks.append(line)
+        crash_time = self.get_event_time(line, False)
+        line = self.f.readline()
+        continue
+      m = self.re_java_crash.search(line)
+      if m:
+        crash_stacks.append(line)
+        line = self.f.readline()
+        continue
+      # reaching here means not crash, so return new line
+      if line != orig_line:
+        return line
+      else:
+        return self.f.readline()
+
+
+
+  def handle_events(self, event, is_kernel):
+    line = self.f.readline()
+    while len(line) > 0 and not self.re_log_end.match(line):
+      key, m = self.match_an_event(self.event_patterns, line)
+      if m:
+        event.events[key] = self.get_event_time(line, is_kernel)
+        line = self.f.readline()
+        continue
+      key, m = self.match_an_event(self.timing_patterns, line)
+      if m:
+        name = m.group('name')
+        time = float(m.group('time'))
+        if key.endswith('_secs'):
+          time = time * 1000
+        event.timings[name] = int(time)
+        line = self.f.readline()
+        continue
+      key, m = self.match_an_event(self.shutdown_event_patterns, line)
+      if m:
+          event.shutdown_events[key] = self.get_event_time(line, is_kernel)
+          line = self.f.readline()
+          continue
+      if not is_kernel: # collect crash
+        line = self.check_crash(event, line)
+        continue
+      line = self.f.readline()
+
+  def handle_kernel_log(self):
+    if DBG:
+      print "start " + KERNEL_LOG_TITLE
+    self.handle_events(self.current_boot_kernel, True)
+
+  def handle_system_log(self):
+    if DBG:
+      print "start " + SYSYEM_LOG_TITLE
+    self.handle_events(self.current_boot_logcat, False)
+
+  def handle_last_kernel_log(self):
+      if DBG:
+          print "start " + LAST_KMSG_TITLE
+      self.handle_events(self.last_boot_kernel, True)
+
+  def handle_last_system_log(self):
+    if DBG:
+        print "start " + LAST_LOGCAT_TITLE
+    self.handle_events(self.last_boot_logcat, False)
+
+  def handle_system_props(self):
+    if DBG:
+        print "start " + SYSTEM_PROPS_TITLE
+    re_prop = re.compile(r"""\[(.+)\].*\[(.*)\]""")
+    RO_BOOTTIME_PROP = "ro.boottime."
+    boottime_props = {} # K: prop name, V: boot time in ms
+    line = self.f.readline()
+    while len(line) > 0 and not self.re_log_end.match(line):
+      m = re_prop.match(line)
+      if not m:
+        print "Cannot parse prop:", line
+        line = self.f.readline()
+        continue
+      if m.group(1).startswith(RO_BOOTTIME_PROP):
+        name = m.group(1)[len(RO_BOOTTIME_PROP):]
+        time = int(m.group(2)) / 1000000 # ns to ms
+        boottime_props[name] = time
+      elif m.group(1) == "ro.boot.boottime":
+        print "Found bootloader boottime ", line
+        entries = m.group(2).split(",")
+        for entry in entries:
+          values = entry.split(":")
+          if values[0] != "SW":
+              self.bootloader_time += int(values[1])
+      line = self.f.readline()
+    self.boottime_props = collections.OrderedDict(sorted(
+        boottime_props.items(), key = lambda item: item[1]))
+
+  def parse(self):
+    while (True):
+      l = self.f.readline()
+      if len(l) == 0: # EOF
+        return
+      m = self.re_log_start.match(l)
+      if not m:
+        continue
+      #print m.group(1)
+      if m.group(1).startswith(KERNEL_LOG_TITLE):
+        self.handle_kernel_log()
+      elif m.group(1).startswith(SYSYEM_LOG_TITLE):
+        self.handle_system_log()
+      elif m.group(1).startswith(SYSTEM_PROPS_TITLE):
+        self.handle_system_props()
+      elif m.group(1).startswith(LAST_KMSG_TITLE):
+        self.handle_last_kernel_log()
+      elif m.group(1).startswith(LAST_LOGCAT_TITLE):
+        self.handle_last_system_log()
+
+  def dump_props(self):
+    if self.bootloader_time != 0:
+      print "*bootloader time:", self.bootloader_time
+    if self.boottime_props:
+      print "*ro.boottime.*:"
+      for name, t in self.boottime_props.iteritems():
+        print ' {0:30}: {1}'.format(name, t)
+
+  def reset_event_times(self, kernel_event, logcat_event):
+    has_boot_complete = True
+    kernel_bootcomplete_time = kernel_event.events.get("BootComplete_kernel")
+    if not kernel_bootcomplete_time:
+      has_boot_complete = False
+    logcat_bootcomplete_time = logcat_event.events.get("BootComplete")
+    if not logcat_bootcomplete_time:
+      has_boot_complete = False
+    time_delta = 0
+    if has_boot_complete:
+      time_delta = logcat_bootcomplete_time - kernel_bootcomplete_time
+    else:
+      time_delta = logcat_event.events.values()[0] if len(logcat_event.events) > 0 else 0
+    logcat_event.reset_events_time(time_delta)
+    logcat_event.reset_shutdown_events_time()
+    kernel_event.reset_shutdown_events_time()
+    return has_boot_complete
+
+  def dump(self):
+    self.dump_props()
+    boot_complete_found = self.reset_event_times(self.current_boot_kernel, self.current_boot_logcat)
+    print "* Kernel dmesg:"
+    self.current_boot_kernel.dump()
+    print "\n\n* Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found\
+        else "(time set relative to the first event):")
+    self.current_boot_logcat.dump()
+    print "\n\n\n==== Data from last boot ==="
+    boot_complete_found = self.reset_event_times(self.last_boot_kernel, self.last_boot_logcat)
+    print "\n\n* Last Kernel dmesg:"
+    self.last_boot_kernel.dump()
+    print "\n\n* Last Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found \
+        else "(time set relative to the first event):")
+    self.last_boot_logcat.dump()
+
+def main():
+  args = init_arguments()
+
+  parser = Parser(args.config, args.bugreport_file[0])
+  parser.parse()
+  parser.dump()
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/bootanalyze/config.yaml b/tools/bootanalyze/config.yaml
index ea60434..37f2891 100644
--- a/tools/bootanalyze/config.yaml
+++ b/tools/bootanalyze/config.yaml
@@ -1,13 +1,13 @@
 #YAML
 time_correction_key: correction
 timings:
-  system_server: SystemServerTiming(Async)?\s*:\s*(?P<name>[^\s]+) took to complete:\s(?P<time>[0-9]+)ms
+  system_server: SystemServerTiming(Async)?\s*:\s*(?P<name>\S+) took to complete:\s(?P<time>[0-9]+)ms
   fs_shutdown: (?P<name>boot_fs_shutdown),(?P<time>[0-9]+),([0-9]+)
-  ueventd_secs: ueventd:\s(?P<name>[^\s].+)\stook\s(?P<time>[.0-9]+)\sseconds
-  init_command_ms: init:\sCommand\s(?P<name>[^\s].+)\sreturned.*took\s(?P<time>[.0-9]+)ms
-  init_service_exec_secs: init:\sService\s.*exec\s[^\s]+\s\((?P<name>[^\s].+)\).*pid.*\swaiting\stook\s(?P<time>[.0-9]+)\sseconds
-  zygote64_timing: (?P<name>Zygote64Timing\:\s[^\s]+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
-  zygote32_timing: (?P<name>Zygote32Timing\:\s[^\s]+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
+  ueventd_secs: ueventd:\s(?P<name>\S.+)\stook\s(?P<time>[.0-9]+)\sseconds
+  init_command_ms: init:\sCommand\s(?P<name>\S.+)\sreturned.*took\s(?P<time>[.0-9]+)ms
+  init_service_exec_secs: init:\sService\s.*exec\s\S+\s\((?P<name>\S.+)\).*pid.*\swaiting\stook\s(?P<time>[.0-9]+)\sseconds
+  zygote64_timing: (?P<name>Zygote64Timing\:\s\S+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
+  zygote32_timing: (?P<name>Zygote32Timing\:\s\S+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
 events:
   kernel: Linux version
   android_init_1st_stage: init first stage started
@@ -40,7 +40,6 @@
   SystemServer_start: Entered the Android system server!
   system_server_ready: Enabled StrictMode for system server main
   PackageManagerInit_start: SystemServer\s*:\s*StartPackageManagerService
-  PackageManagerInit_ready: StartPackageManagerService took to complete
   BluetoothService_start: Starting com.android.server.BluetoothService
   SystemUi_start: for service com.android.systemui/.
   CarLauncherReady: Em.Overview:\s*onResume
@@ -57,4 +56,23 @@
   BootComplete_kernel: processing action \(sys\.boot_completed=1\)
   LauncherStart: START.*HOME.*(NexusLauncherActivity|GEL|LensPickerTrampolineActivity|SetupWizardActivity)
   FsStat: fs_stat, partition:userdata stat:(0x\S+)
-
+shutdown_events:
+  ShutdownStart: ShutdownThread:\sNotifying thread to start shutdown
+  ShutdownBroadcast: ShutdownThread:\sSending shutdown broadcast
+  ShutdownActivityManagerService: ShutdownThread:\sShutting down activity manager
+  ShutdownPackageManagerService: ShutdownThread:\sShutting down package manager
+  ShutdownNfc: ShutdownThread:\sTurning off NFC
+  ShutdownBt: ShutdownThread:\sDisabling Bluetooth
+  ShutdownRadio: ShutdownThread:\sTurning off cellular radios
+  ShutdownRadiosWait: ShutdownThread:\sWaiting for NFC, Bluetooth and Radio
+  ShutdownBtDone: ShutdownThread:\sBluetooth turned off
+  ShutdownRadioDone: ShutdownThread:\sRadio turned off
+  ShutdownNfcDone: ShutdownThread:\sNFC turned off
+  ShutdownRadiosWaitDone: ShutdownThread:\sNFC, Radio and Bluetooth shutdown complete
+  ShutdownRadiosWaitTimeout: ShutdownThread:\sTimed out waiting for NFC, Radio and Bluetooth shutdown
+  ShutdownStorageManagerSerivce: ShutdownThread:\sShutting down StorageManagerService
+  ShutdownStorageManagerSerivceDone: ShutdownThread:\sResult code [\d]+ from StorageManagerService\.shutdown
+  ShutdownStorageManagerSerivceTimeout: ShutdownThread:\sShutdown wait timed out
+  ShutdownStartDone: ShutdownThread:\sPerforming low-level shutdown
+  ShutdownInitAction: init\s+:\sprocessing action \(sys\.shutdown\.requested
+  ShutdownInitFsShutdown: init\s+:\sShutdown timeout
diff --git a/tools/emulator/diagnostic_injector.py b/tools/emulator/diagnostic_injector.py
index 4bd3317..7678576 100755
--- a/tools/emulator/diagnostic_injector.py
+++ b/tools/emulator/diagnostic_injector.py
@@ -20,8 +20,9 @@
 # Use thusly:
 # $ ./diagnostic_injector.py <path/to/diagnostic.json>
 
-import sys
+import argparse
 import json
+import sys
 import time
 
 import vhal_consts_2_1 as c
@@ -48,8 +49,8 @@
 from diagnostic_builder import DiagnosticEventBuilder
 
 class DiagnosticHalWrapper(object):
-    def __init__(self):
-        self.vhal = Vhal(c.vhal_types_2_0)
+    def __init__(self, device):
+        self.vhal = Vhal(c.vhal_types_2_0, device)
         self.liveFrameConfig = self.chat(
             lambda hal: hal.getConfig(c.VEHICLEPROPERTY_OBD2_LIVE_FRAME))
         self.freezeFrameConfig = self.chat(
@@ -105,11 +106,14 @@
             else:
                 print("fail: %s" % status)
 
-if len(sys.argv) < 2:
-    print("Syntax: diagnostic_injector.py <path/to/diagnostic.json>")
-    sys.exit(1)
+parser = argparse.ArgumentParser(description='Diagnostic Events Injector')
+parser.add_argument('jsondoc', nargs='+')
+parser.add_argument('-s', action='store', dest='deviceid', default=None)
 
-halWrapper = DiagnosticHalWrapper()
+args = parser.parse_args()
 
-for arg in sys.argv[1:]:
+halWrapper = DiagnosticHalWrapper(device=args.deviceid)
+
+for arg in args.jsondoc:
+    print("Injecting %s" % arg)
     halWrapper.inject(arg)
diff --git a/tools/emulator/diagnostic_sensors.py b/tools/emulator/diagnostic_sensors.py
index abde4b8..c808182 100644
--- a/tools/emulator/diagnostic_sensors.py
+++ b/tools/emulator/diagnostic_sensors.py
@@ -17,115 +17,115 @@
 # This file is generated by types.hal by packages/services/Car/tools/update-obd2-sensors.py
 # DO NOT EDIT MANUALLY
 
-OBD2_SENSOR_INTEGER_FUEL_SYSTEM_STATUS = 0
-OBD2_SENSOR_INTEGER_MALFUNCTION_INDICATOR_LIGHT_ON = 1
-OBD2_SENSOR_INTEGER_IGNITION_MONITORS_SUPPORTED = 2
-OBD2_SENSOR_INTEGER_IGNITION_SPECIFIC_MONITORS = 3
-OBD2_SENSOR_INTEGER_INTAKE_AIR_TEMPERATURE = 4
-OBD2_SENSOR_INTEGER_COMMANDED_SECONDARY_AIR_STATUS = 5
-OBD2_SENSOR_INTEGER_NUM_OXYGEN_SENSORS_PRESENT = 6
-OBD2_SENSOR_INTEGER_RUNTIME_SINCE_ENGINE_START = 7
-OBD2_SENSOR_INTEGER_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8
-OBD2_SENSOR_INTEGER_WARMUPS_SINCE_CODES_CLEARED = 9
-OBD2_SENSOR_INTEGER_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10
-OBD2_SENSOR_INTEGER_ABSOLUTE_BAROMETRIC_PRESSURE = 11
-OBD2_SENSOR_INTEGER_CONTROL_MODULE_VOLTAGE = 12
-OBD2_SENSOR_INTEGER_AMBIENT_AIR_TEMPERATURE = 13
-OBD2_SENSOR_INTEGER_TIME_WITH_MALFUNCTION_LIGHT_ON = 14
-OBD2_SENSOR_INTEGER_TIME_SINCE_TROUBLE_CODES_CLEARED = 15
-OBD2_SENSOR_INTEGER_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16
-OBD2_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_VOLTAGE = 17
-OBD2_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_CURRENT = 18
-OBD2_SENSOR_INTEGER_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19
-OBD2_SENSOR_INTEGER_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20
-OBD2_SENSOR_INTEGER_FUEL_TYPE = 21
-OBD2_SENSOR_INTEGER_FUEL_RAIL_ABSOLUTE_PRESSURE = 22
-OBD2_SENSOR_INTEGER_ENGINE_OIL_TEMPERATURE = 23
-OBD2_SENSOR_INTEGER_DRIVER_DEMAND_PERCENT_TORQUE = 24
-OBD2_SENSOR_INTEGER_ENGINE_ACTUAL_PERCENT_TORQUE = 25
-OBD2_SENSOR_INTEGER_ENGINE_REFERENCE_PERCENT_TORQUE = 26
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_IDLE = 27
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31
-OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX = OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4
-OBD2_SENSOR_INTEGER_VENDOR_START_INDEX = OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX + 1
+DIAGNOSTIC_SENSOR_INTEGER_FUEL_SYSTEM_STATUS = 0
+DIAGNOSTIC_SENSOR_INTEGER_MALFUNCTION_INDICATOR_LIGHT_ON = 1
+DIAGNOSTIC_SENSOR_INTEGER_IGNITION_MONITORS_SUPPORTED = 2
+DIAGNOSTIC_SENSOR_INTEGER_IGNITION_SPECIFIC_MONITORS = 3
+DIAGNOSTIC_SENSOR_INTEGER_INTAKE_AIR_TEMPERATURE = 4
+DIAGNOSTIC_SENSOR_INTEGER_COMMANDED_SECONDARY_AIR_STATUS = 5
+DIAGNOSTIC_SENSOR_INTEGER_NUM_OXYGEN_SENSORS_PRESENT = 6
+DIAGNOSTIC_SENSOR_INTEGER_RUNTIME_SINCE_ENGINE_START = 7
+DIAGNOSTIC_SENSOR_INTEGER_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8
+DIAGNOSTIC_SENSOR_INTEGER_WARMUPS_SINCE_CODES_CLEARED = 9
+DIAGNOSTIC_SENSOR_INTEGER_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10
+DIAGNOSTIC_SENSOR_INTEGER_ABSOLUTE_BAROMETRIC_PRESSURE = 11
+DIAGNOSTIC_SENSOR_INTEGER_CONTROL_MODULE_VOLTAGE = 12
+DIAGNOSTIC_SENSOR_INTEGER_AMBIENT_AIR_TEMPERATURE = 13
+DIAGNOSTIC_SENSOR_INTEGER_TIME_WITH_MALFUNCTION_LIGHT_ON = 14
+DIAGNOSTIC_SENSOR_INTEGER_TIME_SINCE_TROUBLE_CODES_CLEARED = 15
+DIAGNOSTIC_SENSOR_INTEGER_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16
+DIAGNOSTIC_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_VOLTAGE = 17
+DIAGNOSTIC_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_CURRENT = 18
+DIAGNOSTIC_SENSOR_INTEGER_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19
+DIAGNOSTIC_SENSOR_INTEGER_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20
+DIAGNOSTIC_SENSOR_INTEGER_FUEL_TYPE = 21
+DIAGNOSTIC_SENSOR_INTEGER_FUEL_RAIL_ABSOLUTE_PRESSURE = 22
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_OIL_TEMPERATURE = 23
+DIAGNOSTIC_SENSOR_INTEGER_DRIVER_DEMAND_PERCENT_TORQUE = 24
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_ACTUAL_PERCENT_TORQUE = 25
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_REFERENCE_PERCENT_TORQUE = 26
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_IDLE = 27
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31
+DIAGNOSTIC_SENSOR_INTEGER_LAST_SYSTEM_INDEX = DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4
+DIAGNOSTIC_SENSOR_INTEGER_VENDOR_START_INDEX = DIAGNOSTIC_SENSOR_INTEGER_LAST_SYSTEM_INDEX + 1
 
 
 
-OBD2_SENSOR_FLOAT_CALCULATED_ENGINE_LOAD = 0
-OBD2_SENSOR_FLOAT_ENGINE_COOLANT_TEMPERATURE = 1
-OBD2_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK1 = 2
-OBD2_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK1 = 3
-OBD2_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK2 = 4
-OBD2_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK2 = 5
-OBD2_SENSOR_FLOAT_FUEL_PRESSURE = 6
-OBD2_SENSOR_FLOAT_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7
-OBD2_SENSOR_FLOAT_ENGINE_RPM = 8
-OBD2_SENSOR_FLOAT_VEHICLE_SPEED = 9
-OBD2_SENSOR_FLOAT_TIMING_ADVANCE = 10
-OBD2_SENSOR_FLOAT_MAF_AIR_FLOW_RATE = 11
-OBD2_SENSOR_FLOAT_THROTTLE_POSITION = 12
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_VOLTAGE = 13
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_VOLTAGE = 16
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_VOLTAGE = 19
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_VOLTAGE = 22
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_VOLTAGE = 25
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_VOLTAGE = 28
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_VOLTAGE = 31
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_VOLTAGE = 34
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36
-OBD2_SENSOR_FLOAT_FUEL_RAIL_PRESSURE = 37
-OBD2_SENSOR_FLOAT_FUEL_RAIL_GAUGE_PRESSURE = 38
-OBD2_SENSOR_FLOAT_COMMANDED_EXHAUST_GAS_RECIRCULATION = 39
-OBD2_SENSOR_FLOAT_EXHAUST_GAS_RECIRCULATION_ERROR = 40
-OBD2_SENSOR_FLOAT_COMMANDED_EVAPORATIVE_PURGE = 41
-OBD2_SENSOR_FLOAT_FUEL_TANK_LEVEL_INPUT = 42
-OBD2_SENSOR_FLOAT_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47
-OBD2_SENSOR_FLOAT_ABSOLUTE_LOAD_VALUE = 48
-OBD2_SENSOR_FLOAT_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49
-OBD2_SENSOR_FLOAT_RELATIVE_THROTTLE_POSITION = 50
-OBD2_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_B = 51
-OBD2_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_C = 52
-OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_D = 53
-OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_E = 54
-OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_F = 55
-OBD2_SENSOR_FLOAT_COMMANDED_THROTTLE_ACTUATOR = 56
-OBD2_SENSOR_FLOAT_ETHANOL_FUEL_PERCENTAGE = 57
-OBD2_SENSOR_FLOAT_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66
-OBD2_SENSOR_FLOAT_RELATIVE_ACCELERATOR_PEDAL_POSITION = 67
-OBD2_SENSOR_FLOAT_HYBRID_BATTERY_PACK_REMAINING_LIFE = 68
-OBD2_SENSOR_FLOAT_FUEL_INJECTION_TIMING = 69
-OBD2_SENSOR_FLOAT_ENGINE_FUEL_RATE = 70
-OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX = OBD2_SENSOR_FLOAT_ENGINE_FUEL_RATE
-OBD2_SENSOR_FLOAT_VENDOR_START_INDEX = OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX + 1
+DIAGNOSTIC_SENSOR_FLOAT_CALCULATED_ENGINE_LOAD = 0
+DIAGNOSTIC_SENSOR_FLOAT_ENGINE_COOLANT_TEMPERATURE = 1
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK1 = 2
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK1 = 3
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK2 = 4
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK2 = 5
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_PRESSURE = 6
+DIAGNOSTIC_SENSOR_FLOAT_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7
+DIAGNOSTIC_SENSOR_FLOAT_ENGINE_RPM = 8
+DIAGNOSTIC_SENSOR_FLOAT_VEHICLE_SPEED = 9
+DIAGNOSTIC_SENSOR_FLOAT_TIMING_ADVANCE = 10
+DIAGNOSTIC_SENSOR_FLOAT_MAF_AIR_FLOW_RATE = 11
+DIAGNOSTIC_SENSOR_FLOAT_THROTTLE_POSITION = 12
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR1_VOLTAGE = 13
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR2_VOLTAGE = 16
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR3_VOLTAGE = 19
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR4_VOLTAGE = 22
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR5_VOLTAGE = 25
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR6_VOLTAGE = 28
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR7_VOLTAGE = 31
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR8_VOLTAGE = 34
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_RAIL_PRESSURE = 37
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_RAIL_GAUGE_PRESSURE = 38
+DIAGNOSTIC_SENSOR_FLOAT_COMMANDED_EXHAUST_GAS_RECIRCULATION = 39
+DIAGNOSTIC_SENSOR_FLOAT_EXHAUST_GAS_RECIRCULATION_ERROR = 40
+DIAGNOSTIC_SENSOR_FLOAT_COMMANDED_EVAPORATIVE_PURGE = 41
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_TANK_LEVEL_INPUT = 42
+DIAGNOSTIC_SENSOR_FLOAT_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_LOAD_VALUE = 48
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49
+DIAGNOSTIC_SENSOR_FLOAT_RELATIVE_THROTTLE_POSITION = 50
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_B = 51
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_C = 52
+DIAGNOSTIC_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_D = 53
+DIAGNOSTIC_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_E = 54
+DIAGNOSTIC_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_F = 55
+DIAGNOSTIC_SENSOR_FLOAT_COMMANDED_THROTTLE_ACTUATOR = 56
+DIAGNOSTIC_SENSOR_FLOAT_ETHANOL_FUEL_PERCENTAGE = 57
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66
+DIAGNOSTIC_SENSOR_FLOAT_RELATIVE_ACCELERATOR_PEDAL_POSITION = 67
+DIAGNOSTIC_SENSOR_FLOAT_HYBRID_BATTERY_PACK_REMAINING_LIFE = 68
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_INJECTION_TIMING = 69
+DIAGNOSTIC_SENSOR_FLOAT_ENGINE_FUEL_RATE = 70
+DIAGNOSTIC_SENSOR_FLOAT_LAST_SYSTEM_INDEX = DIAGNOSTIC_SENSOR_FLOAT_ENGINE_FUEL_RATE
+DIAGNOSTIC_SENSOR_FLOAT_VENDOR_START_INDEX = DIAGNOSTIC_SENSOR_FLOAT_LAST_SYSTEM_INDEX + 1
 
 
diff --git a/tools/emulator/vhal_const_generate.py b/tools/emulator/vhal_const_generate.py
index 6695bd3..55bcd1b 100755
--- a/tools/emulator/vhal_const_generate.py
+++ b/tools/emulator/vhal_const_generate.py
@@ -104,9 +104,9 @@
 
 # Work around the fact that the parser doesn't (yet?) deal with inheritance.
 # WARNING:  This pattern is rather unsafe since we're not merging the lists as we should!
-vhal_21_doc['enums']['VehiclePropertyGroup'] = vhal_20_doc['enums']['VehiclePropertyGroup']
-vhal_21_doc['enums']['VehiclePropertyType'] = vhal_20_doc['enums']['VehiclePropertyType']
-vhal_21_doc['enums']['VehicleArea'] = vhal_20_doc['enums']['VehicleArea']
+# vhal_21_doc['enums']['VehiclePropertyGroup'] = vhal_20_doc['enums']['VehiclePropertyGroup']
+# vhal_21_doc['enums']['VehiclePropertyType'] = vhal_20_doc['enums']['VehiclePropertyType']
+# vhal_21_doc['enums']['VehicleArea'] = vhal_20_doc['enums']['VehicleArea']
 
 def generateHal20():
     print("********************************")
@@ -144,4 +144,4 @@
 
 
 generateHal20()
-generateHal21()
+# generateHal21()
diff --git a/tools/emulator/vhal_consts_2_0.py b/tools/emulator/vhal_consts_2_0.py
index 50518fd..59c430c 100644
--- a/tools/emulator/vhal_consts_2_0.py
+++ b/tools/emulator/vhal_consts_2_0.py
@@ -15,18 +15,236 @@
 # DO NOT EDIT MANUALLY
 # This file was autogenerated by vhal_const_generate.py
 
-# VehicleApPowerSetState
-VEHICLEAPPOWERSETSTATE_BOOT_COMPLETE = 0x1
-VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_ENTRY = 0x2
-VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_EXIT = 0x3
-VEHICLEAPPOWERSETSTATE_SHUTDOWN_POSTPONE = 0x4
-VEHICLEAPPOWERSETSTATE_SHUTDOWN_START = 0x5
-VEHICLEAPPOWERSETSTATE_DISPLAY_OFF = 0x6
-VEHICLEAPPOWERSETSTATE_DISPLAY_ON = 0x7
+# VehicleAudioContextFlag
+VEHICLEAUDIOCONTEXTFLAG_MUSIC_FLAG = 0x1
+VEHICLEAUDIOCONTEXTFLAG_NAVIGATION_FLAG = 0x2
+VEHICLEAUDIOCONTEXTFLAG_VOICE_COMMAND_FLAG = 0x4
+VEHICLEAUDIOCONTEXTFLAG_CALL_FLAG = 0x8
+VEHICLEAUDIOCONTEXTFLAG_ALARM_FLAG = 0x10
+VEHICLEAUDIOCONTEXTFLAG_NOTIFICATION_FLAG = 0x20
+VEHICLEAUDIOCONTEXTFLAG_UNKNOWN_FLAG = 0x40
+VEHICLEAUDIOCONTEXTFLAG_SAFETY_ALERT_FLAG = 0x80
+VEHICLEAUDIOCONTEXTFLAG_CD_ROM_FLAG = 0x100
+VEHICLEAUDIOCONTEXTFLAG_AUX_AUDIO_FLAG = 0x200
+VEHICLEAUDIOCONTEXTFLAG_SYSTEM_SOUND_FLAG = 0x400
+VEHICLEAUDIOCONTEXTFLAG_RADIO_FLAG = 0x800
+VEHICLEAUDIOCONTEXTFLAG_EXT_SOURCE_FLAG = 0x1000
 
-# VehicleApPowerStateIndex
-VEHICLEAPPOWERSTATEINDEX_STATE = 0x0
-VEHICLEAPPOWERSTATEINDEX_ADDITIONAL = 0x1
+# VehicleAudioFocusIndex
+VEHICLEAUDIOFOCUSINDEX_FOCUS = 0x0
+VEHICLEAUDIOFOCUSINDEX_STREAMS = 0x1
+VEHICLEAUDIOFOCUSINDEX_EXTERNAL_FOCUS_STATE = 0x2
+VEHICLEAUDIOFOCUSINDEX_AUDIO_CONTEXTS = 0x3
+
+# Obd2CommonIgnitionMonitors
+OBD2COMMONIGNITIONMONITORS_COMPONENTS_AVAILABLE = 0x1
+OBD2COMMONIGNITIONMONITORS_COMPONENTS_INCOMPLETE = 0x2
+OBD2COMMONIGNITIONMONITORS_FUEL_SYSTEM_AVAILABLE = 0x4
+OBD2COMMONIGNITIONMONITORS_FUEL_SYSTEM_INCOMPLETE = 0x8
+OBD2COMMONIGNITIONMONITORS_MISFIRE_AVAILABLE = 0x10
+OBD2COMMONIGNITIONMONITORS_MISFIRE_INCOMPLETE = 0x20
+
+# Obd2FuelType
+OBD2FUELTYPE_NOT_AVAILABLE = 0x0
+OBD2FUELTYPE_GASOLINE = 0x1
+OBD2FUELTYPE_METHANOL = 0x2
+OBD2FUELTYPE_ETHANOL = 0x3
+OBD2FUELTYPE_DIESEL = 0x4
+OBD2FUELTYPE_LPG = 0x5
+OBD2FUELTYPE_CNG = 0x6
+OBD2FUELTYPE_PROPANE = 0x7
+OBD2FUELTYPE_ELECTRIC = 0x8
+OBD2FUELTYPE_BIFUEL_RUNNING_GASOLINE = 0x9
+OBD2FUELTYPE_BIFUEL_RUNNING_METHANOL = 0xa
+OBD2FUELTYPE_BIFUEL_RUNNING_ETHANOL = 0xb
+OBD2FUELTYPE_BIFUEL_RUNNING_LPG = 0xc
+OBD2FUELTYPE_BIFUEL_RUNNING_CNG = 0xd
+OBD2FUELTYPE_BIFUEL_RUNNING_PROPANE = 0xe
+OBD2FUELTYPE_BIFUEL_RUNNING_ELECTRIC = 0xf
+OBD2FUELTYPE_BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 0x10
+OBD2FUELTYPE_HYBRID_GASOLINE = 0x11
+OBD2FUELTYPE_HYBRID_ETHANOL = 0x12
+OBD2FUELTYPE_HYBRID_DIESEL = 0x13
+OBD2FUELTYPE_HYBRID_ELECTRIC = 0x14
+OBD2FUELTYPE_HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 0x15
+OBD2FUELTYPE_HYBRID_REGENERATIVE = 0x16
+OBD2FUELTYPE_BIFUEL_RUNNING_DIESEL = 0x17
+
+# VehicleAudioVolumeState
+VEHICLEAUDIOVOLUMESTATE_STATE_OK = 0x0
+VEHICLEAUDIOVOLUMESTATE_LIMIT_REACHED = 0x1
+
+# VmsMessageWithLayerIntegerValuesIndex
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_TYPE = 0x1
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_SUBTYPE = 0x2
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_VERSION = 0x3
+
+# Obd2SparkIgnitionMonitors
+OBD2SPARKIGNITIONMONITORS_EGR_AVAILABLE = 0x40
+OBD2SPARKIGNITIONMONITORS_EGR_INCOMPLETE = 0x80
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_AVAILABLE = 0x100
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x200
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_AVAILABLE = 0x400
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_INCOMPLETE = 0x800
+OBD2SPARKIGNITIONMONITORS_AC_REFRIGERANT_AVAILABLE = 0x1000
+OBD2SPARKIGNITIONMONITORS_AC_REFRIGERANT_INCOMPLETE = 0x2000
+OBD2SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_AVAILABLE = 0x4000
+OBD2SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x8000
+OBD2SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_AVAILABLE = 0x10000
+OBD2SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_INCOMPLETE = 0x20000
+OBD2SPARKIGNITIONMONITORS_HEATED_CATALYST_AVAILABLE = 0x40000
+OBD2SPARKIGNITIONMONITORS_HEATED_CATALYST_INCOMPLETE = 0x80000
+OBD2SPARKIGNITIONMONITORS_CATALYST_AVAILABLE = 0x100000
+OBD2SPARKIGNITIONMONITORS_CATALYST_INCOMPLETE = 0x200000
+
+# VehicleArea
+VEHICLEAREA_GLOBAL = 0x1000000
+VEHICLEAREA_ZONE = 0x2000000
+VEHICLEAREA_WINDOW = 0x3000000
+VEHICLEAREA_MIRROR = 0x4000000
+VEHICLEAREA_SEAT = 0x5000000
+VEHICLEAREA_DOOR = 0x6000000
+VEHICLEAREA_MASK = 0xf000000
+
+# Obd2SecondaryAirStatus
+OBD2SECONDARYAIRSTATUS_UPSTREAM = 0x1
+OBD2SECONDARYAIRSTATUS_DOWNSTREAM_OF_CATALYCIC_CONVERTER = 0x2
+OBD2SECONDARYAIRSTATUS_FROM_OUTSIDE_OR_OFF = 0x4
+OBD2SECONDARYAIRSTATUS_PUMP_ON_FOR_DIAGNOSTICS = 0x8
+
+# VehicleAudioHwVariantConfigFlag
+VEHICLEAUDIOHWVARIANTCONFIGFLAG_INTERNAL_RADIO_FLAG = 0x1
+
+# VmsMessageWithLayerAndPublisherIdIntegerValuesIndex
+VMSMESSAGEWITHLAYERANDPUBLISHERIDINTEGERVALUESINDEX_PUBLISHER_ID = 0x4
+
+# Obd2CompressionIgnitionMonitors
+OBD2COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_AVAILABLE = 0x40
+OBD2COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_INCOMPLETE = 0x80
+OBD2COMPRESSIONIGNITIONMONITORS_PM_FILTER_AVAILABLE = 0x100
+OBD2COMPRESSIONIGNITIONMONITORS_PM_FILTER_INCOMPLETE = 0x200
+OBD2COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_AVAILABLE = 0x400
+OBD2COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_INCOMPLETE = 0x800
+OBD2COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_AVAILABLE = 0x1000
+OBD2COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_INCOMPLETE = 0x2000
+OBD2COMPRESSIONIGNITIONMONITORS_NOx_SCR_AVAILABLE = 0x4000
+OBD2COMPRESSIONIGNITIONMONITORS_NOx_SCR_INCOMPLETE = 0x8000
+OBD2COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_AVAILABLE = 0x10000
+OBD2COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_INCOMPLETE = 0x20000
+
+# VmsOfferingMessageIntegerValuesIndex
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_PUBLISHER_ID = 0x1
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_NUMBER_OF_OFFERS = 0x2
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_OFFERING_START = 0x3
+
+# VehicleAreaZone
+VEHICLEAREAZONE_ROW_1_LEFT = 0x1
+VEHICLEAREAZONE_ROW_1_CENTER = 0x2
+VEHICLEAREAZONE_ROW_1_RIGHT = 0x4
+VEHICLEAREAZONE_ROW_1 = 0x8
+VEHICLEAREAZONE_ROW_2_LEFT = 0x10
+VEHICLEAREAZONE_ROW_2_CENTER = 0x20
+VEHICLEAREAZONE_ROW_2_RIGHT = 0x40
+VEHICLEAREAZONE_ROW_2 = 0x80
+VEHICLEAREAZONE_ROW_3_LEFT = 0x100
+VEHICLEAREAZONE_ROW_3_CENTER = 0x200
+VEHICLEAREAZONE_ROW_3_RIGHT = 0x400
+VEHICLEAREAZONE_ROW_3 = 0x800
+VEHICLEAREAZONE_ROW_4_LEFT = 0x1000
+VEHICLEAREAZONE_ROW_4_CENTER = 0x2000
+VEHICLEAREAZONE_ROW_4_RIGHT = 0x4000
+VEHICLEAREAZONE_ROW_4 = 0x8000
+VEHICLEAREAZONE_WHOLE_CABIN = 0x80000000
+
+# Obd2IgnitionMonitorKind
+OBD2IGNITIONMONITORKIND_SPARK = 0x0
+OBD2IGNITIONMONITORKIND_COMPRESSION = 0x1
+
+# VmsAvailabilityStateIntegerValuesIndex
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_SEQUENCE_NUMBER = 0x1
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS = 0x2
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_LAYERS_START = 0x3
+
+# VehicleApPowerStateConfigFlag
+VEHICLEAPPOWERSTATECONFIGFLAG_ENABLE_DEEP_SLEEP_FLAG = 0x1
+VEHICLEAPPOWERSTATECONFIGFLAG_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2
+
+# VehicleHvacFanDirection
+VEHICLEHVACFANDIRECTION_FACE = 0x1
+VEHICLEHVACFANDIRECTION_FLOOR = 0x2
+VEHICLEHVACFANDIRECTION_FACE_AND_FLOOR = 0x3
+VEHICLEHVACFANDIRECTION_DEFROST = 0x4
+VEHICLEHVACFANDIRECTION_DEFROST_AND_FLOOR = 0x5
+
+# VehicleHwKeyInputAction
+VEHICLEHWKEYINPUTACTION_ACTION_DOWN = 0x0
+VEHICLEHWKEYINPUTACTION_ACTION_UP = 0x1
+
+# Wheel
+WHEEL_UNKNOWN = 0x0
+WHEEL_LEFT_FRONT = 0x1
+WHEEL_RIGHT_FRONT = 0x2
+WHEEL_LEFT_REAR = 0x4
+WHEEL_RIGHT_REAR = 0x8
+
+# VehicleAudioVolumeCapabilityFlag
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_PERSISTENT_STORAGE = 0x1
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_MASTER_VOLUME_ONLY = 0x2
+
+# VehicleAudioVolumeIndex
+VEHICLEAUDIOVOLUMEINDEX_INDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMEINDEX_INDEX_VOLUME = 0x1
+VEHICLEAUDIOVOLUMEINDEX_INDEX_STATE = 0x2
+
+# VehicleRadioConstants
+VEHICLERADIOCONSTANTS_VEHICLE_RADIO_PRESET_MIN_VALUE = 0x1
+
+# VehicleGear
+VEHICLEGEAR_GEAR_NEUTRAL = 0x1
+VEHICLEGEAR_GEAR_REVERSE = 0x2
+VEHICLEGEAR_GEAR_PARK = 0x4
+VEHICLEGEAR_GEAR_DRIVE = 0x8
+VEHICLEGEAR_GEAR_LOW = 0x10
+VEHICLEGEAR_GEAR_1 = 0x10
+VEHICLEGEAR_GEAR_2 = 0x20
+VEHICLEGEAR_GEAR_3 = 0x40
+VEHICLEGEAR_GEAR_4 = 0x80
+VEHICLEGEAR_GEAR_5 = 0x100
+VEHICLEGEAR_GEAR_6 = 0x200
+VEHICLEGEAR_GEAR_7 = 0x400
+VEHICLEGEAR_GEAR_8 = 0x800
+VEHICLEGEAR_GEAR_9 = 0x1000
+
+# VehicleDrivingStatus
+VEHICLEDRIVINGSTATUS_UNRESTRICTED = 0x0
+VEHICLEDRIVINGSTATUS_NO_VIDEO = 0x1
+VEHICLEDRIVINGSTATUS_NO_KEYBOARD_INPUT = 0x2
+VEHICLEDRIVINGSTATUS_NO_VOICE_INPUT = 0x4
+VEHICLEDRIVINGSTATUS_NO_CONFIG = 0x8
+VEHICLEDRIVINGSTATUS_LIMIT_MESSAGE_LEN = 0x10
+
+# VehicleAudioRoutingPolicyIndex
+VEHICLEAUDIOROUTINGPOLICYINDEX_STREAM = 0x0
+VEHICLEAUDIOROUTINGPOLICYINDEX_CONTEXTS = 0x1
+
+# VmsSubscriptionsStateIntegerValuesIndex
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_SEQUENCE_NUMBER = 0x1
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_NUMBER_OF_LAYERS = 0x2
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS = 0x3
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_SUBSCRIPTIONS_START = 0x4
+
+# SubscribeFlags
+SUBSCRIBEFLAGS_UNDEFINED = 0x0
+SUBSCRIBEFLAGS_HAL_EVENT = 0x1
+SUBSCRIBEFLAGS_SET_CALL = 0x2
+SUBSCRIBEFLAGS_DEFAULT = 0x1
+
+# VehicleAudioExtFocusFlag
+VEHICLEAUDIOEXTFOCUSFLAG_NONE_FLAG = 0x0
+VEHICLEAUDIOEXTFOCUSFLAG_PERMANENT_FLAG = 0x1
+VEHICLEAUDIOEXTFOCUSFLAG_TRANSIENT_FLAG = 0x2
+VEHICLEAUDIOEXTFOCUSFLAG_PLAY_ONLY_FLAG = 0x4
+VEHICLEAUDIOEXTFOCUSFLAG_MUTE_MEDIA_FLAG = 0x8
 
 # VehicleAudioFocusRequest
 VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN = 0x1
@@ -35,18 +253,197 @@
 VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_NO_DUCK = 0x4
 VEHICLEAUDIOFOCUSREQUEST_REQUEST_RELEASE = 0x5
 
-# VehicleDisplay
-VEHICLEDISPLAY_MAIN = 0x0
-VEHICLEDISPLAY_INSTRUMENT_CLUSTER = 0x1
+# VehiclePropertyType
+VEHICLEPROPERTYTYPE_STRING = 0x100000
+VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
+VEHICLEPROPERTYTYPE_INT32 = 0x400000
+VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
+VEHICLEPROPERTYTYPE_INT64 = 0x500000
+VEHICLEPROPERTYTYPE_FLOAT = 0x600000
+VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
+VEHICLEPROPERTYTYPE_BYTES = 0x700000
+VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
+VEHICLEPROPERTYTYPE_MASK = 0xff0000
 
-# VehicleRadioConstants
-VEHICLERADIOCONSTANTS_VEHICLE_RADIO_PRESET_MIN_VALUE = 0x1
+# VehiclePropertyAccess
+VEHICLEPROPERTYACCESS_NONE = 0x0
+VEHICLEPROPERTYACCESS_READ = 0x1
+VEHICLEPROPERTYACCESS_WRITE = 0x2
+VEHICLEPROPERTYACCESS_READ_WRITE = 0x3
 
-# VehicleAudioFocusIndex
-VEHICLEAUDIOFOCUSINDEX_FOCUS = 0x0
-VEHICLEAUDIOFOCUSINDEX_STREAMS = 0x1
-VEHICLEAUDIOFOCUSINDEX_EXTERNAL_FOCUS_STATE = 0x2
-VEHICLEAUDIOFOCUSINDEX_AUDIO_CONTEXTS = 0x3
+# Obd2FuelSystemStatus
+OBD2FUELSYSTEMSTATUS_OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 0x1
+OBD2FUELSYSTEMSTATUS_CLOSED_LOOP = 0x2
+OBD2FUELSYSTEMSTATUS_OPEN_ENGINE_LOAD_OR_DECELERATION = 0x4
+OBD2FUELSYSTEMSTATUS_OPEN_SYSTEM_FAILURE = 0x8
+OBD2FUELSYSTEMSTATUS_CLOSED_LOOP_BUT_FEEDBACK_FAULT = 0x10
+
+# VehicleTurnSignal
+VEHICLETURNSIGNAL_NONE = 0x0
+VEHICLETURNSIGNAL_RIGHT = 0x1
+VEHICLETURNSIGNAL_LEFT = 0x2
+VEHICLETURNSIGNAL_EMERGENCY = 0x4
+
+# VehicleAudioStreamFlag
+VEHICLEAUDIOSTREAMFLAG_STREAM0_FLAG = 0x1
+VEHICLEAUDIOSTREAMFLAG_STREAM1_FLAG = 0x2
+VEHICLEAUDIOSTREAMFLAG_STREAM2_FLAG = 0x4
+
+# VehicleApPowerBootupReason
+VEHICLEAPPOWERBOOTUPREASON_USER_POWER_ON = 0x0
+VEHICLEAPPOWERBOOTUPREASON_USER_UNLOCK = 0x1
+VEHICLEAPPOWERBOOTUPREASON_TIMER = 0x2
+
+# VehicleAudioFocusState
+VEHICLEAUDIOFOCUSSTATE_STATE_GAIN = 0x1
+VEHICLEAUDIOFOCUSSTATE_STATE_GAIN_TRANSIENT = 0x2
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_CAN_DUCK = 0x3
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT = 0x4
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS = 0x5
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_EXLCUSIVE = 0x6
+
+# VehicleAudioStream
+VEHICLEAUDIOSTREAM_STREAM0 = 0x0
+VEHICLEAUDIOSTREAM_STREAM1 = 0x1
+
+# DiagnosticFloatSensorIndex
+DIAGNOSTICFLOATSENSORINDEX_CALCULATED_ENGINE_LOAD = 0x0
+DIAGNOSTICFLOATSENSORINDEX_ENGINE_COOLANT_TEMPERATURE = 0x1
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK1 = 0x2
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK1 = 0x3
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK2 = 0x4
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK2 = 0x5
+DIAGNOSTICFLOATSENSORINDEX_FUEL_PRESSURE = 0x6
+DIAGNOSTICFLOATSENSORINDEX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x7
+DIAGNOSTICFLOATSENSORINDEX_ENGINE_RPM = 0x8
+DIAGNOSTICFLOATSENSORINDEX_VEHICLE_SPEED = 0x9
+DIAGNOSTICFLOATSENSORINDEX_TIMING_ADVANCE = 0xa
+DIAGNOSTICFLOATSENSORINDEX_MAF_AIR_FLOW_RATE = 0xb
+DIAGNOSTICFLOATSENSORINDEX_THROTTLE_POSITION = 0xc
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR1_VOLTAGE = 0xd
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 0xe
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 0xf
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR2_VOLTAGE = 0x10
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 0x11
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 0x12
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR3_VOLTAGE = 0x13
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 0x14
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 0x15
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR4_VOLTAGE = 0x16
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 0x17
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 0x18
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR5_VOLTAGE = 0x19
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 0x1a
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 0x1b
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR6_VOLTAGE = 0x1c
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 0x1d
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 0x1e
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR7_VOLTAGE = 0x1f
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 0x20
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 0x21
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR8_VOLTAGE = 0x22
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 0x23
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 0x24
+DIAGNOSTICFLOATSENSORINDEX_FUEL_RAIL_PRESSURE = 0x25
+DIAGNOSTICFLOATSENSORINDEX_FUEL_RAIL_GAUGE_PRESSURE = 0x26
+DIAGNOSTICFLOATSENSORINDEX_COMMANDED_EXHAUST_GAS_RECIRCULATION = 0x27
+DIAGNOSTICFLOATSENSORINDEX_EXHAUST_GAS_RECIRCULATION_ERROR = 0x28
+DIAGNOSTICFLOATSENSORINDEX_COMMANDED_EVAPORATIVE_PURGE = 0x29
+DIAGNOSTICFLOATSENSORINDEX_FUEL_TANK_LEVEL_INPUT = 0x2a
+DIAGNOSTICFLOATSENSORINDEX_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x2b
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 0x2c
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 0x2d
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 0x2e
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 0x2f
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_LOAD_VALUE = 0x30
+DIAGNOSTICFLOATSENSORINDEX_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 0x31
+DIAGNOSTICFLOATSENSORINDEX_RELATIVE_THROTTLE_POSITION = 0x32
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_B = 0x33
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_C = 0x34
+DIAGNOSTICFLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_D = 0x35
+DIAGNOSTICFLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_E = 0x36
+DIAGNOSTICFLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_F = 0x37
+DIAGNOSTICFLOATSENSORINDEX_COMMANDED_THROTTLE_ACTUATOR = 0x38
+DIAGNOSTICFLOATSENSORINDEX_ETHANOL_FUEL_PERCENTAGE = 0x39
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x3a
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3b
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x3c
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x3d
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x3e
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3f
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x40
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x41
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x42
+DIAGNOSTICFLOATSENSORINDEX_RELATIVE_ACCELERATOR_PEDAL_POSITION = 0x43
+DIAGNOSTICFLOATSENSORINDEX_HYBRID_BATTERY_PACK_REMAINING_LIFE = 0x44
+DIAGNOSTICFLOATSENSORINDEX_FUEL_INJECTION_TIMING = 0x45
+DIAGNOSTICFLOATSENSORINDEX_ENGINE_FUEL_RATE = 0x46
+DIAGNOSTICFLOATSENSORINDEX_LAST_SYSTEM_INDEX = 0x46
+
+# VehicleAreaMirror
+VEHICLEAREAMIRROR_DRIVER_LEFT = 0x1
+VEHICLEAREAMIRROR_DRIVER_RIGHT = 0x2
+VEHICLEAREAMIRROR_DRIVER_CENTER = 0x4
+
+# VehicleAreaWindow
+VEHICLEAREAWINDOW_FRONT_WINDSHIELD = 0x1
+VEHICLEAREAWINDOW_REAR_WINDSHIELD = 0x2
+VEHICLEAREAWINDOW_ROOF_TOP = 0x4
+VEHICLEAREAWINDOW_ROW_1_LEFT = 0x10
+VEHICLEAREAWINDOW_ROW_1_RIGHT = 0x20
+VEHICLEAREAWINDOW_ROW_2_LEFT = 0x100
+VEHICLEAREAWINDOW_ROW_2_RIGHT = 0x200
+VEHICLEAREAWINDOW_ROW_3_LEFT = 0x1000
+VEHICLEAREAWINDOW_ROW_3_RIGHT = 0x2000
+
+# VehiclePropertyOperation
+VEHICLEPROPERTYOPERATION_GENERIC = 0x0
+VEHICLEPROPERTYOPERATION_SET = 0x1
+VEHICLEPROPERTYOPERATION_GET = 0x2
+VEHICLEPROPERTYOPERATION_SUBSCRIBE = 0x3
+
+# VehicleAreaDoor
+VEHICLEAREADOOR_ROW_1_LEFT = 0x1
+VEHICLEAREADOOR_ROW_1_RIGHT = 0x4
+VEHICLEAREADOOR_ROW_2_LEFT = 0x10
+VEHICLEAREADOOR_ROW_2_RIGHT = 0x40
+VEHICLEAREADOOR_ROW_3_LEFT = 0x100
+VEHICLEAREADOOR_ROW_3_RIGHT = 0x400
+VEHICLEAREADOOR_HOOD = 0x10000000
+VEHICLEAREADOOR_REAR = 0x20000000
+
+# StatusCode
+STATUSCODE_OK = 0x0
+STATUSCODE_TRY_AGAIN = 0x1
+STATUSCODE_INVALID_ARG = 0x2
+STATUSCODE_NOT_AVAILABLE = 0x3
+STATUSCODE_ACCESS_DENIED = 0x4
+STATUSCODE_INTERNAL_ERROR = 0x5
+
+# VehicleAreaSeat
+VEHICLEAREASEAT_ROW_1_LEFT = 0x1
+VEHICLEAREASEAT_ROW_1_CENTER = 0x2
+VEHICLEAREASEAT_ROW_1_RIGHT = 0x4
+VEHICLEAREASEAT_ROW_2_LEFT = 0x10
+VEHICLEAREASEAT_ROW_2_CENTER = 0x20
+VEHICLEAREASEAT_ROW_2_RIGHT = 0x40
+VEHICLEAREASEAT_ROW_3_LEFT = 0x100
+VEHICLEAREASEAT_ROW_3_CENTER = 0x200
+VEHICLEAREASEAT_ROW_3_RIGHT = 0x400
+
+# VmsMessageType
+VMSMESSAGETYPE_SUBSCRIBE = 0x1
+VMSMESSAGETYPE_SUBSCRIBE_TO_PUBLISHER = 0x2
+VMSMESSAGETYPE_UNSUBSCRIBE = 0x3
+VMSMESSAGETYPE_UNSUBSCRIBE_TO_PUBLISHER = 0x4
+VMSMESSAGETYPE_OFFERING = 0x5
+VMSMESSAGETYPE_AVAILABILITY_REQUEST = 0x6
+VMSMESSAGETYPE_SUBSCRIPTIONS_REQUEST = 0x7
+VMSMESSAGETYPE_AVAILABILITY_RESPONSE = 0x8
+VMSMESSAGETYPE_AVAILABILITY_CHANGE = 0x9
+VMSMESSAGETYPE_SUBSCRIPTIONS_RESPONSE = 0xa
+VMSMESSAGETYPE_SUBSCRIPTIONS_CHANGE = 0xb
+VMSMESSAGETYPE_DATA = 0xc
 
 # VehicleProperty
 VEHICLEPROPERTY_INVALID = 0x0
@@ -147,59 +544,75 @@
 VEHICLEPROPERTY_WINDOW_VENT_POS = 0x11400bc2
 VEHICLEPROPERTY_WINDOW_VENT_MOVE = 0x11400bc3
 VEHICLEPROPERTY_WINDOW_LOCK = 0x11200bc4
+VEHICLEPROPERTY_WHEEL_TICK = 0x11e00306
+VEHICLEPROPERTY_ABS_ACTIVE = 0x1120040a
+VEHICLEPROPERTY_TRACTION_CONTROL_ACTIVE = 0x1120040b
+VEHICLEPROPERTY_HVAC_AUTO_RECIRC_ON = 0x12200512
+VEHICLEPROPERTY_VEHICLE_MAP_SERVICE = 0x11e00c00
+VEHICLEPROPERTY_OBD2_LIVE_FRAME = 0x11e00d00
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME = 0x11e00d01
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO = 0x11e00d02
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR = 0x11e00d03
 
-# VehicleAreaZone
-VEHICLEAREAZONE_ROW_1_LEFT = 0x1
-VEHICLEAREAZONE_ROW_1_CENTER = 0x2
-VEHICLEAREAZONE_ROW_1_RIGHT = 0x4
-VEHICLEAREAZONE_ROW_1 = 0x8
-VEHICLEAREAZONE_ROW_2_LEFT = 0x10
-VEHICLEAREAZONE_ROW_2_CENTER = 0x20
-VEHICLEAREAZONE_ROW_2_RIGHT = 0x40
-VEHICLEAREAZONE_ROW_2 = 0x80
-VEHICLEAREAZONE_ROW_3_LEFT = 0x100
-VEHICLEAREAZONE_ROW_3_CENTER = 0x200
-VEHICLEAREAZONE_ROW_3_RIGHT = 0x400
-VEHICLEAREAZONE_ROW_3 = 0x800
-VEHICLEAREAZONE_ROW_4_LEFT = 0x1000
-VEHICLEAREAZONE_ROW_4_CENTER = 0x2000
-VEHICLEAREAZONE_ROW_4_RIGHT = 0x4000
-VEHICLEAREAZONE_ROW_4 = 0x8000
-VEHICLEAREAZONE_WHOLE_CABIN = 0x80000000
-
-# SubscribeFlags
-SUBSCRIBEFLAGS_UNDEFINED = 0x0
-SUBSCRIBEFLAGS_HAL_EVENT = 0x1
-SUBSCRIBEFLAGS_SET_CALL = 0x2
-SUBSCRIBEFLAGS_DEFAULT = 0x1
-
-# Wheel
-WHEEL_UNKNOWN = 0x0
-WHEEL_LEFT_FRONT = 0x1
-WHEEL_RIGHT_FRONT = 0x2
-WHEEL_LEFT_REAR = 0x4
-WHEEL_RIGHT_REAR = 0x8
-
-# StatusCode
-STATUSCODE_OK = 0x0
-STATUSCODE_TRY_AGAIN = 0x1
-STATUSCODE_INVALID_ARG = 0x2
-STATUSCODE_NOT_AVAILABLE = 0x3
-STATUSCODE_ACCESS_DENIED = 0x4
-STATUSCODE_INTERNAL_ERROR = 0x5
-
-# VehicleAudioHwVariantConfigFlag
-VEHICLEAUDIOHWVARIANTCONFIGFLAG_INTERNAL_RADIO_FLAG = 0x1
+# VehicleIgnitionState
+VEHICLEIGNITIONSTATE_UNDEFINED = 0x0
+VEHICLEIGNITIONSTATE_LOCK = 0x1
+VEHICLEIGNITIONSTATE_OFF = 0x2
+VEHICLEIGNITIONSTATE_ACC = 0x3
+VEHICLEIGNITIONSTATE_ON = 0x4
+VEHICLEIGNITIONSTATE_START = 0x5
 
 # VehiclePropertyGroup
 VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
 VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
 VEHICLEPROPERTYGROUP_MASK = 0xf0000000
 
-# VehicleAudioStreamFlag
-VEHICLEAUDIOSTREAMFLAG_STREAM0_FLAG = 0x1
-VEHICLEAUDIOSTREAMFLAG_STREAM1_FLAG = 0x2
-VEHICLEAUDIOSTREAMFLAG_STREAM2_FLAG = 0x4
+# VehicleApPowerSetState
+VEHICLEAPPOWERSETSTATE_BOOT_COMPLETE = 0x1
+VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_ENTRY = 0x2
+VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_EXIT = 0x3
+VEHICLEAPPOWERSETSTATE_SHUTDOWN_POSTPONE = 0x4
+VEHICLEAPPOWERSETSTATE_SHUTDOWN_START = 0x5
+VEHICLEAPPOWERSETSTATE_DISPLAY_OFF = 0x6
+VEHICLEAPPOWERSETSTATE_DISPLAY_ON = 0x7
+
+# VmsBaseMessageIntegerValuesIndex
+VMSBASEMESSAGEINTEGERVALUESINDEX_MESSAGE_TYPE = 0x0
+
+# DiagnosticIntegerSensorIndex
+DIAGNOSTICINTEGERSENSORINDEX_FUEL_SYSTEM_STATUS = 0x0
+DIAGNOSTICINTEGERSENSORINDEX_MALFUNCTION_INDICATOR_LIGHT_ON = 0x1
+DIAGNOSTICINTEGERSENSORINDEX_IGNITION_MONITORS_SUPPORTED = 0x2
+DIAGNOSTICINTEGERSENSORINDEX_IGNITION_SPECIFIC_MONITORS = 0x3
+DIAGNOSTICINTEGERSENSORINDEX_INTAKE_AIR_TEMPERATURE = 0x4
+DIAGNOSTICINTEGERSENSORINDEX_COMMANDED_SECONDARY_AIR_STATUS = 0x5
+DIAGNOSTICINTEGERSENSORINDEX_NUM_OXYGEN_SENSORS_PRESENT = 0x6
+DIAGNOSTICINTEGERSENSORINDEX_RUNTIME_SINCE_ENGINE_START = 0x7
+DIAGNOSTICINTEGERSENSORINDEX_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 0x8
+DIAGNOSTICINTEGERSENSORINDEX_WARMUPS_SINCE_CODES_CLEARED = 0x9
+DIAGNOSTICINTEGERSENSORINDEX_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 0xa
+DIAGNOSTICINTEGERSENSORINDEX_ABSOLUTE_BAROMETRIC_PRESSURE = 0xb
+DIAGNOSTICINTEGERSENSORINDEX_CONTROL_MODULE_VOLTAGE = 0xc
+DIAGNOSTICINTEGERSENSORINDEX_AMBIENT_AIR_TEMPERATURE = 0xd
+DIAGNOSTICINTEGERSENSORINDEX_TIME_WITH_MALFUNCTION_LIGHT_ON = 0xe
+DIAGNOSTICINTEGERSENSORINDEX_TIME_SINCE_TROUBLE_CODES_CLEARED = 0xf
+DIAGNOSTICINTEGERSENSORINDEX_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 0x10
+DIAGNOSTICINTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_VOLTAGE = 0x11
+DIAGNOSTICINTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_CURRENT = 0x12
+DIAGNOSTICINTEGERSENSORINDEX_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x13
+DIAGNOSTICINTEGERSENSORINDEX_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 0x14
+DIAGNOSTICINTEGERSENSORINDEX_FUEL_TYPE = 0x15
+DIAGNOSTICINTEGERSENSORINDEX_FUEL_RAIL_ABSOLUTE_PRESSURE = 0x16
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_OIL_TEMPERATURE = 0x17
+DIAGNOSTICINTEGERSENSORINDEX_DRIVER_DEMAND_PERCENT_TORQUE = 0x18
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_ACTUAL_PERCENT_TORQUE = 0x19
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_REFERENCE_PERCENT_TORQUE = 0x1a
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_IDLE = 0x1b
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 0x1c
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 0x1d
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 0x1e
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 0x1f
+DIAGNOSTICINTEGERSENSORINDEX_LAST_SYSTEM_INDEX = 0x1f
 
 # VehiclePropertyChangeMode
 VEHICLEPROPERTYCHANGEMODE_STATIC = 0x0
@@ -208,21 +621,17 @@
 VEHICLEPROPERTYCHANGEMODE_POLL = 0x3
 VEHICLEPROPERTYCHANGEMODE_ON_SET = 0x4
 
-# VehicleAreaSeat
-VEHICLEAREASEAT_ROW_1_LEFT = 0x1
-VEHICLEAREASEAT_ROW_1_CENTER = 0x2
-VEHICLEAREASEAT_ROW_1_RIGHT = 0x4
-VEHICLEAREASEAT_ROW_2_LEFT = 0x10
-VEHICLEAREASEAT_ROW_2_CENTER = 0x20
-VEHICLEAREASEAT_ROW_2_RIGHT = 0x40
-VEHICLEAREASEAT_ROW_3_LEFT = 0x100
-VEHICLEAREASEAT_ROW_3_CENTER = 0x200
-VEHICLEAREASEAT_ROW_3_RIGHT = 0x400
+# VehicleApPowerState
+VEHICLEAPPOWERSTATE_OFF = 0x0
+VEHICLEAPPOWERSTATE_DEEP_SLEEP = 0x1
+VEHICLEAPPOWERSTATE_ON_DISP_OFF = 0x2
+VEHICLEAPPOWERSTATE_ON_FULL = 0x3
+VEHICLEAPPOWERSTATE_SHUTDOWN_PREPARE = 0x4
 
-# VehicleAudioVolumeIndex
-VEHICLEAUDIOVOLUMEINDEX_INDEX_STREAM = 0x0
-VEHICLEAUDIOVOLUMEINDEX_INDEX_VOLUME = 0x1
-VEHICLEAUDIOVOLUMEINDEX_INDEX_STATE = 0x2
+# VehicleInstrumentClusterType
+VEHICLEINSTRUMENTCLUSTERTYPE_NONE = 0x0
+VEHICLEINSTRUMENTCLUSTERTYPE_HAL_INTERFACE = 0x1
+VEHICLEINSTRUMENTCLUSTERTYPE_EXTERNAL_DISPLAY = 0x2
 
 # VehicleUnit
 VEHICLEUNIT_SHOULD_NOT_USE = 0x0
@@ -241,189 +650,22 @@
 VEHICLEUNIT_SECS = 0x53
 VEHICLEUNIT_YEAR = 0x59
 
-# VehicleAreaMirror
-VEHICLEAREAMIRROR_DRIVER_LEFT = 0x1
-VEHICLEAREAMIRROR_DRIVER_RIGHT = 0x2
-VEHICLEAREAMIRROR_DRIVER_CENTER = 0x4
-
-# VehiclePropertyAccess
-VEHICLEPROPERTYACCESS_NONE = 0x0
-VEHICLEPROPERTYACCESS_READ = 0x1
-VEHICLEPROPERTYACCESS_WRITE = 0x2
-VEHICLEPROPERTYACCESS_READ_WRITE = 0x3
-
-# VehicleAudioContextFlag
-VEHICLEAUDIOCONTEXTFLAG_MUSIC_FLAG = 0x1
-VEHICLEAUDIOCONTEXTFLAG_NAVIGATION_FLAG = 0x2
-VEHICLEAUDIOCONTEXTFLAG_VOICE_COMMAND_FLAG = 0x4
-VEHICLEAUDIOCONTEXTFLAG_CALL_FLAG = 0x8
-VEHICLEAUDIOCONTEXTFLAG_ALARM_FLAG = 0x10
-VEHICLEAUDIOCONTEXTFLAG_NOTIFICATION_FLAG = 0x20
-VEHICLEAUDIOCONTEXTFLAG_UNKNOWN_FLAG = 0x40
-VEHICLEAUDIOCONTEXTFLAG_SAFETY_ALERT_FLAG = 0x80
-VEHICLEAUDIOCONTEXTFLAG_CD_ROM_FLAG = 0x100
-VEHICLEAUDIOCONTEXTFLAG_AUX_AUDIO_FLAG = 0x200
-VEHICLEAUDIOCONTEXTFLAG_SYSTEM_SOUND_FLAG = 0x400
-VEHICLEAUDIOCONTEXTFLAG_RADIO_FLAG = 0x800
-VEHICLEAUDIOCONTEXTFLAG_EXT_SOURCE_FLAG = 0x1000
-
-# VehicleDrivingStatus
-VEHICLEDRIVINGSTATUS_UNRESTRICTED = 0x0
-VEHICLEDRIVINGSTATUS_NO_VIDEO = 0x1
-VEHICLEDRIVINGSTATUS_NO_KEYBOARD_INPUT = 0x2
-VEHICLEDRIVINGSTATUS_NO_VOICE_INPUT = 0x4
-VEHICLEDRIVINGSTATUS_NO_CONFIG = 0x8
-VEHICLEDRIVINGSTATUS_LIMIT_MESSAGE_LEN = 0x10
-
-# VehicleGear
-VEHICLEGEAR_GEAR_NEUTRAL = 0x1
-VEHICLEGEAR_GEAR_REVERSE = 0x2
-VEHICLEGEAR_GEAR_PARK = 0x4
-VEHICLEGEAR_GEAR_DRIVE = 0x8
-VEHICLEGEAR_GEAR_LOW = 0x10
-VEHICLEGEAR_GEAR_1 = 0x10
-VEHICLEGEAR_GEAR_2 = 0x20
-VEHICLEGEAR_GEAR_3 = 0x40
-VEHICLEGEAR_GEAR_4 = 0x80
-VEHICLEGEAR_GEAR_5 = 0x100
-VEHICLEGEAR_GEAR_6 = 0x200
-VEHICLEGEAR_GEAR_7 = 0x400
-VEHICLEGEAR_GEAR_8 = 0x800
-VEHICLEGEAR_GEAR_9 = 0x1000
-
-# VehicleTurnSignal
-VEHICLETURNSIGNAL_NONE = 0x0
-VEHICLETURNSIGNAL_RIGHT = 0x1
-VEHICLETURNSIGNAL_LEFT = 0x2
-VEHICLETURNSIGNAL_EMERGENCY = 0x4
+# VehicleAudioVolumeLimitIndex
+VEHICLEAUDIOVOLUMELIMITINDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMELIMITINDEX_MAX_VOLUME = 0x1
 
 # VehicleApPowerStateShutdownParam
 VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_IMMEDIATELY = 0x1
 VEHICLEAPPOWERSTATESHUTDOWNPARAM_CAN_SLEEP = 0x2
 VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_ONLY = 0x3
 
-# VehiclePropertyOperation
-VEHICLEPROPERTYOPERATION_GENERIC = 0x0
-VEHICLEPROPERTYOPERATION_SET = 0x1
-VEHICLEPROPERTYOPERATION_GET = 0x2
-VEHICLEPROPERTYOPERATION_SUBSCRIBE = 0x3
+# VehicleApPowerStateIndex
+VEHICLEAPPOWERSTATEINDEX_STATE = 0x0
+VEHICLEAPPOWERSTATEINDEX_ADDITIONAL = 0x1
 
-# VehiclePropertyType
-VEHICLEPROPERTYTYPE_STRING = 0x100000
-VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
-VEHICLEPROPERTYTYPE_INT32 = 0x400000
-VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
-VEHICLEPROPERTYTYPE_INT64 = 0x500000
-VEHICLEPROPERTYTYPE_FLOAT = 0x600000
-VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
-VEHICLEPROPERTYTYPE_BYTES = 0x700000
-VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
-VEHICLEPROPERTYTYPE_MASK = 0xff0000
-
-# VehicleAreaDoor
-VEHICLEAREADOOR_ROW_1_LEFT = 0x1
-VEHICLEAREADOOR_ROW_1_RIGHT = 0x4
-VEHICLEAREADOOR_ROW_2_LEFT = 0x10
-VEHICLEAREADOOR_ROW_2_RIGHT = 0x40
-VEHICLEAREADOOR_ROW_3_LEFT = 0x100
-VEHICLEAREADOOR_ROW_3_RIGHT = 0x400
-VEHICLEAREADOOR_HOOD = 0x10000000
-VEHICLEAREADOOR_REAR = 0x20000000
-
-# VehicleHwKeyInputAction
-VEHICLEHWKEYINPUTACTION_ACTION_DOWN = 0x0
-VEHICLEHWKEYINPUTACTION_ACTION_UP = 0x1
-
-# VehicleApPowerStateConfigFlag
-VEHICLEAPPOWERSTATECONFIGFLAG_ENABLE_DEEP_SLEEP_FLAG = 0x1
-VEHICLEAPPOWERSTATECONFIGFLAG_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2
-
-# VehicleIgnitionState
-VEHICLEIGNITIONSTATE_UNDEFINED = 0x0
-VEHICLEIGNITIONSTATE_LOCK = 0x1
-VEHICLEIGNITIONSTATE_OFF = 0x2
-VEHICLEIGNITIONSTATE_ACC = 0x3
-VEHICLEIGNITIONSTATE_ON = 0x4
-VEHICLEIGNITIONSTATE_START = 0x5
-
-# VehicleAudioVolumeLimitIndex
-VEHICLEAUDIOVOLUMELIMITINDEX_STREAM = 0x0
-VEHICLEAUDIOVOLUMELIMITINDEX_MAX_VOLUME = 0x1
-
-# VehicleAreaWindow
-VEHICLEAREAWINDOW_FRONT_WINDSHIELD = 0x1
-VEHICLEAREAWINDOW_REAR_WINDSHIELD = 0x2
-VEHICLEAREAWINDOW_ROOF_TOP = 0x4
-VEHICLEAREAWINDOW_ROW_1_LEFT = 0x10
-VEHICLEAREAWINDOW_ROW_1_RIGHT = 0x20
-VEHICLEAREAWINDOW_ROW_2_LEFT = 0x100
-VEHICLEAREAWINDOW_ROW_2_RIGHT = 0x200
-VEHICLEAREAWINDOW_ROW_3_LEFT = 0x1000
-VEHICLEAREAWINDOW_ROW_3_RIGHT = 0x2000
-
-# VehicleAudioFocusState
-VEHICLEAUDIOFOCUSSTATE_STATE_GAIN = 0x1
-VEHICLEAUDIOFOCUSSTATE_STATE_GAIN_TRANSIENT = 0x2
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_CAN_DUCK = 0x3
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT = 0x4
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS = 0x5
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_EXLCUSIVE = 0x6
-
-# VehicleAudioVolumeCapabilityFlag
-VEHICLEAUDIOVOLUMECAPABILITYFLAG_PERSISTENT_STORAGE = 0x1
-VEHICLEAUDIOVOLUMECAPABILITYFLAG_MASTER_VOLUME_ONLY = 0x2
-
-# VehicleApPowerState
-VEHICLEAPPOWERSTATE_OFF = 0x0
-VEHICLEAPPOWERSTATE_DEEP_SLEEP = 0x1
-VEHICLEAPPOWERSTATE_ON_DISP_OFF = 0x2
-VEHICLEAPPOWERSTATE_ON_FULL = 0x3
-VEHICLEAPPOWERSTATE_SHUTDOWN_PREPARE = 0x4
-
-# VehicleAudioVolumeState
-VEHICLEAUDIOVOLUMESTATE_STATE_OK = 0x0
-VEHICLEAUDIOVOLUMESTATE_LIMIT_REACHED = 0x1
-
-# VehicleAudioRoutingPolicyIndex
-VEHICLEAUDIOROUTINGPOLICYINDEX_STREAM = 0x0
-VEHICLEAUDIOROUTINGPOLICYINDEX_CONTEXTS = 0x1
-
-# VehicleAudioStream
-VEHICLEAUDIOSTREAM_STREAM0 = 0x0
-VEHICLEAUDIOSTREAM_STREAM1 = 0x1
-
-# VehicleInstrumentClusterType
-VEHICLEINSTRUMENTCLUSTERTYPE_NONE = 0x0
-VEHICLEINSTRUMENTCLUSTERTYPE_HAL_INTERFACE = 0x1
-VEHICLEINSTRUMENTCLUSTERTYPE_EXTERNAL_DISPLAY = 0x2
-
-# VehicleAudioExtFocusFlag
-VEHICLEAUDIOEXTFOCUSFLAG_NONE_FLAG = 0x0
-VEHICLEAUDIOEXTFOCUSFLAG_PERMANENT_FLAG = 0x1
-VEHICLEAUDIOEXTFOCUSFLAG_TRANSIENT_FLAG = 0x2
-VEHICLEAUDIOEXTFOCUSFLAG_PLAY_ONLY_FLAG = 0x4
-VEHICLEAUDIOEXTFOCUSFLAG_MUTE_MEDIA_FLAG = 0x8
-
-# VehicleHvacFanDirection
-VEHICLEHVACFANDIRECTION_FACE = 0x1
-VEHICLEHVACFANDIRECTION_FLOOR = 0x2
-VEHICLEHVACFANDIRECTION_FACE_AND_FLOOR = 0x3
-VEHICLEHVACFANDIRECTION_DEFROST = 0x4
-VEHICLEHVACFANDIRECTION_DEFROST_AND_FLOOR = 0x5
-
-# VehicleApPowerBootupReason
-VEHICLEAPPOWERBOOTUPREASON_USER_POWER_ON = 0x0
-VEHICLEAPPOWERBOOTUPREASON_USER_UNLOCK = 0x1
-VEHICLEAPPOWERBOOTUPREASON_TIMER = 0x2
-
-# VehicleArea
-VEHICLEAREA_GLOBAL = 0x1000000
-VEHICLEAREA_ZONE = 0x2000000
-VEHICLEAREA_WINDOW = 0x3000000
-VEHICLEAREA_MIRROR = 0x4000000
-VEHICLEAREA_SEAT = 0x5000000
-VEHICLEAREA_DOOR = 0x6000000
-VEHICLEAREA_MASK = 0xf000000
+# VehicleDisplay
+VEHICLEDISPLAY_MAIN = 0x0
+VEHICLEDISPLAY_INSTRUMENT_CLUSTER = 0x1
 
 # Create a container of value_type constants to be used by vhal_emulator
 class vhal_types_2_0:
diff --git a/tools/emulator/vhal_consts_2_1.py b/tools/emulator/vhal_consts_2_1.py
index d367b85..e5dbcff 100644
--- a/tools/emulator/vhal_consts_2_1.py
+++ b/tools/emulator/vhal_consts_2_1.py
@@ -15,255 +15,3 @@
 # DO NOT EDIT MANUALLY
 # This file was autogenerated by vhal_const_generate.py
 from vhal_consts_2_0 import *
-
-# FuelType
-FUELTYPE_NOT_AVAILABLE = 0x0
-FUELTYPE_GASOLINE = 0x1
-FUELTYPE_METHANOL = 0x2
-FUELTYPE_ETHANOL = 0x3
-FUELTYPE_DIESEL = 0x4
-FUELTYPE_LPG = 0x5
-FUELTYPE_CNG = 0x6
-FUELTYPE_PROPANE = 0x7
-FUELTYPE_ELECTRIC = 0x8
-FUELTYPE_BIFUEL_RUNNING_GASOLINE = 0x9
-FUELTYPE_BIFUEL_RUNNING_METHANOL = 0xa
-FUELTYPE_BIFUEL_RUNNING_ETHANOL = 0xb
-FUELTYPE_BIFUEL_RUNNING_LPG = 0xc
-FUELTYPE_BIFUEL_RUNNING_CNG = 0xd
-FUELTYPE_BIFUEL_RUNNING_PROPANE = 0xe
-FUELTYPE_BIFUEL_RUNNING_ELECTRIC = 0xf
-FUELTYPE_BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 0x10
-FUELTYPE_HYBRID_GASOLINE = 0x11
-FUELTYPE_HYBRID_ETHANOL = 0x12
-FUELTYPE_HYBRID_DIESEL = 0x13
-FUELTYPE_HYBRID_ELECTRIC = 0x14
-FUELTYPE_HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 0x15
-FUELTYPE_HYBRID_REGENERATIVE = 0x16
-FUELTYPE_BIFUEL_RUNNING_DIESEL = 0x17
-
-# VmsBaseMessageIntegerValuesIndex
-VMSBASEMESSAGEINTEGERVALUESINDEX_VMS_MESSAGE_TYPE = 0x0
-
-# SparkIgnitionMonitors
-SPARKIGNITIONMONITORS_EGR_AVAILABLE = 0x40
-SPARKIGNITIONMONITORS_EGR_INCOMPLETE = 0x80
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_AVAILABLE = 0x100
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x200
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_AVAILABLE = 0x400
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_INCOMPLETE = 0x800
-SPARKIGNITIONMONITORS_AC_REFRIGERANT_AVAILABLE = 0x1000
-SPARKIGNITIONMONITORS_AC_REFRIGERANT_INCOMPLETE = 0x2000
-SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_AVAILABLE = 0x4000
-SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x8000
-SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_AVAILABLE = 0x10000
-SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_INCOMPLETE = 0x20000
-SPARKIGNITIONMONITORS_HEATED_CATALYST_AVAILABLE = 0x40000
-SPARKIGNITIONMONITORS_HEATED_CATALYST_INCOMPLETE = 0x80000
-SPARKIGNITIONMONITORS_CATALYST_AVAILABLE = 0x100000
-SPARKIGNITIONMONITORS_CATALYST_INCOMPLETE = 0x200000
-
-# Obd2FloatSensorIndex
-OBD2FLOATSENSORINDEX_CALCULATED_ENGINE_LOAD = 0x0
-OBD2FLOATSENSORINDEX_ENGINE_COOLANT_TEMPERATURE = 0x1
-OBD2FLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK1 = 0x2
-OBD2FLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK1 = 0x3
-OBD2FLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK2 = 0x4
-OBD2FLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK2 = 0x5
-OBD2FLOATSENSORINDEX_FUEL_PRESSURE = 0x6
-OBD2FLOATSENSORINDEX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x7
-OBD2FLOATSENSORINDEX_ENGINE_RPM = 0x8
-OBD2FLOATSENSORINDEX_VEHICLE_SPEED = 0x9
-OBD2FLOATSENSORINDEX_TIMING_ADVANCE = 0xa
-OBD2FLOATSENSORINDEX_MAF_AIR_FLOW_RATE = 0xb
-OBD2FLOATSENSORINDEX_THROTTLE_POSITION = 0xc
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_VOLTAGE = 0xd
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 0xe
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 0xf
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_VOLTAGE = 0x10
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 0x11
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 0x12
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_VOLTAGE = 0x13
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 0x14
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 0x15
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_VOLTAGE = 0x16
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 0x17
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 0x18
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_VOLTAGE = 0x19
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 0x1a
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 0x1b
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_VOLTAGE = 0x1c
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 0x1d
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 0x1e
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_VOLTAGE = 0x1f
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 0x20
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 0x21
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_VOLTAGE = 0x22
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 0x23
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 0x24
-OBD2FLOATSENSORINDEX_FUEL_RAIL_PRESSURE = 0x25
-OBD2FLOATSENSORINDEX_FUEL_RAIL_GAUGE_PRESSURE = 0x26
-OBD2FLOATSENSORINDEX_COMMANDED_EXHAUST_GAS_RECIRCULATION = 0x27
-OBD2FLOATSENSORINDEX_EXHAUST_GAS_RECIRCULATION_ERROR = 0x28
-OBD2FLOATSENSORINDEX_COMMANDED_EVAPORATIVE_PURGE = 0x29
-OBD2FLOATSENSORINDEX_FUEL_TANK_LEVEL_INPUT = 0x2a
-OBD2FLOATSENSORINDEX_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x2b
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 0x2c
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 0x2d
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 0x2e
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 0x2f
-OBD2FLOATSENSORINDEX_ABSOLUTE_LOAD_VALUE = 0x30
-OBD2FLOATSENSORINDEX_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 0x31
-OBD2FLOATSENSORINDEX_RELATIVE_THROTTLE_POSITION = 0x32
-OBD2FLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_B = 0x33
-OBD2FLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_C = 0x34
-OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_D = 0x35
-OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_E = 0x36
-OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_F = 0x37
-OBD2FLOATSENSORINDEX_COMMANDED_THROTTLE_ACTUATOR = 0x38
-OBD2FLOATSENSORINDEX_ETHANOL_FUEL_PERCENTAGE = 0x39
-OBD2FLOATSENSORINDEX_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x3a
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3b
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x3c
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x3d
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x3e
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3f
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x40
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x41
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x42
-OBD2FLOATSENSORINDEX_RELATIVE_ACCELERATOR_PEDAL_POSITION = 0x43
-OBD2FLOATSENSORINDEX_HYBRID_BATTERY_PACK_REMAINING_LIFE = 0x44
-OBD2FLOATSENSORINDEX_FUEL_INJECTION_TIMING = 0x45
-OBD2FLOATSENSORINDEX_ENGINE_FUEL_RATE = 0x46
-OBD2FLOATSENSORINDEX_LAST_SYSTEM_INDEX = 0x46
-
-# CommonIgnitionMonitors
-COMMONIGNITIONMONITORS_COMPONENTS_AVAILABLE = 0x1
-COMMONIGNITIONMONITORS_COMPONENTS_INCOMPLETE = 0x2
-COMMONIGNITIONMONITORS_FUEL_SYSTEM_AVAILABLE = 0x4
-COMMONIGNITIONMONITORS_FUEL_SYSTEM_INCOMPLETE = 0x8
-COMMONIGNITIONMONITORS_MISFIRE_AVAILABLE = 0x10
-COMMONIGNITIONMONITORS_MISFIRE_INCOMPLETE = 0x20
-
-# IgnitionMonitorKind
-IGNITIONMONITORKIND_SPARK = 0x0
-IGNITIONMONITORKIND_COMPRESSION = 0x1
-
-# SecondaryAirStatus
-SECONDARYAIRSTATUS_UPSTREAM = 0x1
-SECONDARYAIRSTATUS_DOWNSTREAM_OF_CATALYCIC_CONVERTER = 0x2
-SECONDARYAIRSTATUS_FROM_OUTSIDE_OR_OFF = 0x4
-SECONDARYAIRSTATUS_PUMP_ON_FOR_DIAGNOSTICS = 0x8
-
-# VmsMessageType
-VMSMESSAGETYPE_SUBSCRIBE = 0x1
-VMSMESSAGETYPE_UNSUBSCRIBE = 0x2
-VMSMESSAGETYPE_DATA = 0x3
-VMSMESSAGETYPE_OFFERING = 0x4
-VMSMESSAGETYPE_AVAILABILITY_REQUEST = 0x5
-VMSMESSAGETYPE_AVAILABILITY_RESPONSE = 0x6
-VMSMESSAGETYPE_SUBSCRIPTION_REQUEST = 0x7
-VMSMESSAGETYPE_SUBSCRIPTION_RESPONSE = 0x8
-
-# CompressionIgnitionMonitors
-COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_AVAILABLE = 0x40
-COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_INCOMPLETE = 0x80
-COMPRESSIONIGNITIONMONITORS_PM_FILTER_AVAILABLE = 0x100
-COMPRESSIONIGNITIONMONITORS_PM_FILTER_INCOMPLETE = 0x200
-COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_AVAILABLE = 0x400
-COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_INCOMPLETE = 0x800
-COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_AVAILABLE = 0x1000
-COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_INCOMPLETE = 0x2000
-COMPRESSIONIGNITIONMONITORS_NOx_SCR__AVAILABLE = 0x4000
-COMPRESSIONIGNITIONMONITORS_NOx_SCR_INCOMPLETE = 0x8000
-COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_AVAILABLE = 0x10000
-COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_INCOMPLETE = 0x20000
-
-# VehiclePropertyGroup
-VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
-VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
-VEHICLEPROPERTYGROUP_MASK = 0xf0000000
-
-# Obd2IntegerSensorIndex
-OBD2INTEGERSENSORINDEX_FUEL_SYSTEM_STATUS = 0x0
-OBD2INTEGERSENSORINDEX_MALFUNCTION_INDICATOR_LIGHT_ON = 0x1
-OBD2INTEGERSENSORINDEX_IGNITION_MONITORS_SUPPORTED = 0x2
-OBD2INTEGERSENSORINDEX_IGNITION_SPECIFIC_MONITORS = 0x3
-OBD2INTEGERSENSORINDEX_INTAKE_AIR_TEMPERATURE = 0x4
-OBD2INTEGERSENSORINDEX_COMMANDED_SECONDARY_AIR_STATUS = 0x5
-OBD2INTEGERSENSORINDEX_NUM_OXYGEN_SENSORS_PRESENT = 0x6
-OBD2INTEGERSENSORINDEX_RUNTIME_SINCE_ENGINE_START = 0x7
-OBD2INTEGERSENSORINDEX_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 0x8
-OBD2INTEGERSENSORINDEX_WARMUPS_SINCE_CODES_CLEARED = 0x9
-OBD2INTEGERSENSORINDEX_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 0xa
-OBD2INTEGERSENSORINDEX_ABSOLUTE_BAROMETRIC_PRESSURE = 0xb
-OBD2INTEGERSENSORINDEX_CONTROL_MODULE_VOLTAGE = 0xc
-OBD2INTEGERSENSORINDEX_AMBIENT_AIR_TEMPERATURE = 0xd
-OBD2INTEGERSENSORINDEX_TIME_WITH_MALFUNCTION_LIGHT_ON = 0xe
-OBD2INTEGERSENSORINDEX_TIME_SINCE_TROUBLE_CODES_CLEARED = 0xf
-OBD2INTEGERSENSORINDEX_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 0x10
-OBD2INTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_VOLTAGE = 0x11
-OBD2INTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_CURRENT = 0x12
-OBD2INTEGERSENSORINDEX_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x13
-OBD2INTEGERSENSORINDEX_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 0x14
-OBD2INTEGERSENSORINDEX_FUEL_TYPE = 0x15
-OBD2INTEGERSENSORINDEX_FUEL_RAIL_ABSOLUTE_PRESSURE = 0x16
-OBD2INTEGERSENSORINDEX_ENGINE_OIL_TEMPERATURE = 0x17
-OBD2INTEGERSENSORINDEX_DRIVER_DEMAND_PERCENT_TORQUE = 0x18
-OBD2INTEGERSENSORINDEX_ENGINE_ACTUAL_PERCENT_TORQUE = 0x19
-OBD2INTEGERSENSORINDEX_ENGINE_REFERENCE_PERCENT_TORQUE = 0x1a
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_IDLE = 0x1b
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 0x1c
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 0x1d
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 0x1e
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 0x1f
-OBD2INTEGERSENSORINDEX_LAST_SYSTEM_INDEX = 0x1f
-
-# VehicleProperty
-VEHICLEPROPERTY_WHEEL_TICK = 0x11610306
-VEHICLEPROPERTY_OBD2_LIVE_FRAME = 0x11e00d00
-VEHICLEPROPERTY_OBD2_FREEZE_FRAME = 0x11e00d01
-VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO = 0x11e00d02
-VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR = 0x11e00d03
-VEHICLEPROPERTY_VEHICLE_MAP_SERVICE = 0x11e00c00
-
-# FuelSystemStatus
-FUELSYSTEMSTATUS_OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 0x1
-FUELSYSTEMSTATUS_CLOSED_LOOP = 0x2
-FUELSYSTEMSTATUS_OPEN_ENGINE_LOAD_OR_DECELERATION = 0x4
-FUELSYSTEMSTATUS_OPEN_SYSTEM_FAILURE = 0x8
-FUELSYSTEMSTATUS_CLOSED_LOOP_BUT_FEEDBACK_FAULT = 0x10
-
-# VehiclePropertyType
-VEHICLEPROPERTYTYPE_STRING = 0x100000
-VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
-VEHICLEPROPERTYTYPE_INT32 = 0x400000
-VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
-VEHICLEPROPERTYTYPE_INT64 = 0x500000
-VEHICLEPROPERTYTYPE_FLOAT = 0x600000
-VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
-VEHICLEPROPERTYTYPE_BYTES = 0x700000
-VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
-VEHICLEPROPERTYTYPE_MASK = 0xff0000
-
-# VmsSimpleMessageIntegerValuesIndex
-VMSSIMPLEMESSAGEINTEGERVALUESINDEX_VMS_LAYER_ID = 0x1
-VMSSIMPLEMESSAGEINTEGERVALUESINDEX_VMS_LAYER_VERSION = 0x2
-
-# VehicleArea
-VEHICLEAREA_GLOBAL = 0x1000000
-VEHICLEAREA_ZONE = 0x2000000
-VEHICLEAREA_WINDOW = 0x3000000
-VEHICLEAREA_MIRROR = 0x4000000
-VEHICLEAREA_SEAT = 0x5000000
-VEHICLEAREA_DOOR = 0x6000000
-VEHICLEAREA_MASK = 0xf000000
-
-# VmsOfferingMessageIntegerValuesIndex
-VMSOFFERINGMESSAGEINTEGERVALUESINDEX_VMS_NUMBER_OF_LAYERS_DEPENDENCIES = 0x1
-VMSOFFERINGMESSAGEINTEGERVALUESINDEX_FIRST_DEPENDENCIES_INDEX = 0x2
-
-# VmsSubscriptionResponseFormat
-VMSSUBSCRIPTIONRESPONSEFORMAT_SEQUENCE_NUMBER = 0x1
-VMSSUBSCRIPTIONRESPONSEFORMAT_NUMBER_OF_LAYERS = 0x2
-VMSSUBSCRIPTIONRESPONSEFORMAT_FIRST_LAYER = 0x3
diff --git a/tools/emulator/vhal_emulator.py b/tools/emulator/vhal_emulator.py
index 4c94e4f..7cc6da7 100644
--- a/tools/emulator/vhal_emulator.py
+++ b/tools/emulator/vhal_emulator.py
@@ -129,17 +129,22 @@
         """
         print("len = ", len(data), "str = ", ":".join("{:02x}".format(ord(d)) for d in data))
 
-    def openSocket(self):
+    def openSocket(self, device=None):
         """
             Connects to an Android Auto device running a Vehicle HAL with simulator.
         """
         # Hard-coded socket port needs to match the one in DefaultVehicleHal
-        portNumber = 33452
-        # Setup ADB port forwarding
-        subprocess.call("adb forward tcp:%d tcp:%d" % (portNumber, portNumber), shell=True)
+        remotePortNumber = 33452
+        extraArgs = '' if device is None else '-s %s' % device
+        adbCmd = 'adb %s forward tcp:0 tcp:%d' % (extraArgs, remotePortNumber)
+        adbResp = subprocess.check_output(adbCmd, shell=True)[0:-1]
+        localPortNumber = int(adbResp)
+        print('Connecting local port %s to remote port %s on %s' % (
+            localPortNumber, remotePortNumber,
+            'default device' if device is None else 'device %s' % device))
         # Open the socket and connect
         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        self.sock.connect(('localhost', portNumber))
+        self.sock.connect(('localhost', localPortNumber))
 
     def rxMsg(self):
         """
@@ -249,11 +254,11 @@
             return
         self._txCmd(cmd)
 
-    def __init__(self, types):
+    def __init__(self, types, device=None):
         # Save the list of types constants
         self._types = types
         # Open the socket
-        self.openSocket()
+        self.openSocket(device)
         # Get the list of configs
         self.getConfigAll()
         msg = self.rxMsg()
diff --git a/tools/emulator/vhal_emulator_test.py b/tools/emulator/vhal_emulator_test.py
index 1d0ada0..ea4030d 100755
--- a/tools/emulator/vhal_emulator_test.py
+++ b/tools/emulator/vhal_emulator_test.py
@@ -37,7 +37,7 @@
 sys.dont_write_bytecode = True
 
 import VehicleHalProto_pb2
-import vhal_consts_2_0
+import vhal_consts_2_1
 import vhal_emulator
 import logging
 
@@ -47,6 +47,16 @@
     _configs = 0                    # List of configs from DUT
     _log = 0                        # Logger module
     _vhal = 0                       # Handle to VHAL object that communicates over socket to DUT
+    # TODO: b/38203109 - Fix OBD2 values, implement handling for complex properties
+    _skipProps = [
+                    vhal_consts_2_1.VEHICLEPROPERTY_OBD2_LIVE_FRAME,
+                    vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME,
+                    vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO,
+                    vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR,
+                    vhal_consts_2_1.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE,
+                    vhal_consts_2_1.VEHICLEPROPERTY_WHEEL_TICK,     # Need to support complex properties
+                    0x21E00666      # FakeDataControllingProperty - an internal test property
+                 ]
 
     def _getMidpoint(self, minVal, maxVal):
         retVal =  minVal + (maxVal - minVal)/2
@@ -60,7 +70,7 @@
         elif valType in self._types.TYPE_BYTES:
             # Generate array of integers counting from 0
             testValue = list(range(len(origValue)))
-        elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
+        elif valType == vhal_consts_2_1.VEHICLEPROPERTYTYPE_BOOLEAN:
             testValue = origValue ^ 1
         elif valType in self._types.TYPE_INT32:
             try:
@@ -87,7 +97,7 @@
             testValue = "%.5f" % testValue
             testValue = float(testValue)
         else:
-            self._log.error("generateTestValue:  valType=%d is not handled", valType)
+            self._log.error("generateTestValue:  valType=0x%X is not handled", valType)
             testValue = None
         return testValue
 
@@ -103,7 +113,7 @@
                 value = rxMsg.value[0].string_value
             elif valType in self._types.TYPE_BYTES:
                 value = rxMsg.value[0].bytes_value
-            elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
+            elif valType == vhal_consts_2_1.VEHICLEPROPERTYTYPE_BOOLEAN:
                 value = rxMsg.value[0].int32_values[0]
             elif valType in self._types.TYPE_INT32:
                 value = rxMsg.value[0].int32_values[0]
@@ -115,7 +125,7 @@
                 value = "%.5f" % value
                 value = float(value)
             else:
-                self._log.error("getValuesFromMsg:  valType=%d is not handled", valType)
+                self._log.error("getValueFromMsg:  valType=0x%X is not handled", valType)
                 value = None
         return value
 
@@ -126,10 +136,10 @@
         retVal = 1
         rxMsg = self._vhal.rxMsg()
         if rxMsg.msg_type != expectedType:
-            self._log.error("rxMsg Type expected: %d, received: %d", expectedType, rxMsg.msg_type)
+            self._log.error("rxMsg Type expected: 0x%X, received: 0x%X", expectedType, rxMsg.msg_type)
             retVal = 0
         if rxMsg.status != expectedStatus:
-            self._log.error("rxMsg Status expected: %d, received: %d", expectedStatus, rxMsg.status)
+            self._log.error("rxMsg Status expected: 0x%X, received: 0x%X", expectedStatus, rxMsg.status)
             retVal = 0
         return rxMsg, retVal
 
@@ -138,13 +148,13 @@
     def testGetConfig(self):
         self._log.info("Starting testGetConfig...")
         for cfg in self._configs:
-            self._log.debug("  Getting config for propId=%d", cfg.prop)
+            self._log.debug("  Getting config for propId=0x%X", cfg.prop)
             self._vhal.getConfig(cfg.prop)
             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
                                                    VehicleHalProto_pb2.RESULT_OK)
             if retVal:
                 if rxMsg.config[0] != cfg:
-                    self._log.error("testGetConfig failed.  prop=%d, expected:\n%s\nreceived:\n%s",
+                    self._log.error("testGetConfig failed.  prop=0x%X, expected:\n%s\nreceived:\n%s",
                                cfg.prop, str(cfg), str(rxMsg.config))
         self._log.info("  Finished testGetConfig!")
 
@@ -152,13 +162,13 @@
     def testGetBadConfig(self):
         self._log.info("Starting testGetBadConfig...")
         for prop in self._badProps:
-            self._log.debug("  Testing bad propId=%d", prop)
+            self._log.debug("  Testing bad propId=0x%X", prop)
             self._vhal.getConfig(prop)
             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
                                                    VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
             if retVal:
                 for cfg in rxMsg.config:
-                    self._log.error("testGetBadConfig  prop=%d, expected:None, received:\n%s",
+                    self._log.error("testGetBadConfig  prop=0x%X, expected:None, received:\n%s",
                                     cfg.prop, str(rxMsg.config))
         self._log.info("  Finished testGetBadConfig!")
 
@@ -177,6 +187,11 @@
     def testGetSet(self):
         self._log.info("Starting testGetSet()...")
         for cfg in self._configs:
+            if cfg.prop in self._skipProps:
+                # Skip properties that cannot be handled properly by this test.
+                self._log.warning("  Skipping propId=0x%X", cfg.prop)
+                continue
+
             areas = cfg.supported_areas
             idx = -1
             while (idx == -1) | (areas != 0):
@@ -188,7 +203,7 @@
                 # Remove the area from areas
                 areas ^= area
 
-                self._log.debug("  Testing propId=%d, area=%d", cfg.prop, area)
+                self._log.debug("  Testing propId=0x%X, area=0x%X", cfg.prop, area)
 
                 # Get the current value
                 self._vhal.getProperty(cfg.prop, area)
@@ -198,21 +213,21 @@
                 # Save the original value
                 origValue = self._getValueFromMsg(rxMsg)
                 if origValue == None:
-                    self._log.error("testGetSet:  Could not get value for prop=%d, area=%d",
+                    self._log.error("testGetSet:  Could not get value for prop=0x%X, area=0x%X",
                                     cfg.prop, area)
                     continue
 
                 # Generate the test value
                 testValue = self._generateTestValue(cfg, idx, origValue)
                 if testValue == None:
-                    self._log.error("testGetSet:  Cannot generate test value for prop=%d, area=%d",
+                    self._log.error("testGetSet:  Cannot generate test value for prop=0x%X, area=0x%X",
                                     cfg.prop, area)
                     continue
 
                 # Send the new value
                 self._vhal.setProperty(cfg.prop, area, testValue)
                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP,
-                                                       VehicleHalProto_pb2.RESULT_OK)
+                                                        VehicleHalProto_pb2.RESULT_OK)
 
                 # Get the new value and verify it
                 self._vhal.getProperty(cfg.prop, area)
@@ -220,7 +235,7 @@
                                                        VehicleHalProto_pb2.RESULT_OK)
                 newValue = self._getValueFromMsg(rxMsg)
                 if newValue != testValue:
-                    self._log.error("testGetSet: set failed for propId=%d, area=%d", cfg.prop, area)
+                    self._log.error("testGetSet: set failed for propId=0x%X, area=0x%X", cfg.prop, area)
                     print("testValue= ", testValue, "newValue= ", newValue)
                     continue
 
@@ -233,13 +248,13 @@
     def testGetBadProperty(self):
         self._log.info("Starting testGetBadProperty()...")
         for prop in self._badProps:
-            self._log.debug("  Testing bad propId=%d", prop)
+            self._log.debug("  Testing bad propId=0x%X", prop)
             self._vhal.getProperty(prop, 0)
             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
                                                    VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
             if retVal:
                 for value in rxMsg.value:
-                    self._log.error("testGetBadProperty  prop=%d, expected:None, received:\n%s",
+                    self._log.error("testGetBadProperty  prop=0x%X, expected:None, received:\n%s",
                                     prop, str(rxMsg))
         self._log.info("  Finished testGetBadProperty()!")
 
@@ -248,12 +263,12 @@
         area = 1
         value = 100
         for prop in self._badProps:
-            self._log.debug("  Testing bad propId=%d", prop)
+            self._log.debug("  Testing bad propId=0x%X", prop)
             area = area + 1
             value = value + 1
             try:
                 self._vhal.setProperty(prop, area, value)
-                self._log.error("testGetBadProperty failed.  prop=%d, area=%d, value=%d",
+                self._log.error("testGetBadProperty failed.  prop=0x%X, area=0x%X, value=%d",
                                 prop, area, value)
             except ValueError as e:
                 # Received expected error
@@ -290,5 +305,5 @@
         self._configs = self._vhal.rxMsg().config
 
 if __name__ == '__main__':
-    v = VhalTest(vhal_consts_2_0.vhal_types_2_0)
+    v = VhalTest(vhal_consts_2_1.vhal_types_2_0)
     v.runTests()
diff --git a/tools/io_analysis/check_file_read.py b/tools/io_analysis/check_file_read.py
index 06b4f43..f785436 100644
--- a/tools/io_analysis/check_file_read.py
+++ b/tools/io_analysis/check_file_read.py
@@ -23,6 +23,7 @@
 import sys
 
 DBG = False
+DBG_ISSUE = False
 
 # hard coded maps to detect partition for given device or the other way around
 # this can be different per each device. This works for marlin.
@@ -31,83 +32,240 @@
 for key, value in DEVICE_TO_PARTITION.iteritems():
   PARTITION_TO_DEVICE[value] = key
 
-RE_DO_SYS_OPEN = r""".+\s+([0-9]+\.[0-9]+):\s+do_sys_open:\s+(\S+):\sopen..(\S+).,\s([0-9]+).\s+.+inode\s=\s([0-9]+)"""
+# init-1     [003] ....     2.703964: do_sys_open: init: open("/sys/fs/selinux/null", 131074, 0) fd = 0, inode = 22
+RE_DO_SYS_OPEN = r"""\s+\S+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+do_sys_open:\s+(\S+):\sopen..(\S+).,\s([0-9]+).\s+.+inode\s=\s([0-9]+)"""
+# init-1     [003] ...1     2.703991: ext4_ext_map_blocks_enter: dev 253,0 ino 2719 lblk 154 len 30 flags
 RE_EXT4_MA_BLOCKS_ENTER = r"""\s+(\S+)-([0-9]+).+\s+([0-9]+\.[0-9]+):\s+ext4_ext_map_blocks_enter:\s+dev\s+(\S+)\s+ino\s+([0-9]+)\s+lblk\s+([0-9]+)\s+len\s+([0-9]+)"""
+# init-1     [002] ...1     2.687205: ext4_ext_map_blocks_exit: dev 253,0 ino 8 flags  lblk 0 pblk 196608 len 1 mflags M ret 1
+RE_EXT4_MA_BLOCKS_EXIT = r"""\s+(\S+)-([0-9]+).+\s+([0-9]+\.[0-9]+):\s+ext4_ext_map_blocks_exit:\s+dev\s+(\S+)\s+ino\s+([0-9]+)\sflags.*\slblk\s+([0-9]+)\spblk\s([0-9]+)\slen\s+([0-9]+).*mflags\s(\S*)\sret\s([0-9]+)"""
+#  init-1     [002] ...1     2.887119: block_bio_remap: 8,0 R 10010384 + 8 <- (259,18) 3998944
+RE_BLOCK_BIO_REMAP = r""".+block_bio_remap:\s\d+,\d+\s\S+\s(\d+)\s\+\s\d+\s<-\s\([^\)]+\)\s(\d+)"""
+# kworker/u9:1-83    [003] d..2     2.682991: block_rq_issue: 8,0 RA 0 () 10140208 + 32 [kworker/u9:1]
+RE_BLOCK_RQ_ISSUE = r"""\s+\S+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+block_rq_issue:\s+([0-9]+)\,([0-9]+)\s+([RW]\S*)\s[0-9]+\s\([^\)]*\)\s([0-9]+)\s+\+\s+([0-9]+)\s+\[([^\]]+)\]"""
+
+EXT4_SIZE_TO_BLOCK_SIZE = 8 # ext4: 4KB, block device block size: 512B
+
+class FileAccess:
+  def __init__(self, file):
+    self.file = file
+    self.accesses = []
+    self.total_access = 0
+    self.ext4_access_size_histogram = {} #key: read size, value: occurrence
+    self.block_access_size_histogram = {}
+    self.ext4_single_block_accesses = {} # process name, occurrence
+    self.block_single_block_accesses = {} # process name, occurrence
+    self.blocks_histogram = {} # K: offset, V: read counter
+
+  def add_if_single_block(self, container, size, offset, process_name):
+    if size != 1:
+      return
+    offsets = container.get(process_name)
+    if not offsets:
+      offsets = []
+      container[process_name] = offsets
+    offsets.append(offset)
+
+  def add_access(self, time, offset, size, process_name, read_sizes):
+    self.accesses.append((time, offset, size, process_name))
+    self.total_access += size
+    self.ext4_access_size_histogram[size] = self.ext4_access_size_histogram.get(size, 0) + 1
+    read_offset = offset
+    for s in read_sizes:
+      self.block_access_size_histogram[s] = self.block_access_size_histogram.get(s, 0) + 1
+      self.add_if_single_block(self.block_single_block_accesses, s, read_offset, process_name)
+      read_offset += s
+    for i in range(size):
+      self.blocks_histogram[offset + i] = self.blocks_histogram.get(offset + i, 0) + 1
+    self.add_if_single_block(self.ext4_single_block_accesses, size, offset, process_name)
+
+  def add_merged_access(self, time, offsets, lens, process_names):
+    total_read_offsets = set() # each read can overwrap. So count only once for block level counting
+    for i in range(len(offsets)):
+      self.accesses.append((time, offsets[i], lens[i], process_names[i]))
+      self.ext4_access_size_histogram[lens[i]] = self.ext4_access_size_histogram.get(lens[i], 0) + 1
+      self.add_if_single_block(self.ext4_single_block_accesses, lens[i], offsets[i], process_names[i])
+      for j in range(len(lens)):
+        total_read_offsets.add(offsets[i] + j)
+    total_lens = len(total_read_offsets)
+    start_offset = min(total_read_offsets)
+    self.total_access += total_lens
+    self.block_access_size_histogram[total_lens] = self.block_access_size_histogram.get(total_lens, 0) \
+                                                   + 1
+    self.add_if_single_block(self.block_single_block_accesses, total_lens, start_offset, \
+                             process_names[0])
+    for s in range(total_lens):
+      self.blocks_histogram[start_offset + s] = self.blocks_histogram.get(start_offset + s, 0) + 1
+
+
+  def dump(self):
+    if len(self.ext4_access_size_histogram) > 1:
+      print "   Ext4 access size histograms:", collections.OrderedDict( \
+        sorted(self.ext4_access_size_histogram.items(), key = lambda item: item[0]))
+    if len(self.ext4_single_block_accesses) > 0 and self.total_access > 1:
+      print "   Ext4 single block accesses:", collections.OrderedDict( \
+        sorted(self.ext4_single_block_accesses.items(), key = lambda item: item[1], reverse = True))
+    if len(self.block_access_size_histogram) > 1:
+      print "   Block access size histograms:", collections.OrderedDict( \
+        sorted(self.block_access_size_histogram.items(), key = lambda item: item[0]))
+    if len(self.block_single_block_accesses) > 0 and self.total_access > 1:
+      print "   Block single block accesses:", collections.OrderedDict( \
+        sorted(self.block_single_block_accesses.items(), key = lambda item: item[1], reverse = True))
+    if self.total_access > 1:
+      sorted_blocks_histogram = sorted(self.blocks_histogram.items(), key = lambda item: item[1], \
+                                       reverse = True)
+      prints = []
+      repeating_reads_counter = 0
+      for entry in sorted_blocks_histogram:
+        offset = entry[0]
+        counter = entry[1]
+        if counter == 1:
+          break
+        prints.append(str(offset) + ":" + str(counter))
+        repeating_reads_counter += (counter - 1)
+      if len(prints) > 0:
+        print "   repeating accesses", repeating_reads_counter, " offset:count ->", ','.join(prints)
 
 class FileEvent:
   def __init__(self, open_time, file_name, process_name, inode, flags):
     self.file_name = file_name
     self.inode = inode
+    self.total_open = 1
     self.processes = []
     self.processes.append((open_time, process_name, flags))
-    self.reads = []
-    self.total_reads = 0
-    self.total_open = 1
-    self.blocks = {}
-    self.total_rereads = 0
-    self.read_size_histogram = {} #key: read size, value: occurrence
-    self.single_block_reads = {} # process name, occurrence
+    self.read = FileAccess(self)
+    self.write = FileAccess(self)
+
 
   def add_open(self, open_time, process_name, flags):
     self.processes.append((open_time, process_name, flags))
     self.total_open += 1
 
-  def add_read(self, time, offset, size, process_name):
-    self.reads.append((time, offset, size, process_name))
-    self.total_reads += size
-    for i in range(offset, offset + size):
-      if not self.blocks.get(i):
-        self.blocks[i] = 1
-      else:
-        self.blocks[i] += 1
-        self.total_rereads += 1
-    if not self.read_size_histogram.get(size):
-      self.read_size_histogram[size] = 1
+  def add_access(self, is_read, time, offset, size, process_name, read_sizes):
+    if is_read:
+      self.read.add_access(time, offset, size, process_name, read_sizes)
     else:
-      self.read_size_histogram[size] += 1
-    if size == 1:
-      if not self.single_block_reads.get(process_name):
-        self.single_block_reads[process_name] = 1
-      else:
-        self.single_block_reads[process_name] += 1
+      self.write.add_access(time, offset, size, process_name, read_sizes)
 
-  def dump(self):
-    print " filename %s, total reads %d, total open %d total rereads %d inode %s" \
-      % (self.file_name, self.total_reads, self.total_open, self.total_rereads, self.inode)
+  def add_merged_access(self, is_read, time, offsets, lens, process_names):
+    if is_read:
+      self.read.add_merged_access(time, offsets, lens, process_names)
+    else:
+      self.write.add_merged_access(time, offsets, lens, process_names)
+
+  def dump(self, name_to_pid_map):
+    print " ***filename %s, total reads %d, total writes %d, total open %d inode %s" \
+          % (self.file_name, self.read.total_access, self.write.total_access, self.total_open,\
+             self.inode)
     process_names = []
     for opener in self.processes:
-      process_names.append(opener[1])
+      process_names.append(opener[1] + "-" + name_to_pid_map.get(opener[1], '?') + " t:" + \
+                           str(opener[0]) + " flags:" + opener[2])
     print "  Processes opened this file:", ','.join(process_names)
-    if len(self.read_size_histogram) > 1:
-      print "  Read size histograms:", collections.OrderedDict( \
-        sorted(self.read_size_histogram.items(), key = lambda item: item[0]))
-    if len(self.single_block_reads) > 1 and len(self.reads) > 1:
-      print "  Single block reads:", collections.OrderedDict( \
-        sorted(self.single_block_reads.items(), key = lambda item: item[1], reverse = True))
+    if self.read.total_access > 0:
+      print "  ****Reads:"
+      self.read.dump()
+    if self.write.total_access > 0:
+      print "  ****Writes:"
+      self.write.dump()
+
+  def dump_short(self):
+    print "    filename %s, total reads %d, total writes %d" % (self.file_name,
+      self.read.total_access, self.write.total_access)
+
+class PendingAccess:
+  def __init__(self, process_name, pid, time, dev, inode, lblk, pblk, len, fevent):
+    self.process_name = process_name
+    self.pid = pid
+    self.time = time
+    self.dev = dev
+    self.inode = inode
+    self.lblk = lblk
+    self.pblk = pblk
+    self.blk_len = len * EXT4_SIZE_TO_BLOCK_SIZE
+    self.len = len
+    self.fevent = fevent
+    self.pending_accesses = set()
+    for i in range(len):
+      self.pending_accesses.add(i)
+    self.access_sizes = [] # valid read for this file in block dev level.
+    self.block_access_counter = 0
+
+  def get_valid_access(self, block_offset, block_len):
+    ext4_offset = block_offset / EXT4_SIZE_TO_BLOCK_SIZE
+    if ext4_offset > self.len:
+      return 0, 0
+    ext4_len = block_len / EXT4_SIZE_TO_BLOCK_SIZE
+    if (ext4_offset + ext4_len) > self.len:
+      ext4_len = self.len - ext4_offset
+    return ext4_offset, ext4_len
+
+  def queue_block_access(self, ext4_offset, ext4_len):
+    if ext4_len <= 0:
+      return
+    self.block_access_counter += 1
+    ext4_blocks_accessed = 0
+    for i in range(ext4_len):
+      ext4_block_i = i + ext4_offset
+      if ext4_block_i in self.pending_accesses:
+        ext4_blocks_accessed += 1
+        self.pending_accesses.remove(ext4_block_i)
+    if ext4_blocks_accessed > 0:
+      self.access_sizes.append(ext4_blocks_accessed)
+
+  def handle_req_complete(self, time, is_read):
+    self.fevent.add_access(is_read, self.time, self.lblk, self.len, self.process_name,\
+                           self.access_sizes)
+
+  def handle_merged_req(self, time, offsets, lens, names, is_read):
+    self.fevent.add_merged_access(is_read, time, offsets, lens, names)
+
+  def is_req_complete(self):
+    return len(self.pending_accesses) == 0
+
+  def is_req_started(self):
+    return self.len is not len(self.pending_accesses)
 
 class Trace:
   def __init__(self):
     self.files_per_device = {} # key: device, value: { key: inode, value; FileEvent }
     self.re_open = re.compile(RE_DO_SYS_OPEN)
-    self.re_read = re.compile(RE_EXT4_MA_BLOCKS_ENTER)
+    self.re_ext4_access = re.compile(RE_EXT4_MA_BLOCKS_EXIT)
+    self.re_bio_remap = re.compile(RE_BLOCK_BIO_REMAP)
+    self.re_block_issue = re.compile(RE_BLOCK_RQ_ISSUE)
+    # req from ext4 that has not gone down to block level yet, K:block address,
+    #  V: list of PendingRead
+    self.pending_accesses = {}
+    self.remap = {}
+    self.process_names = {} # K: PID, V : name
 
   def handle_line(self, line):
     match = self.re_open.match(line)
     if match:
       self.handle_open(match)
       return
-    match = self.re_read.match(line)
+    match = self.re_ext4_access.match(line)
     if match:
-      self.handle_read(match)
+      self.handle_ext4_block_exit(match)
+      return
+    match = self.re_bio_remap.match(line)
+    if match:
+      self.handle_bio_remap(match)
+      return
+    match = self.re_block_issue.match(line)
+    if match:
+      self.handle_block_issue(match)
       return
 
   def handle_open(self, match):
-    time = match.group(1)
-    process_name = match.group(2)
-    file_name = match.group(3)
-    flag = match.group(4)
-    inode = match.group(5)
+    pid = int(match.group(1))
+    time = match.group(2)
+    process_name = match.group(3)
+    file_name = match.group(4)
+    flag = match.group(5)
+    inode = int(match.group(6))
     dev_name = None
+    self.process_names[pid] = process_name
+    #print "open", pid, process_name, file_name, inode
     for p in PARTITION_TO_DEVICE:
       if file_name.startswith(p):
         dev_name = PARTITION_TO_DEVICE[p]
@@ -123,14 +281,24 @@
     else:
       fevent.add_open(time, process_name, flag)
 
-  def handle_read(self, match):
+  def handle_ext4_block_exit(self, match):
     process_name = match.group(1)
-    pid = match.group(2)
-    time = match.group(3)
+    pid = int(match.group(2))
+    time = float(match.group(3))
     dev = match.group(4)
-    inode = match.group(5)
-    offset = int(match.group(6))
-    size = int(match.group(7))
+    inode = int(match.group(5))
+    lblk = int(match.group(6))
+    pblk = int(match.group(7)) * EXT4_SIZE_TO_BLOCK_SIZE # address in ext4 blocks, ...
+    l = int(match.group(8))
+    mflags = match.group(9)
+    ret = int(match.group(10))
+    if ret <= 0: # no block access
+      return
+    process_name = self.process_names.get(pid, process_name)
+    if process_name == '<...>':
+      process_name = "pid-" + str(pid)
+    if DBG_ISSUE:
+      print "ext4", pblk, l, inode, process_name
     files = self.files_per_device.get(dev)
     if not files:
       if DEVICE_TO_PARTITION.get(dev):
@@ -138,45 +306,123 @@
         self.files_per_device[dev] = files
       else:
         if DBG:
-          print "read ignored for device", dev
+          print "access ignored for device", dev
         return
     fevent = files.get(inode)
     if not fevent:
       if DBG:
         print 'no open for device %s with inode %s' % (dev, inode)
-      fevent = FileEvent(time, "unknown", process_name, inode, 0)
+      fevent = FileEvent(time, "unknown", process_name, inode, "-")
       files[inode] = fevent
-    fevent.add_read(time, offset, size, process_name + "-" + pid)
+    pending_access = PendingAccess(process_name, pid, time, dev, inode, lblk, pblk, l,\
+                                   fevent)
+    access_list = self.pending_accesses.get(pblk, [])
+    access_list.append(pending_access)
+    self.pending_accesses[pblk] = access_list
 
+  def handle_bio_remap(self, match):
+    new_addr = int(match.group(1))
+    old_addr = int(match.group(2))
+    self.remap[new_addr] = old_addr
+    if DBG_ISSUE:
+      print "remap", new_addr, old_addr
+
+  def handle_block_issue(self, match):
+    pid = int(match.group(1))
+    time = float(match.group(2))
+    dev_major = match.group(3)
+    dev_minor = match.group(4)
+    access = match.group(5)
+    new_address = int(match.group(6))
+    l = int(match.group(7))
+    name = match.group(8)
+    name = self.process_names.get(pid, name)
+    if name == '<...>':
+      name = "pid-" + str(pid)
+    is_read = not 'W' in access
+    accesses_per_inodes = {} # K:inodes, V: list of two entries, 1st: offsets, 2nd: length
+    addrs_to_remove = []
+    completed_reqs = []
+    address = self.remap.get(new_address, new_address)
+    if DBG_ISSUE:
+      print "issue", address, l, is_read, access
+    for access_addr, access_list in self.pending_accesses.iteritems():
+      if (address >= access_addr) and (address + l) > access_addr:
+        reqs_to_remove = []
+        for pending in access_list:
+          offset, valid_access_size = pending.get_valid_access(address - access_addr, l)
+          if valid_access_size > 0:
+            if pending.is_req_started(): # spread across multiple reads. complete alone
+              pending.queue_block_access(offset, valid_access_size)
+              if pending.is_req_complete():
+                pending.handle_req_complete(time, is_read)
+                reqs_to_remove.append(pending)
+            else: # possible multiple reads completed in this read. complete them together
+              pending.queue_block_access(offset, valid_access_size)
+              if pending.is_req_complete():
+                reads = accesses_per_inodes.get(pending.inode, [[], [], []])
+                reads[0].append(offset + pending.lblk)
+                reads[1].append(valid_access_size)
+                reads[2].append(pending.process_name)
+                accesses_per_inodes[pending.inode] = reads
+                completed_reqs.append(pending)
+                reqs_to_remove.append(pending)
+        for to_remove in reqs_to_remove:
+          access_list.remove(to_remove)
+        if len(access_list) == 0:
+          addrs_to_remove.append(access_addr)
+    for addr in addrs_to_remove:
+      del self.pending_accesses[addr]
+    for pending in completed_reqs: # these will be reported as batch
+      accesses = accesses_per_inodes.get(pending.inode)
+      if not accesses: # merged one already dispatched
+        continue
+      if len(accesses[0]) == 1:
+        pending.handle_req_complete(time, is_read)
+      else: #merged
+        pending.handle_merged_req(time, accesses[0], accesses[1], accesses[2], is_read)
+        del accesses_per_inodes[pending.inode]
 
   def dump_partition(self, partition_name, files):
-    print "**Dump partition:", partition_name, "toal number of files:", len(files)
+    name_to_pid_map = {}
+    for pid, name in self.process_names.iteritems():
+      name_to_pid_map[name] = str(pid)
+    print "**Dump partition:", partition_name, "total number of files:", len(files)
     total_reads = 0
-    total_rereads = 0
-    vs = files.values()
-    vs.sort(key=lambda f : f.total_reads, reverse = True)
-    for f in vs:
-      f.dump()
-      total_reads += f.total_reads
-      total_rereads += f.total_rereads
-    print " Total reads for partition", total_reads, "rereads", total_rereads
-    return total_reads, total_rereads, len(files)
+    total_writes = 0
+    files_sorted_by_read = files.values()
+    files_sorted_by_read.sort(key=lambda f : f.read.total_access, reverse = True)
+    files_sorted_by_write = files.values()
+    files_sorted_by_write.sort(key=lambda f : f.write.total_access, reverse = True)
+    print " Top 10 readers:"
+    for i in range(min(10, len(files_sorted_by_read))):
+      files_sorted_by_read[i].dump_short()
+    print " Top 10 writers:"
+    for i in range(min(10, len(files_sorted_by_write))):
+      files_sorted_by_write[i].dump_short()
+    for f in files_sorted_by_read:
+      f.dump(name_to_pid_map)
+      total_reads += f.read.total_access
+      total_writes += f.write.total_access
+    print " Total reads:", total_reads, " total writes:", total_writes
+    return total_reads, total_writes, len(files)
 
 
   def dump(self):
-    print "Dump read per each partition"
+    print "*Dump R/W per each partition"
     total_reads = 0
-    total_rereads = 0
+    total_writes = 0
     summaries = []
     for d in self.files_per_device:
-      reads, rereads, num_files = self.dump_partition(DEVICE_TO_PARTITION[d], \
+      reads, writes, num_files = self.dump_partition(DEVICE_TO_PARTITION[d], \
         self.files_per_device[d])
       total_reads += reads
-      total_rereads += rereads
-      summaries.append((DEVICE_TO_PARTITION[d], reads, rereads, num_files))
-    print "**Summary**"
-    print "Total blocks read", total_reads, "reread", total_rereads
-    print "Partition total_reads total_rereads num_files"
+      total_writes += writes
+      summaries.append((DEVICE_TO_PARTITION[d], reads, writes, num_files))
+    print "*Summary*"
+    print "Total blocks read", total_reads
+    print "Total blocks wrote", total_writes
+    print "Partition total_reads total_writes num_files"
     for s in summaries:
       print s[0], s[1], s[2], s[3]
 
diff --git a/tools/io_analysis/check_io_trace_all.py b/tools/io_analysis/check_io_trace_all.py
index 586a677..8ea466d 100644
--- a/tools/io_analysis/check_io_trace_all.py
+++ b/tools/io_analysis/check_io_trace_all.py
@@ -22,7 +22,12 @@
 import string
 import sys
 
-RE_BLOCK = r'.+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+block_bio_queue:\s+([0-9]+)\,([0-9]+)\s+([RW]\S*)\s+([0-9]+)\s+\+\s+([0-9]+)\s+\[([^\]]+)'
+# ex) <...>-52    [001] ...1     1.362574: block_bio_queue: 8,16 R 0 + 8 [kworker/u8:1]
+RE_BLOCK = r'.+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+block_bio_queue:\s+([0-9]+)\,([0-9]+)\s(\S+)\s+([0-9]+)\s+\+\s+([0-9]+)\s+\[([^\]]+)'
+# ex)  <...>-453   [001] d..4     3.181854: sched_blocked_reason: pid=471 iowait=1 caller=__wait_on_buffer+0x24/0x2c
+RE_SCHED_BLOCKED_READSON = r'.+-([0-9]+)\s+\[([0-9]+)\]\s.*\s+([0-9]+\.[0-9]+):\s+sched_blocked_reason:\spid=([0-9]+)\siowait=([01])\scaller=(\S+)'
+# ex) <idle>-0     [000] d..3     3.181864: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ueventd next_pid=471 next_prio=120
+RE_SCHED_SWITCH = r'.+-([0-9]+)\s+\[([0-9]+)\]\s.*\s+([0-9]+\.[0-9]+):\s+sched_switch:\sprev_comm=(.+)\sprev_pid=([0-9]+)\sprev_prio=([0-9]+)\sprev_state=(\S+).*next_comm=(.+)\snext_pid=([0-9]+)\snext_prio=([0-9]+)'
 
 # dev_num = major * MULTIPLIER + minor
 DEV_MAJOR_MULTIPLIER = 1000
@@ -56,6 +61,7 @@
     self.total_dm_reads = 0
     self.total_dm_writes = 0
 
+
   def add_read_event(self, major, minor, event):
     devNum = major * DEV_MAJOR_MULTIPLIER + minor;
     events = self.reads.get(devNum)
@@ -104,7 +110,7 @@
       dev = sorted_w.popitem(last=False)
       print " ", dev[0],dev[1]
 
-class Trace:
+class IoTrace:
 
   def __init__(self):
     self.ios = {} #K: process name, v:ProcessData
@@ -114,10 +120,22 @@
     self.total_writes_per_device = {}
     self.total_dm_reads = {} #K: devnum, V: blocks
     self.total_dm_writes = {}
+    self.re_block = re.compile(RE_BLOCK)
 
-  def parse_bio_queue(self, l, match):
+  def parse(self, l):
+    match = self.re_block.match(l)
+    if not match:
+      return False
+    try:
+      self.do_parse_bio_queue(l, match)
+    except ValueError:
+      print "cannot parse:", l
+      raise
+    return True
+
+  def do_parse_bio_queue(self, l, match):
     pid = match.group(1)
-    start_time = int(float(match.group(2))*1000000) #us
+    start_time = float(match.group(2))*1000 #ms
     major = int(match.group(3))
     minor =  int(match.group(4))
     devNum = major * DEV_MAJOR_MULTIPLIER + minor;
@@ -132,25 +150,25 @@
       self.ios[process] = io
     if major == DM_MAJOR:
       devNum = major * DEV_MAJOR_MULTIPLIER + minor;
-      if operation[0] == 'R':
+      if 'R' in operation[0]:
         if devNum not in self.total_dm_reads:
           self.total_dm_reads[devNum] = 0
         self.total_dm_reads[devNum] += size
         io.add_dm_read(size)
-      elif operation[0] == 'W':
+      elif 'W' in operation[0]:
         if devNum not in self.total_dm_writes:
           self.total_dm_writes[devNum] = 0
         self.total_dm_writes[devNum] += size
         io.add_dm_write(size)
       return
-    if operation[0] == 'R':
+    if 'R' in operation[0]:
       io.add_read_event(major, minor, event)
       self.total_reads += size
       per_device = self.total_reads_per_device.get(devNum)
       if not per_device:
         self.total_reads_per_device[devNum] = 0
       self.total_reads_per_device[devNum] += size
-    elif operation[0] == 'W':
+    elif 'W' in operation[0]:
       io.add_write_event(major, minor, event)
       self.total_writes += size
       per_device = self.total_writes_per_device.get(devNum)
@@ -158,13 +176,6 @@
         self.total_writes_per_device[devNum] = 0
       self.total_writes_per_device[devNum] += size
 
-  def parse_block_trace(self, l, match):
-    try:
-      self.parse_bio_queue(l, match)
-    except ValueError:
-      print "cannot parse:", l
-      raise
-
   def dump(self):
     print "total read blocks,", self.total_reads
     print "total write blocks,", self.total_writes
@@ -206,20 +217,170 @@
       dev = sorted_by_total_w.popitem(last=False)
       print dev[0],dev[1]
 
+class SchedProcess:
+  def __init__(self, pid):
+    self.pid = pid
+    self.name = "unknown"
+    self.total_execution_time = 0.0
+    self.total_io_wait_time = 0.0
+    self.total_other_wait_time = 0.0
+    self.waiting_calls = {} # k: waiting_call, v : waiting counter
+    self.io_waiting_call_times = {} # k: waiting_call, v: total wait time
+    self.in_iowait = False
+    self.last_waiting_call = None
+    self.last_switch_out_time = 0.0
+    self.last_switch_in_time = 0.0
+    self.last_core = -1
+    self.execution_time_per_core = {} # k: core, v : time
+    self.io_latency_histograms = {} # k : delay in ms, v : count
+
+  def handle_reason(self, current_time, iowait, waiting_call):
+    #if self.pid == 1232:
+    #  print current_time, iowait, waiting_call
+    if iowait == 1:
+      self.in_iowait = True
+    self.last_waiting_call = waiting_call
+    call_counter = self.waiting_calls.get(waiting_call, 0)
+    call_counter += 1
+    self.waiting_calls[waiting_call] = call_counter
+
+  def handle_switch_out(self, current_time, out_state, priority, name, core):
+    #if self.pid == 1232:
+    #  print "out", current_time, out_state
+    if self.name != name:
+      self.name = name
+    self.last_switch_out_time = current_time
+    if self.last_switch_in_time == 0.0: # switch in not recorded. ignore this one
+      return
+    execution_time = current_time - self.last_switch_in_time
+    self.total_execution_time += execution_time
+    core_execution_time = self.execution_time_per_core.get(core, 0.0)
+    core_execution_time += execution_time
+    self.execution_time_per_core[core] = core_execution_time
+
+  def handle_switch_in(self, current_time, priority, name, core):
+    #if self.pid == 1232:
+    #  print "in", current_time, self.in_iowait
+    if self.name != name:
+      self.name = name
+    self.last_switch_in_time = current_time
+    if self.last_switch_out_time == 0.0: # in without out, probably 1st
+      self.in_iowait = False
+      return
+    wait_time = current_time - self.last_switch_out_time
+    if self.in_iowait:
+      self.total_io_wait_time += wait_time
+      total_waiting_call_time = self.io_waiting_call_times.get(self.last_waiting_call, 0.0)
+      total_waiting_call_time += wait_time
+      self.io_waiting_call_times[self.last_waiting_call] = total_waiting_call_time
+      wait_time_ms = int(wait_time*10) / 10.0 # resolution up to 0.1 ms
+      histogram_count = self.io_latency_histograms.get(wait_time_ms, 0)
+      histogram_count += 1
+      self.io_latency_histograms[wait_time_ms] = histogram_count
+    else:
+      self.total_other_wait_time += wait_time
+    self.in_iowait = False
+
+
+  def dump(self):
+    print "PID:", self.pid, " name:", self.name
+    print " total execution time:", self.total_execution_time,\
+      " io wait:", self.total_io_wait_time, " other wait:", self.total_other_wait_time
+    sorted_data = collections.OrderedDict(sorted(self.execution_time_per_core.items(), \
+      key = lambda item: item[0], reverse = False))
+    print " Core execution:", sorted_data
+    sorted_data = collections.OrderedDict(sorted(self.waiting_calls.items(), \
+      key = lambda item: item[1], reverse = True))
+    print " Wait calls:", sorted_data
+    sorted_data = collections.OrderedDict(sorted(self.io_waiting_call_times.items(), \
+      key = lambda item: item[1], reverse = True))
+    print " IO Wait time per wait calls:", sorted_data
+    sorted_data = collections.OrderedDict(sorted(self.io_latency_histograms.items(), \
+      key = lambda item: item[0], reverse = False))
+    print " Wait time histogram:", sorted_data
+
+class SchedTrace:
+  def __init__(self):
+    self.re_switch = re.compile(RE_SCHED_SWITCH)
+    self.re_reason = re.compile(RE_SCHED_BLOCKED_READSON)
+    self.processes = {} # key: pid, v : SchedProcess
+
+  def parse(self, l):
+    checked_reason = False
+    match = self.re_switch.match(l)
+    if not match:
+      match = self.re_reason.match(l)
+      checked_reason = True
+    if not match:
+      return False
+    try:
+      if checked_reason:
+        self.do_handle_reason(l, match)
+      else:
+        self.do_handle_switch(l, match)
+    except ValueError:
+      print "cannot parse:", l
+      raise
+    return True
+
+  def do_handle_switch(self, l, match):
+    current_pid = int(match.group(1))
+    cpu_core = int(match.group(2))
+    current_time = float(match.group(3))*1000 #ms
+    out_name = match.group(4)
+    out_pid = int(match.group(5))
+    out_prio = int(match.group(6))
+    out_state = match.group(7)
+    in_name = match.group(8)
+    in_pid = int(match.group(9))
+    in_prio = int(match.group(10))
+    out_process = self.processes.get(out_pid)
+    if not out_process:
+      out_process = SchedProcess(out_pid)
+      self.processes[out_pid] = out_process
+    in_process = self.processes.get(in_pid)
+    if not in_process:
+      in_process = SchedProcess(in_pid)
+      self.processes[in_pid] = in_process
+    out_process.handle_switch_out(current_time, out_state, out_prio, out_name, cpu_core)
+    in_process.handle_switch_in(current_time, in_prio, in_name, cpu_core)
+
+  def do_handle_reason(self, l, match):
+    current_pid = int(match.group(1))
+    cpu_core = int(match.group(2))
+    current_time = float(match.group(3))*1000 #ms
+    pid = int(match.group(4))
+    iowait = int(match.group(5))
+    waiting_call = match.group(6)
+    process = self.processes.get(pid)
+    if not process:
+      process = SchedProcess(pid)
+      self.processes[pid] = process
+    process.handle_reason(current_time, iowait, waiting_call)
+
+  def dump(self):
+    sorted_by_total_execution = collections.OrderedDict(sorted(self.processes.items(), \
+      key = lambda item: item[1].total_io_wait_time, reverse = True))
+    for k, v in sorted_by_total_execution.iteritems():
+      if v.total_execution_time > 10.0 or v.total_io_wait_time != 0.0:
+        v.dump()
+
 def main(argv):
   if (len(argv) < 2):
     print "check_io_trace_all.py filename"
     return
   filename = argv[1]
 
-  trace = Trace()
-  prog = re.compile(RE_BLOCK)
+  io_trace = IoTrace()
+  sched_trace = SchedTrace()
   with open(filename) as f:
     for l in f:
-      result = prog.match(l)
-      if result:
-        trace.parse_block_trace(l, result)
-  trace.dump()
+      if io_trace.parse(l):
+        continue
+      sched_trace.parse(l)
+  io_trace.dump()
+  print "\n\n\n"
+  sched_trace.dump()
 
 if __name__ == '__main__':
   main(sys.argv)
diff --git a/tools/update-obd2-sensors.py b/tools/update-obd2-sensors.py
index 7f22f52..e161258 100755
--- a/tools/update-obd2-sensors.py
+++ b/tools/update-obd2-sensors.py
@@ -109,22 +109,51 @@
             str(sensorId) + ";"
 
     def prefix(self, theSensors):
-        s = "    public static final class Obd2%sSensorIndex {\n" % \
-            theSensors.descriptor
-        s += "        private Obd2%sSensorIndex() {}\n" % \
-            theSensors.descriptor
+        s = \
+"/*\n" + \
+" * Copyright (C) 2017 The Android Open Source Project\n" + \
+" *\n" + \
+" * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + \
+" * you may not use this file except in compliance with the License.\n" + \
+" * You may obtain a copy of the License at\n" + \
+" *\n" + \
+" *      http://www.apache.org/licenses/LICENSE-2.0\n" + \
+" *\n" + \
+" * Unless required by applicable law or agreed to in writing, software\n" + \
+" * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + \
+" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + \
+" * See the License for the specific language governing permissions and\n" + \
+" * limitations under the License.\n" + \
+"*/\n" + \
+"\n" + \
+"package android.car.diagnostic;\n" + \
+"\n" + \
+"import android.annotation.IntDef;\n" + \
+"import android.annotation.SystemApi;\n" + \
+"import java.lang.annotation.Retention;\n" + \
+"import java.lang.annotation.RetentionPolicy;\n" + \
+"\n" + \
+"/**\n" + \
+" * This class is a container for the indices of diagnostic sensors. The values are extracted by\n" + \
+" * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.\n" + \
+" *\n" + \
+" * DO NOT EDIT MANUALLY\n" + \
+" *\n" + \
+" * @hide\n" + \
+" */\n" + \
+"@SystemApi\n" + \
+"public final class %sSensorIndex {\n" % theSensors.descriptor + \
+"    private %sSensorIndex() {}\n" % theSensors.descriptor
+
         return s
 
-    def suffix(self, theSensors):
-        return "    }"
-
     def indent(self):
-        return 8
+        return 4
 
 class PythonSensorPolicy(SensorPolicy):
     """The sensor policy that emits Python sensor descriptions."""
     def sensor(self, theSensor, theSensors):
-        return "OBD2_SENSOR_%s_%s = %s" % (
+        return "DIAGNOSTIC_SENSOR_%s_%s = %s" % (
             theSensors.descriptor.upper(),
             theSensor.name.upper(),
             self.adjustSensorId(theSensors.descriptor.upper(), str(theSensor.id))
@@ -132,23 +161,22 @@
 
     def adjustSensorId(self, descriptor, sensorId):
         if sensorId.isdigit(): return sensorId
-        return "OBD2_SENSOR_%s_%s" % (descriptor, sensorId.upper())
+        return "DIAGNOSTIC_SENSOR_%s_%s" % (descriptor, sensorId.upper())
 
 class IntDefSensorPolicy(SensorPolicy):
     """The sensor policy that emits @IntDef sensor descriptions."""
     def sensor(self, theSensor, theSensors):
         sensorName = theSensor.name.replace("_INDEX", "")
-        return "Obd2%sSensorIndex.%s," % (theSensors.descriptor,sensorName)
+        return "%sSensorIndex.%s," % (theSensors.descriptor,sensorName)
 
     def prefix(self, theSensors):
-        return "    @Retention(RetentionPolicy.SOURCE)\n    @IntDef({"
+        return "    /** @hide */\n    @Retention(RetentionPolicy.SOURCE)\n    @IntDef({"
 
     def indent(self):
         return 8
 
     def suffix(self, theSensors):
-        return "    })\n    public @interface %sSensorIndex {}" % \
-            theSensors.descriptor
+        return "    })\n    public @interface SensorIndex {}"
 
 class SensorMeta(type):
     """Metaclass for sensor classes."""
@@ -182,7 +210,15 @@
 
 def applyPolicy(policy, destfile):
     """Given a sensor policy, apply it to all known sensor types"""
+    applyIntPolicy(policy, destfile)
+    applyFloatPolicy(policy, destfile)
+
+def applyIntPolicy(policy, destfile):
+    "Given a sensor policy, apply it to integer sensors"
     print(policy.sensors(intSensors), file=destfile)
+
+def applyFloatPolicy(policy, destfile):
+    "Given a sensor policy, apply it to float sensors"
     print(policy.sensors(floatSensors), file=destfile)
 
 def java(destfile):
@@ -196,41 +232,18 @@
 
 def generateJava(filepath):
     """Generate Java code for all sensors."""
-    destfile = open(filepath, "w")
-    print("/*", file=destfile)
-    print(" * Copyright (C) 2017 The Android Open Source Project", file=destfile)
-    print(" *", file=destfile)
-    print(" * Licensed under the Apache License, Version 2.0 (the \"License\");", file=destfile)
-    print(" * you may not use this file except in compliance with the License.", file=destfile)
-    print(" * You may obtain a copy of the License at", file=destfile)
-    print(" *", file=destfile)
-    print(" *      http://www.apache.org/licenses/LICENSE-2.0", file=destfile)
-    print(" *", file=destfile)
-    print(" * Unless required by applicable law or agreed to in writing, software", file=destfile)
-    print(" * distributed under the License is distributed on an \"AS IS\" BASIS,", file=destfile)
-    print(" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=destfile)
-    print(" * See the License for the specific language governing permissions and", file=destfile)
-    print(" * limitations under the License.", file=destfile)
-    print("*/", file=destfile)
-    print("", file=destfile)
-    print("package android.car.hardware;", file=destfile)
-    print("", file=destfile)
-    print("import android.annotation.IntDef;", file=destfile)
-    print("import java.lang.annotation.Retention;", file=destfile)
-    print("import java.lang.annotation.RetentionPolicy;", file=destfile)
-    print("", file=destfile)
-    print("/**", file=destfile)
-    print(" * This class is a container for the indices of integer and float diagnostic sensors.", file=destfile)
-    print(" * These values are extracted from types.hal by packages/services/Car/tools/update-obd2-sensors.py", file=destfile)
-    print(" *", file=destfile)
-    print(" * DO NOT EDIT MANUALLY", file=destfile)
-    print(" *", file=destfile)
-    print(" * @hide", file=destfile)
-    print(" */", file=destfile)
-    print("public final class CarDiagnosticSensorIndices {", file=destfile)
-    java(destfile)
-    intdef(destfile)
-    print("}", file=destfile)
+    intfile = open(os.path.join(filepath, "IntegerSensorIndex.java"), "w")
+    floatfile = open(os.path.join(filepath, "FloatSensorIndex.java"), "w")
+    javaPolicy = JavaSensorPolicy()
+    intdefPolicy = IntDefSensorPolicy()
+    applyIntPolicy(javaPolicy, intfile)
+    applyIntPolicy(intdefPolicy, intfile)
+    applyFloatPolicy(javaPolicy, floatfile)
+    applyFloatPolicy(intdefPolicy, floatfile)
+    print("}", file=intfile)
+    print("}", file=floatfile)
+    intfile.close()
+    floatfile.close()
 
 def generatePython(filepath):
     """Generate Python code for all sensors."""
@@ -258,8 +271,8 @@
 def load(filepath):
     """Load sensor data from Vehicle HAL."""
     ast = hidl_parser.parser.parse(filepath)
-    integerSensors = ast['enums']['Obd2IntegerSensorIndex']
-    floatSensors = ast['enums']['Obd2FloatSensorIndex']
+    integerSensors = ast['enums']['DiagnosticIntegerSensorIndex']
+    floatSensors = ast['enums']['DiagnosticFloatSensorIndex']
     for case in integerSensors.cases:
         intSensor(name=case.name, id=case.value)
     for case in floatSensors.cases:
@@ -268,9 +281,9 @@
 import os
 
 if len(sys.argv) != 4:
-    print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/CarDiagnosticSensorIndices.java> <path/to/diagnostic_sensors.py>')
+    print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/android.car.diagnostic> <path/to/diagnostic_sensors.py>')
     print('This script will parse types.hal, and use the resulting', end='')
-    print('parse tree to generate CarDiagnosticSensorIndices.java.')
+    print('parse tree to generate Java and Python lists of sensor identifiers.')
     sys.exit(1)
 load(sys.argv[1])
 generateJava(sys.argv[2])
diff --git a/vehicle-hal-support-lib/Android.mk b/vehicle-hal-support-lib/Android.mk
index 9ff39c6..cd24b59 100644
--- a/vehicle-hal-support-lib/Android.mk
+++ b/vehicle-hal-support-lib/Android.mk
@@ -26,7 +26,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android.hidl.base-V1.0-java \
     android.hardware.automotive.vehicle-V2.0-java \
-    android.hardware.automotive.vehicle-V2.1-java \
     junit \
     legacy-android-test
 
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java
index 1e0c237..d000e7a 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java
@@ -18,8 +18,8 @@
 
 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.Obd2FloatSensorIndex;
-import android.hardware.automotive.vehicle.V2_1.Obd2IntegerSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticFloatSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticIntegerSensorIndex;
 import android.util.SparseArray;
 import java.util.BitSet;
 import java.util.Iterator;
@@ -109,9 +109,9 @@
     public DiagnosticEventBuilder(
             int propertyId, int numVendorIntSensors, int numVendorFloatSensors) {
         mPropertyId = propertyId;
-        mNumIntSensors = Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorIntSensors;
+        mNumIntSensors = DiagnosticIntegerSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorIntSensors;
         final int numFloatSensors =
-                Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorFloatSensors;
+                DiagnosticFloatSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorFloatSensors;
         mBitmask = new BitSet(mNumIntSensors + numFloatSensors);
         mIntValues = new DefaultedArray<>(mNumIntSensors, 0);
         mFloatValues = new DefaultedArray<>(numFloatSensors, 0.0f);
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
index 5dd6a66..b3d0c02 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
@@ -16,8 +16,8 @@
 
 package com.android.car.vehiclehal;
 
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_FREEZE_FRAME;
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_LIVE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_FREEZE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_LIVE_FRAME;
 
 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
index 1a5b8c6..0da3565 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
@@ -21,8 +21,6 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.fail;
 
-import com.google.android.collect.Lists;
-
 import android.hardware.automotive.vehicle.V2_0.IVehicle;
 import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
 import android.hardware.automotive.vehicle.V2_0.StatusCode;
@@ -31,6 +29,9 @@
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
 import android.os.RemoteException;
+import android.os.SystemClock;
+
+import com.google.android.collect.Lists;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -74,6 +75,23 @@
         addProperty(config, new StaticPropertyHandler(value));
     }
 
+    public boolean waitForSubscriber(int propId, long timeoutMillis) {
+        long startTime = SystemClock.elapsedRealtime();
+        try {
+            synchronized (this) {
+                while (mSubscribers.get(propId) == null) {
+                    long waitMillis = startTime - SystemClock.elapsedRealtime() + timeoutMillis;
+                    if (waitMillis < 0) break;
+                    wait(waitMillis);
+                }
+
+                return mSubscribers.get(propId) != null;
+            }
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
     public synchronized void injectEvent(VehiclePropValue value) {
         List<IVehicleCallback> callbacks = mSubscribers.get(value.prop);
         assertNotNull("Injecting event failed for property: " + value.prop
@@ -156,6 +174,7 @@
             if (subscribers == null) {
                 subscribers = new ArrayList<>();
                 mSubscribers.put(opt.propId, subscribers);
+                notifyAll();
             }
             subscribers.add(callback);
         }