Merge "Fix bootanalyze reading of dmesg"
diff --git a/car-lib/src/android/car/vms/IVmsPublisherService.aidl b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
index 26b6e52..3312794 100644
--- a/car-lib/src/android/car/vms/IVmsPublisherService.aidl
+++ b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
@@ -40,4 +40,11 @@
      * 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/IVmsSubscriberService.aidl b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
index 236ae5a..9234134 100644
--- a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
+++ b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
@@ -52,8 +52,12 @@
             in IVmsSubscriberClient listener) = 3;
 
     /**
-     * Tells the VmsSubscriberService a client requests the list of available layers.
-     * The service should call the client's onLayersAvailabilityChange in response.
+     * 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/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java
index 85fd2c2..ea265df 100644
--- a/car-lib/src/android/car/vms/VmsPublisherClientService.java
+++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java
@@ -155,6 +155,23 @@
         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.
diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java
index 640948a..84405f4 100644
--- a/car-lib/src/android/car/vms/VmsSubscriberManager.java
+++ b/car-lib/src/android/car/vms/VmsSubscriberManager.java
@@ -157,6 +157,24 @@
     }
 
     /**
+     * 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.
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index 6eaf3a5..658992b 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -84,12 +84,10 @@
 
 # EVS resources
 PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-service
+PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-sample
 PRODUCT_PACKAGES += android.automotive.evs.manager@1.0
 PRODUCT_PACKAGES += evs_app
-PRODUCT_COPY_FILES += \
-    packages/services/Car/evs/app/config.json:system/etc/automotive/evs/config.json \
-    packages/services/Car/evs/app/CarFromTop.png:system/etc/automotive/evs/CarFromTop.png \
-    packages/services/Car/evs/app/LabeledChecker.png:system/etc/automotive/evs/LabeledChecker.png
+PRODUCT_PACKAGES += evs_app_resources
 
 # Device running Android is a car
 PRODUCT_COPY_FILES += \
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action.xml
index a5a670b..cefd830 100644
--- a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2014 The Android Open Source Project
+  ~ 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.
@@ -14,18 +14,21 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-
-<Button xmlns:android="http://schemas.android.com/apk/res/android"
+<Button
+    xmlns:android="http://schemas.android.com/apk/res/android"
     style="@android:style/Widget.Material.Light.Button.Borderless.Small"
     android:id="@+id/action0"
     android:layout_width="wrap_content"
-    android:layout_height="48dp"
+    android:layout_height="match_parent"
     android:layout_gravity="center"
+    android:fontFamily="sans-serif"
     android:gravity="start|center_vertical"
-    android:layout_marginStart="4dp"
+    android:layout_marginStart="0dp"
     android:textColor="@color/notification_default_color"
-    android:textSize="24sp"
+    android:textSize="@dimen/notification_text_size"
+    android:textStyle="normal"
     android:singleLine="true"
     android:ellipsize="end"
-    android:background="@drawable/notification_material_action_background"
-    />
+    android:paddingStart="@dimen/notification_content_margin_start"
+    android:paddingEnd="@dimen/notification_content_margin_start"
+    android:background="@drawable/notification_material_action_background" />
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_emphasized.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_emphasized.xml
deleted file mode 100644
index f4a0e12..0000000
--- a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_emphasized.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?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
-  -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/button_holder"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_weight="1"
-    android:background="#ff000000">
-    <Button
-        style="@android:style/Widget.Material.Light.Button.Borderless.Small"
-        android:id="@+id/action0"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:gravity="center"
-        android:textColor="@color/notification_default_color"
-        android:textSize="24sp"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:background="@drawable/notification_material_action_background"
-        />
-</FrameLayout>
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_list.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_list.xml
deleted file mode 100644
index 5488c43..0000000
--- a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_list.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/actions_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom">
-    <com.android.internal.widget.NotificationActionListLayout
-            android:id="@+id/actions"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_action_list_height"
-            android:paddingEnd="4dp"
-            android:orientation="horizontal"
-            android:gravity="center_vertical"
-            android:visibility="gone"
-            android:background="@drawable/notification_action_bg"
-            >
-        <!-- actions will be added here -->
-    </com.android.internal.widget.NotificationActionListLayout>
-</FrameLayout>
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_tombstone.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_tombstone.xml
deleted file mode 100644
index ea184ef..0000000
--- a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action_tombstone.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 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
-  -->
-
-<Button xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@android:style/Widget.Material.Light.Button.Borderless.Small"
-    android:id="@+id/action0"
-    android:layout_width="wrap_content"
-    android:layout_height="48dp"
-    android:layout_marginStart="4dp"
-    android:layout_gravity="center"
-    android:gravity="start|center_vertical"
-    android:textColor="#555555"
-    android:textSize="24sp"
-    android:singleLine="true"
-    android:ellipsize="end"
-    android:alpha="0.5"
-    android:enabled="false"
-    android:background="@drawable/notification_material_action_background"
-    />
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_media_action.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_media_action.xml
new file mode 100644
index 0000000..29aafd4
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_media_action.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.
+-->
+<ImageButton
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@android:style/Widget.Material.Button.Borderless.Small"
+    android:id="@+id/action0"
+    android:background="@drawable/notification_material_media_action_background"
+    android:layout_width="0dp"
+    android:layout_height="@dimen/media_notification_action_button_size"
+    android:layout_weight="1"
+    android:padding="0dp"
+    android:gravity="center"
+    android:scaleType="fitCenter" />
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-h600dp/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values-h600dp/dimens.xml
index b10cb31..b659bef 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-h600dp/dimens.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-h600dp/dimens.xml
@@ -30,4 +30,10 @@
          is smaller than the notification_header_height to bring the text closer. Otherwise,
          spacing in the font itself makes the space look too large. -->
     <dimen name="notification_content_margin_top">68dp</dimen>
+
+    <!-- The height of the notification action list. -->
+    <dimen name="notification_action_list_height">96dp</dimen>
+
+    <!-- The size of the media actions in the media notification. -->
+    <dimen name="media_notification_action_button_size">56dp</dimen>
 </resources>
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
new file mode 100644
index 0000000..2774c9e
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
@@ -0,0 +1,22 @@
+<?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>
+    <!-- The background color for the container of notification actions. -->
+    <color name="notification_action_list">#ff11181d</color>   <!-- Dark Blue Grey 800 -->
+</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
new file mode 100644
index 0000000..f7a7a12
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?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>
+    <!-- The background color for the container of notification actions. -->
+    <color name="notification_action_list">#ffeeeeee</color>  <!-- Grey 200 -->
+</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 553c890..125e1fb 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
@@ -89,4 +89,19 @@
 
     <!-- The margin on top of the text of the notification. -->
     <dimen name="notification_text_margin_top">0dp</dimen>
+
+    <!-- The height of the notification action list. -->
+    <dimen name="notification_action_list_height">76dp</dimen>
+
+    <!-- The size of the media actions in the media notification. -->
+    <dimen name="media_notification_action_button_size">36dp</dimen>
+
+    <!-- The bottom padding for the media actions container. -->
+    <dimen name="media_notification_actions_padding_bottom">0dp</dimen>
+
+    <!-- The height of the progress bar. -->
+    <dimen name="notification_progress_bar_height">25dp</dimen>
+
+    <!-- The top margin before the notification progress bar.  -->
+    <dimen name="notification_progress_margin_top">16dp</dimen>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/styles.xml b/car_product/overlay/frameworks/base/core/res/res/values/styles.xml
index de6f6ec..7bcedb9 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/styles.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/styles.xml
@@ -64,4 +64,15 @@
         <item name="fragmentFadeEnterAnimation">@animator/fragment_fade_enter</item>
         <item name="fragmentFadeExitAnimation">@animator/fragment_fade_exit</item>
     </style>
+
+    <!-- The style for the container of media actions in a notification. -->
+    <style name="NotificationMediaActionContainer">
+        <item name="background">@color/notification_action_list</item>
+        <item name="layout_width">match_parent</item>
+        <item name="layout_height">@dimen/notification_action_list_height</item>
+        <item name="layout_marginTop">0dp</item>
+        <item name="paddingStart">0dp</item>
+        <item name="paddingBottom">@dimen/media_notification_actions_padding_bottom</item>
+        <item name="gravity">center</item>
+    </style>
 </resources>
diff --git a/car_product/sepolicy/evs_app.te b/car_product/sepolicy/evs_app.te
new file mode 100644
index 0000000..0e8881e
--- /dev/null
+++ b/car_product/sepolicy/evs_app.te
@@ -0,0 +1,14 @@
+# 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
new file mode 100644
index 0000000..1307616
--- /dev/null
+++ b/car_product/sepolicy/evs_driver.te
@@ -0,0 +1,12 @@
+# 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
new file mode 100644
index 0000000..f5c4ba8
--- /dev/null
+++ b/car_product/sepolicy/evs_manager.te
@@ -0,0 +1,11 @@
+# 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
new file mode 100644
index 0000000..b89b1ba
--- /dev/null
+++ b/car_product/sepolicy/evs_mock.te
@@ -0,0 +1,11 @@
+# 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 25cbef2..53759c7 100644
--- a/car_product/sepolicy/file_contexts
+++ b/car_product/sepolicy/file_contexts
@@ -6,4 +6,11 @@
 
 /(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/evs/app/Android.mk b/evs/app/Android.mk
index 784068d..7a81cbe 100644
--- a/evs/app/Android.mk
+++ b/evs/app/Android.mk
@@ -43,6 +43,8 @@
 
 LOCAL_STRIP_MODULE := keep_symbols
 
+LOCAL_INIT_RC := evs_app.rc
+
 LOCAL_MODULE:= evs_app
 LOCAL_MODULE_TAGS := optional
 
@@ -50,3 +52,25 @@
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
 include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := config.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH  := $(TARGET_ROOT_OUT)/system/etc/automotive/evs
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CarFromTop.png
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH  := $(TARGET_ROOT_OUT)/system/etc/automotive/evs
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := LabeledChecker.png
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH  := $(TARGET_ROOT_OUT)/system/etc/automotive/evs
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/evs/app/RenderTopView.cpp b/evs/app/RenderTopView.cpp
index a44f2af..1579a0a 100644
--- a/evs/app/RenderTopView.cpp
+++ b/evs/app/RenderTopView.cpp
@@ -265,8 +265,6 @@
     glUniformMatrix4fv(loc, 1, false, orthoMatrix.asArray());
     glBindTexture(GL_TEXTURE_2D, mTexAssets.carTopView->glId());
 
-    printf("top view tex=%u\n", mTexAssets.carTopView->glId());
-
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
 
@@ -332,11 +330,9 @@
     } else {
         texId = mTexAssets.checkerBoard->glId();
     }
-    printf("projected tex=%u\n", texId);
     glBindTexture(GL_TEXTURE_2D, texId);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-    //   glDrawArrays(GL_LINE_STRIP, 0, 4);
 
 
     glDisableVertexAttribArray(0);
diff --git a/evs/app/config.json b/evs/app/config.json
index dedac44..5de8bb5 100644
--- a/evs/app/config.json
+++ b/evs/app/config.json
@@ -15,26 +15,26 @@
   },
   "cameras" : [
     {
-      "cameraId" : "/dev/video45",
+      "cameraId" : "/dev/video32",
       "function" : "reverse,park",
       "x" : 0.0,
       "y" : -40.0,
       "z" : 48,
       "yaw" : 180,
       "pitch" : -30,
-      "hfov" : 90,
-      "vfov" : 60
+      "hfov" : 125,
+      "vfov" :103 
     },
     {
-      "cameraId" : "/dev/video1",
+      "cameraId" : "/dev/video45",
       "function" : "front,park",
       "x" : 0.0,
       "y" : 100.0,
       "z" : 48,
       "yaw" : 0,
       "pitch" : -10,
-      "hfov" : 60,
-      "vfov" : 42
+      "hfov" : 70,
+      "vfov" : 43
     },
     {
       "cameraId" : "/dev/video0",
diff --git a/evs/app/evs_app.rc b/evs/app/evs_app.rc
new file mode 100644
index 0000000..f040ff6
--- /dev/null
+++ b/evs/app/evs_app.rc
@@ -0,0 +1,9 @@
+service evs_app /system/bin/evs_app
+    class hal
+    priority -20
+    user automotive_evs
+    group automotive_evs
+
+#on post-fs
+#on boot
+#    start evs_app
\ No newline at end of file
diff --git a/evs/manager/Android.mk b/evs/manager/Android.mk
index 912265e..fe384d9 100644
--- a/evs/manager/Android.mk
+++ b/evs/manager/Android.mk
@@ -21,6 +21,8 @@
     android.hardware.automotive.evs@1.0 \
 
 
+LOCAL_INIT_RC := android.automotive.evs.manager@1.0.rc
+
 LOCAL_MODULE := android.automotive.evs.manager@1.0
 
 LOCAL_MODULE_TAGS := optional
diff --git a/evs/manager/android.automotive.evs.manager@1.0.rc b/evs/manager/android.automotive.evs.manager@1.0.rc
new file mode 100644
index 0000000..98d8792
--- /dev/null
+++ b/evs/manager/android.automotive.evs.manager@1.0.rc
@@ -0,0 +1,10 @@
+service evs_manager /system/bin/android.automotive.evs.manager@1.0
+    class hal
+    priority -20
+    user automotive_evs
+    group automotive_evs
+    onrestart restart evs_app
+
+#on post-fs
+#on boot
+#    start evs_manager
diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 840c239..231ab6e 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -28,6 +28,7 @@
     liblog \
     libutils \
 
+LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc
 
 LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
 
diff --git a/evs/sampleDriver/EvsV4lCamera.cpp b/evs/sampleDriver/EvsV4lCamera.cpp
index b30a23f..045d7ab 100644
--- a/evs/sampleDriver/EvsV4lCamera.cpp
+++ b/evs/sampleDriver/EvsV4lCamera.cpp
@@ -161,7 +161,8 @@
     // Choose which image transfer function we need
     // Map from V4L2 to Android graphic buffer format
     const uint32_t videoSrcFormat = mVideo.getV4LFormat();
-    ALOGI("Configuring to accept %4.4s camera data and convert to NV21", (char*)&videoSrcFormat);
+    ALOGI("Configuring to accept %4.4s camera data and convert to %4.4s",
+          (char*)&videoSrcFormat, (char*)&mFormat);
 
     // TODO:  Simplify this by supporting only ONE fixed output format
     switch (mFormat) {
@@ -192,6 +193,7 @@
     case HAL_PIXEL_FORMAT_YCBCR_422_I:
         switch (videoSrcFormat) {
         case V4L2_PIX_FMT_YUYV:     mFillBufferFromVideo = fillYUYVFromYUYV;    break;
+        case V4L2_PIX_FMT_UYVY:     mFillBufferFromVideo = fillYUYVFromUYVY;    break;
         default:
             // TODO:  Are there other V4L2 formats we must support?
             ALOGE("Unhandled camera format %4.4s", (char*)&videoSrcFormat);
diff --git a/evs/sampleDriver/GlWrapper.cpp b/evs/sampleDriver/GlWrapper.cpp
index 3747f7f..a49eb20 100644
--- a/evs/sampleDriver/GlWrapper.cpp
+++ b/evs/sampleDriver/GlWrapper.cpp
@@ -197,7 +197,11 @@
     //
     status_t err;
 
-    mFlinger = new SurfaceComposerClient;
+    mFlinger = new SurfaceComposerClient();
+    if (mFlinger == nullptr) {
+        ALOGE("SurfaceComposerClient couldn't be allocated");
+        return false;
+    }
     err = mFlinger->initCheck();
     if (err != NO_ERROR) {
         ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
diff --git a/evs/sampleDriver/VideoCapture.cpp b/evs/sampleDriver/VideoCapture.cpp
index bb35310..2122a2c 100644
--- a/evs/sampleDriver/VideoCapture.cpp
+++ b/evs/sampleDriver/VideoCapture.cpp
@@ -94,9 +94,10 @@
     // Set our desired output format
     v4l2_format format;
     format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // Could/should we request V4L2_PIX_FMT_NV21?
+    format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; // Could/should we request V4L2_PIX_FMT_NV21?
     format.fmt.pix.width = 720;                     // TODO:  Can we avoid hard coding dimensions?
     format.fmt.pix.height = 240;                    // For now, this works with available hardware
+    format.fmt.pix.field = V4L2_FIELD_ALTERNATE;    // TODO:  Do we need to specify this?
     ALOGI("Requesting format %c%c%c%c (0x%08X)",
           ((char*)&format.fmt.pix.pixelformat)[0],
           ((char*)&format.fmt.pix.pixelformat)[1],
diff --git a/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
new file mode 100644
index 0000000..198b156
--- /dev/null
+++ b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
@@ -0,0 +1,10 @@
+service evs_driver /system/bin/android.hardware.automotive.evs@1.0-sample
+    class hal
+    priority -20
+    user automotive_evs
+    group automotive_evs
+    onrestart restart evs_manager
+
+#on post-fs
+#on boot
+#    start evs_driver
diff --git a/evs/sampleDriver/bufferCopy.cpp b/evs/sampleDriver/bufferCopy.cpp
index e55170e..a585040 100644
--- a/evs/sampleDriver/bufferCopy.cpp
+++ b/evs/sampleDriver/bufferCopy.cpp
@@ -194,6 +194,41 @@
 }
 
 
+void fillYUYVFromUYVY(const BufferDesc& tgtBuff, uint8_t* tgt, void* imgData, unsigned imgStride) {
+    unsigned width = tgtBuff.width;
+    unsigned height = tgtBuff.height;
+    uint32_t* src = (uint32_t*)imgData;
+    uint32_t* dst = (uint32_t*)tgt;
+    unsigned srcStridePixels = imgStride / 2;
+    unsigned dstStridePixels = tgtBuff.stride;
+
+    const int srcRowPadding32 = srcStridePixels/2 - width/2;  // 2 bytes per pixel, 4 bytes per word
+    const int dstRowPadding32 = dstStridePixels/2 - width/2;  // 2 bytes per pixel, 4 bytes per word
+
+    for (unsigned r=0; r<height; r++) {
+        for (unsigned c=0; c<width/2; c++) {
+            // Note:  we're walking two pixels at a time here (even/odd)
+            uint32_t srcPixel = *src++;
+
+            uint8_t Y1 = (srcPixel)       & 0xFF;
+            uint8_t U  = (srcPixel >> 8)  & 0xFF;
+            uint8_t Y2 = (srcPixel >> 16) & 0xFF;
+            uint8_t V  = (srcPixel >> 24) & 0xFF;
+
+            // Now we write back the pair of pixels with the components swizzled
+            *dst++ = (U)        |
+                     (Y1 << 8)  |
+                     (V  << 16) |
+                     (Y2 << 24);
+        }
+
+        // Skip over any extra data or end of row alignment padding
+        src += srcRowPadding32;
+        dst += dstRowPadding32;
+    }
+}
+
+
 } // namespace implementation
 } // namespace V1_0
 } // namespace evs
diff --git a/evs/sampleDriver/bufferCopy.h b/evs/sampleDriver/bufferCopy.h
index 8bf0b1f..9f68f2b 100644
--- a/evs/sampleDriver/bufferCopy.h
+++ b/evs/sampleDriver/bufferCopy.h
@@ -40,6 +40,9 @@
 void fillYUYVFromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt,
                       void* imgData, unsigned imgStride);
 
+void fillYUYVFromUYVY(const BufferDesc& tgtBuff, uint8_t* tgt,
+                      void* imgData, unsigned imgStride);
+
 } // namespace implementation
 } // namespace V1_0
 } // namespace evs
diff --git a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
index 072e2d6..4cdb640 100644
--- a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
+++ b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
@@ -652,6 +652,9 @@
         // profile.  When unpaired, we remove the device from all of the profiles' device list.
         if (bondState == BluetoothDevice.BOND_NONE) {
             for (Integer profile : mProfilesToConnect) {
+                if (DBG) {
+                    Log.d(TAG, "Removing " + device + " from profile: " + profile);
+                }
                 removeDeviceFromProfile(device, profile);
             }
         }
@@ -1071,6 +1074,8 @@
         }
         for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
             writer.print("Profile: " + devInfo.getProfileLocked() + "\t");
+            writer.print(
+                    "Num of Paired devices: " + devInfo.getNumberOfPairedDevicesLocked() + "\t");
             writer.print("Active Connections: " + devInfo.getNumberOfActiveConnectionsLocked());
             writer.println();
             List<BluetoothDevicesInfo.DeviceInfo> deviceInfoList = devInfo.getDeviceInfoList();
diff --git a/service/src/com/android/car/BluetoothDevicesInfo.java b/service/src/com/android/car/BluetoothDevicesInfo.java
index eb77aad..a383f04 100644
--- a/service/src/com/android/car/BluetoothDevicesInfo.java
+++ b/service/src/com/android/car/BluetoothDevicesInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.car;
 
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothDevice;
 import android.util.Log;
 
@@ -27,7 +28,7 @@
 /**
  * BluetoothDevicesInfo contains all the information pertinent to connection on a Bluetooth Profile.
  * It holds
- * 1. a list of devices {@link #mDeviceList} that has previously paired and connected on this
+ * 1. a list of devices {@link #mDeviceInfoList} that has previously paired and connected on this
  * profile.
  * 2. a Connection Info object {@link #mConnectionInfo} that has following book keeping information:
  * a) profile
@@ -46,7 +47,6 @@
     private static final String TAG = "CarBluetoothDevicesInfo";
     private static final boolean DBG = false;
     private final int DEVICE_NOT_FOUND = -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;
@@ -56,14 +56,20 @@
      * This class holds on to information regarding this bluetooth profile's connection state.
      */
     private class ConnectionInfo {
-
-        int mProfile;                       // which bluetooth profile this Device Info is for
-        boolean mDeviceAvailableToConnect;  // known devices available to connect on this profile.
-        int mDeviceIndex;                   // position of this device in the device List.
-        int mNumPairedDevices;              // Number of paired & connected devices for this profile
-        int mRetryAttempt;                  // Connection Retry counter
-        int mNumActiveConnections;          // Current number of active connections on this profile
-        int mNumConnectionsSupported;       // number of concurrent active connections supported.
+        // which bluetooth profile this Device Info is for
+        private int mProfile;
+        // are there any devices available to connect. It is false either if
+        // 1. no device has been paired to connect on this profile
+        // 2. all paired devices have been tried to connect to, but unsuccessful (not in range etc)
+        private boolean mDeviceAvailableToConnect;
+        // index of device in the mDeviceInfoList that the next connection attempt should be made
+        private int mDeviceIndex;
+        // Connection Retry counter
+        private int mRetryAttempt;
+        // Current number of active connections on this profile
+        private int mNumActiveConnections;
+        // number of concurrent active connections supported.
+        private int mNumConnectionsSupported;
 
         public ConnectionInfo(int profile) {
             // Default the number of concurrent active connections supported to 1.
@@ -79,7 +85,6 @@
         private void initConnectionInfo() {
             mDeviceAvailableToConnect = true;
             mDeviceIndex = 0;
-            mNumPairedDevices = 0;
             mRetryAttempt = 0;
             mNumActiveConnections = 0;
         }
@@ -164,6 +169,17 @@
         return isPresent;
     }
 
+    private DeviceInfo findDeviceInfoInListLocked(@Nullable BluetoothDevice device) {
+        if (device == null) {
+            return null;
+        }
+        for (DeviceInfo devInfo : mDeviceInfoList) {
+            if (devInfo.mBluetoothDevice.getAddress().equals(device.getAddress())) {
+                return devInfo;
+            }
+        }
+        return null;
+    }
     /**
      * Get the current list of connectable devices for this profile.
      *
@@ -198,7 +214,7 @@
         // Check if this device is already in the device list
         if (checkDeviceInListLocked(dev)) {
             if (DBG) {
-                Log.d(TAG, "Device " + dev.getName() + "already in list.  Not adding");
+                Log.d(TAG, "Device " + dev + " already in list.  Not adding");
             }
             return;
         }
@@ -211,14 +227,6 @@
                 Log.d(TAG, "Device List is null");
             }
         }
-        // Increment the number of paired devices for this profile.
-        if (mConnectionInfo != null) {
-            mConnectionInfo.mNumPairedDevices++;
-        } else {
-            if (DBG) {
-                Log.d(TAG, "Unexpected: ConnectionInfo is null");
-            }
-        }
     }
 
     /**
@@ -239,7 +247,7 @@
             }
             if (dev.getAddress().equals(device.getAddress())) {
                 if (DBG) {
-                    Log.d(TAG, "Setting " + dev.getName() + " state to " + state);
+                    Log.d(TAG, "Setting " + dev + " state to " + state);
                 }
                 devInfo.setConnectionState(state);
                 break;
@@ -280,19 +288,22 @@
      */
     public void removeDeviceLocked(BluetoothDevice dev) {
         if (mDeviceInfoList != null) {
-            mDeviceInfoList.remove(dev);
+            DeviceInfo devInfo = findDeviceInfoInListLocked(dev);
+            if (devInfo != null) {
+                mDeviceInfoList.remove(devInfo);
+                // If the device was connected when it was unpaired, we wouldn't have received the
+                // Profile disconnected intents.  Hence check if the device was connected and if it
+                // was, then decrement the number of active connections.
+                if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
+                    mConnectionInfo.mNumActiveConnections--;
+                }
+            }
         } else {
             if (DBG) {
                 Log.d(TAG, "Device List is null");
             }
         }
-        if (mConnectionInfo != null) {
-            mConnectionInfo.mNumPairedDevices--;
-        } else {
-            if (DBG) {
-                Log.d(TAG, "ConnectionInfo is null");
-            }
-        }
+        Log.d(TAG, "Device List size: " + mDeviceInfoList.size());
     }
 
     public void clearDeviceListLocked() {
@@ -310,21 +321,34 @@
      */
     public BluetoothDevice getNextDeviceInQueueLocked() {
         BluetoothDevice device = null;
-        if (mConnectionInfo.mDeviceIndex >= mConnectionInfo.mNumPairedDevices) {
+        int numberOfPairedDevices = getNumberOfPairedDevicesLocked();
+        if (mConnectionInfo.mDeviceIndex >= numberOfPairedDevices) {
             if (DBG) {
                 Log.d(TAG,
                         "No device available for profile "
                                 + mConnectionInfo.mProfile + " "
                                 + mConnectionInfo.mDeviceIndex + "/"
-                                + mConnectionInfo.mNumPairedDevices);
+                                + numberOfPairedDevices);
             }
-            mConnectionInfo.mDeviceIndex = 0; //reset the index
+            // mDeviceIndex is the index of the device in the mDeviceInfoList, that the next
+            // connection attempt would be made on.  It is moved ahead on
+            // updateConnectionStatusLocked() so it always holds the index of the next device to
+            // connect to.  But here, when we get the next device to connect to, if we see that
+            // the index is greater than the number of devices in the list, then we move the index
+            // back to the first device in the list and don't return anything.
+            // The reason why this is reset is to imply that connection attempts on this profile has
+            // been exhausted and if you want to retry connecting on this profile, we will start
+            // from the first device.
+            // The reason to reset here rather than in updateConnectionStatusLocked() is to make
+            // sure we have the latest view of the numberOfPairedDevices before we say we have
+            // exhausted the list.
+            mConnectionInfo.mDeviceIndex = 0;
             return null;
         }
         device = mDeviceInfoList.get(mConnectionInfo.mDeviceIndex).mBluetoothDevice;
         if (DBG) {
             Log.d(TAG, "Getting device " + mConnectionInfo.mDeviceIndex + " from list: "
-                    + device.getName());
+                    + device);
         }
         return device;
     }
@@ -347,18 +371,18 @@
         }
         if (success) {
             if (DBG) {
-                Log.d(TAG, mConnectionInfo.mProfile + " connected to " + device.getName());
+                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) {
-                Log.d(TAG, "Position of " + device.getName() + " in Q: " + positionInQ);
+                Log.d(TAG, "Position of " + device + " in Q: " + positionInQ);
             }
             // If the device that connected is not in the list, it could be because it is being
             // paired and getting added to the device list for this profile for the first time.
             if (positionInQ == DEVICE_NOT_FOUND) {
-                Log.d(TAG, "Connected device not in Q: " + device.getName());
+                Log.d(TAG, "Connected device not in Q: " + device);
                 addDeviceLocked(device);
                 positionInQ = mDeviceInfoList.size() - 1;
             } else if (positionInQ != mConnectionInfo.mDeviceIndex) {
@@ -370,7 +394,8 @@
                 // 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.getName());
+                    Log.d(TAG, "Different device connected: " + device + " CurrIndex: "
+                            + mConnectionInfo.mDeviceIndex);
                 }
             }
 
@@ -450,8 +475,8 @@
      *
      * @return number of paired devices on this profile.
      */
-    public Integer getNumberOfPairedDevicesLocked() {
-        return mConnectionInfo.mNumPairedDevices;
+    public int getNumberOfPairedDevicesLocked() {
+        return mDeviceInfoList.size();
     }
 
     /**
@@ -521,13 +546,17 @@
         mConnectionInfo.mDeviceIndex = 0;
         mConnectionInfo.mRetryAttempt = 0;
         mConnectionInfo.mDeviceAvailableToConnect = true;
+        for (DeviceInfo info : mDeviceInfoList) {
+            setConnectionStateLocked(info.getBluetoothDevice(),
+                    BluetoothProfile.STATE_DISCONNECTED);
+        }
     }
 
     public void resetDeviceListLocked() {
         if (mDeviceInfoList != null) {
             mDeviceInfoList.clear();
-            mConnectionInfo.mNumPairedDevices = 0;
         }
         resetConnectionInfoLocked();
     }
+
 }
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
index 683c1c8..37d265a 100644
--- a/service/src/com/android/car/VmsPublisherService.java
+++ b/service/src/com/android/car/VmsPublisherService.java
@@ -144,6 +144,12 @@
         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.
diff --git a/service/src/com/android/car/VmsPublishersInfo.java b/service/src/com/android/car/VmsPublishersInfo.java
new file mode 100644
index 0000000..04ee82f
--- /dev/null
+++ b/service/src/com/android/car/VmsPublishersInfo.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 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/VmsSubscriberService.java b/service/src/com/android/car/VmsSubscriberService.java
index eabb0a6..fc0a885 100644
--- a/service/src/com/android/car/VmsSubscriberService.java
+++ b/service/src/com/android/car/VmsSubscriberService.java
@@ -262,6 +262,13 @@
     }
 
     @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();
diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java
index b294985..8ab5427 100644
--- a/service/src/com/android/car/hal/VmsHalService.java
+++ b/service/src/com/android/car/hal/VmsHalService.java
@@ -37,6 +37,7 @@
 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;
@@ -71,13 +72,13 @@
     private final IBinder mHalPublisherToken = new Binder();
     private final VehicleHal mVehicleHal;
 
-    private final Object mRoutingLock = new Object();
+    private final Object mLock = new Object();
     private final VmsRouting mRouting = new VmsRouting();
-    private final Object mAvailabilityLock = new Object();
-    @GuardedBy("mAvailabilityLock")
+    @GuardedBy("mLock")
     private final Map<IBinder, VmsLayersOffering> mOfferings = new HashMap<>();
-    @GuardedBy("mAvailabilityLock")
+    @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.
@@ -125,7 +126,7 @@
 
     public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
         boolean firstSubscriptionForLayer = false;
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             // Check if publishers need to be notified about this change in subscriptions.
             firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
 
@@ -139,7 +140,7 @@
 
     public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
         boolean layerHasSubscribers = true;
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             if (!mRouting.hasLayerSubscriptions(layer)) {
                 Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
                 return;
@@ -157,50 +158,74 @@
     }
 
     public void addSubscription(IVmsSubscriberClient listener) {
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             mRouting.addSubscription(listener);
         }
     }
 
     public void removeSubscription(IVmsSubscriberClient listener) {
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             mRouting.removeSubscription(listener);
         }
     }
 
     public void removeDeadListener(IVmsSubscriberClient listener) {
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             mRouting.removeDeadListener(listener);
         }
     }
 
     public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) {
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             return mRouting.getListeners(layer);
         }
     }
 
     public Set<IVmsSubscriberClient> getAllListeners() {
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             return mRouting.getAllListeners();
         }
     }
 
     public boolean isHalSubscribed(VmsLayer layer) {
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             return mRouting.isHalSubscribed(layer);
         }
     }
 
     public VmsSubscriptionState getSubscriptionState() {
-        synchronized (mRoutingLock) {
+        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 (mRoutingLock) {
+        synchronized (mLock) {
             // Check if publishers need to be notified about this change in subscriptions.
             firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
 
@@ -214,7 +239,7 @@
 
     public void removeHalSubscription(VmsLayer layer) {
         boolean layerHasSubscribers = true;
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             if (!mRouting.hasLayerSubscriptions(layer)) {
                 Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
                 return;
@@ -232,14 +257,14 @@
     }
 
     public boolean containsListener(IVmsSubscriberClient listener) {
-        synchronized (mRoutingLock) {
+        synchronized (mLock) {
             return mRouting.containsListener(listener);
         }
     }
 
     public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering){
         Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
-        synchronized (mAvailabilityLock) {
+        synchronized (mLock) {
             updateOffering(publisherToken, offering);
             availableLayers = mAvailableLayers.getAvailableLayers();
         }
@@ -248,7 +273,7 @@
 
     public Set<VmsLayer> getAvailableLayers() {
         //TODO(b/36872877): wrap available layers in VmsAvailabilityState similar to VmsSubscriptionState.
-        synchronized (mAvailabilityLock) {
+        synchronized (mLock) {
             return mAvailableLayers.getAvailableLayers();
         }
     }
@@ -475,7 +500,7 @@
         }
         // Store the HAL offering.
         VmsLayersOffering offering = new VmsLayersOffering(offeredLayers);
-        synchronized (mAvailabilityLock) {
+        synchronized (mLock) {
             updateOffering(mHalPublisherToken, offering);
         }
     }
@@ -489,7 +514,7 @@
      * </ul>
      */
     private void handleAvailabilityEvent() {
-        synchronized (mAvailabilityLock) {
+        synchronized (mLock) {
             Collection<VmsLayer> availableLayers = mAvailableLayers.getAvailableLayers();
             VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
                 VmsMessageType.AVAILABILITY_RESPONSE, availableLayers);
@@ -527,7 +552,7 @@
 
     private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) {
         Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
-        synchronized (mAvailabilityLock) {
+        synchronized (mLock) {
             mOfferings.put(publisherToken, offering);
 
             // Update layers availability.
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
index 63492f8..d8e344b 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
@@ -22,6 +22,7 @@
 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;
 
@@ -35,18 +36,24 @@
  */
 @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.
-        publishIfNeeded(subscriptionState);
-        declareOffering(subscriptionState);
+        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);
     }
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
index 20fa0b6..3b3a94f 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
@@ -29,13 +29,12 @@
 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.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
@@ -43,6 +42,7 @@
 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);
@@ -127,6 +127,27 @@
     }
 
     /**
+     * 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.
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java
new file mode 100644
index 0000000..2b75012
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.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.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);
+    }
+}