Merge "Update notification actions."
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 e73603c..7c60150 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -84,6 +84,7 @@
 
 # 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 += \
@@ -91,6 +92,10 @@
     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
 
+# Device running Android is a car
+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
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
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/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/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/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index 29012da..f2ee831 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -118,12 +118,8 @@
             new MediaMuteAudioFocusListener();
 
     @GuardedBy("mLock")
-    private int mBottomFocusState;
-    @GuardedBy("mLock")
     private boolean mRadioOrExtSourceActive = false;
     @GuardedBy("mLock")
-    private boolean mCallActive = false;
-    @GuardedBy("mLock")
     private int mCurrentAudioContexts = 0;
     @GuardedBy("mLock")
     private int mCurrentPrimaryAudioContext = 0;
@@ -209,11 +205,6 @@
             int r = mAudioManager.requestAudioFocus(mBottomAudioFocusListener, mAttributeBottom,
                     AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_FLAG_DELAY_OK);
             synchronized (mLock) {
-                if (r == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
-                    mBottomFocusState = AudioManager.AUDIOFOCUS_GAIN;
-                } else {
-                    mBottomFocusState = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
-                }
                 mCurrentFocusState = currentState;
                 mCurrentAudioContexts = 0;
             }
@@ -458,7 +449,7 @@
                     " mLastFocusRequestToCar:" + mLastFocusRequestToCar);
             writer.println(" mCurrentAudioContexts:0x" +
                     Integer.toHexString(mCurrentAudioContexts));
-            writer.println(" mCallActive:" + mCallActive + " mRadioOrExtSourceActive:" +
+            writer.println(" mRadioOrExtSourceActive:" +
                     mRadioOrExtSourceActive);
             writer.println(" mCurrentPrimaryAudioContext:" + mCurrentPrimaryAudioContext +
                     " mCurrentPrimaryPhysicalStream:" + mCurrentPrimaryPhysicalStream);
@@ -985,11 +976,6 @@
                 CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_MEDIA_MUTE) {
                 muteMedia = true;
         }
-        if (logicalStreamTypeForTop == CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL) {
-            mCallActive = true;
-        } else {
-            mCallActive = false;
-        }
         // other apps having focus
         int focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
         int extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG;
@@ -1578,9 +1564,6 @@
     private class BottomAudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
         @Override
         public void onAudioFocusChange(int focusChange) {
-            synchronized (mLock) {
-                mBottomFocusState = focusChange;
-            }
         }
     }
 
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);
+    }
+}
diff --git a/tools/bootanalyze/bootanalyze.py b/tools/bootanalyze/bootanalyze.py
index 4b814c7..8c13d6f 100755
--- a/tools/bootanalyze/bootanalyze.py
+++ b/tools/bootanalyze/bootanalyze.py
@@ -476,6 +476,16 @@
       zygote_pids.append(pid)
   events[event] = line
 
+def update_name_if_already_exist(events, name):
+  existing_event = events.get(name)
+  i = 0
+  new_name = name
+  while existing_event:
+    i += 1
+    new_name = name + "_" + str(i)
+    existing_event = events.get(new_name)
+  return new_name
+
 def collect_events(search_events, command, timings, stop_events, disable_timing_after_zygote):
   events = collections.OrderedDict()
   timing_events = {}
@@ -512,7 +522,8 @@
       elif event.startswith("zygote"):
         handle_zygote_event(zygote_pids, events, event, line)
       else:
-        events[event] = line
+        new_event = update_name_if_already_exist(events, event)
+        events[new_event] = line
       if event in stop_events:
         stop_events.remove(event)
         print "remaining stop_events:", stop_events
diff --git a/tools/bootanalyze/config.yaml b/tools/bootanalyze/config.yaml
index 923ca5d..9d322ed 100644
--- a/tools/bootanalyze/config.yaml
+++ b/tools/bootanalyze/config.yaml
@@ -43,6 +43,8 @@
   CarLauncherReady: Em.Overview:\s*onResume
   CarService_start: for service com.android.car/.CarService
   BootAnimStart: starting service 'bootanim'
+  BootAnimSfWait: BootAnimation:\sWaiting\sfor\sSurfaceFlinger\stook\s
+  BootAnimShowStart: BootAnimation:\sBootAnimationShownTiming\sstart\stime
   BootAnimStopRequest: TELLING SURFACE FLINGER WE ARE BOOTED
   BootAnimEnd: Service 'bootanim'
   KeyguardStart: KeyguardServiceDelegate.*\*\*\* Keyguard started