Merge "Load VNS property access gatekeeping from file" into nyc-dev
diff --git a/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java b/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java
index 825af4b..5fc912c 100644
--- a/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java
+++ b/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java
@@ -281,6 +281,7 @@
         Log.d(TAG, "msgDeviceConnect");
         // We are connected to a new device via A2DP now.
         mA2dpDevice = device;
+        handleProfileOrDeviceConnect();
     }
 
     private void msgProfileConnect(BluetoothProfile profile) {
@@ -297,18 +298,28 @@
 
         if (mA2dpDevice != null && !mA2dpDevice.equals(devices.get(0))) {
             Log.e(TAG, "A2dp device : " + mA2dpDevice + " avrcp device " + devices.get(0));
+            return;
         }
         mA2dpDevice = devices.get(0);
+        handleProfileOrDeviceConnect();
+    }
 
-        PlaybackState playbackState = mAvrcpProfile.getPlaybackState(mA2dpDevice);
+    // Called when either the device or profile is connected. Note that either may be connected
+    // independent of each other. We set the initial state only if both are connected.
+    private void handleProfileOrDeviceConnect() {
+        if (mA2dpDevice == null || mAvrcpProfile == null) {
+            Log.e(TAG, "device and profile should be non-null.");
+            return;
+        }
+
+        // Set a dummy playback state so that the user can press play and start music.
         // Add actions required for playback and rebuild the object.
-        PlaybackState.Builder pbb = new PlaybackState.Builder(playbackState);
-        playbackState = pbb.setActions(mTransportControlFlags).build();
-
-        MediaMetadata mediaMetadata = mAvrcpProfile.getMetadata(mA2dpDevice);
-        Log.d(TAG, "Media metadata " + mediaMetadata + " playback state " + playbackState);
-        mSession.setMetadata(mAvrcpProfile.getMetadata(mA2dpDevice));
-        mSession.setPlaybackState(playbackState);
+        PlaybackState.Builder pbb = new PlaybackState.Builder();
+        pbb = pbb.setState(PlaybackState.STATE_STOPPED, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+                PLAYBACK_SPEED)
+                 .setActions(mTransportControlFlags);
+        mSession.setPlaybackState(pbb.build());
+        mSession.setMetadata(new MediaMetadata.Builder().build());
     }
 
     private void msgDeviceDisconnect(BluetoothDevice device) {
@@ -329,6 +340,7 @@
                 .setActions(mTransportControlFlags)
                 .setErrorMessage(getString(R.string.bluetooth_disconnected));
         mSession.setPlaybackState(pbb.build());
+        mSession.setMetadata(null);
     }
 
     private void msgProfileDisconnect() {
diff --git a/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java b/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java
index b88ceb6..0d7b090 100644
--- a/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java
+++ b/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package com.google.android.car.pbapsink;
 
 import com.android.vcard.VCardEntry;
diff --git a/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java b/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java
index a8c41cc..9bdc215 100644
--- a/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java
+++ b/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java
@@ -1,405 +1,405 @@
-  /*
-   * Copyright (C) 2015 The Android Open Source Project
-   *
-   * Licensed under the Apache License, Version 2.0 (the "License");
-   * you may not use this file except in compliance with the License.
-   * You may obtain a copy of the License at
-   *
-   *      http://www.apache.org/licenses/LICENSE-2.0
-   *
-   * Unless required by applicable law or agreed to in writing, software
-   * distributed under the License is distributed on an "AS IS" BASIS,
-   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   * See the License for the specific language governing permissions and
-   * limitations under the License.
-   */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-  package com.google.android.auto.mapservice;
+package com.google.android.auto.mapservice;
 
-  import android.bluetooth.BluetoothDevice;
-  import android.bluetooth.BluetoothAdapter;
-  import android.content.BroadcastReceiver;
-  import android.content.ComponentName;
-  import android.content.Context;
-  import android.content.Intent;
-  import android.content.IntentFilter;
-  import android.content.ServiceConnection;
-  import android.os.Handler;
-  import android.os.IBinder;
-  import android.os.Looper;
-  import android.os.RemoteException;
-  import android.util.Log;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
 
-  import java.lang.ref.WeakReference;
-  import java.util.List;
+import java.lang.ref.WeakReference;
+import java.util.List;
 
-  public final class BluetoothMapManager {
-      public static final String CALLBACK_MISMATCH = "CALLBACK_MISMATCH";
+public final class BluetoothMapManager {
+    public static final String CALLBACK_MISMATCH = "CALLBACK_MISMATCH";
 
-      /**
-       * Connection State(s) for the service bound to this Manager.
-       * The connection state manage if the Manager is connected to Service:
-       * DISCONNECTED: Manager cannot execute calls on service currently because the client either
-       * never called connect() on the manager OR the device is disconnected. In the later case the
-       * client will have to eventually call connect() again.
-       * CONNECTING: This state persists from calling onBind on the service, until we have
-       * successfully received onConnect from the service.
-       * CONNECTED: The service is successfully connected and ready to receive commands.
-       */
-      private static final int DISCONNECTED = 0;
-      private static final int SUSPENDED = 1;
-      private static final int CONNECTING = 2;
-      private static final int CONNECTED = 3;
+    /**
+     * Connection State(s) for the service bound to this Manager.
+     * The connection state manage if the Manager is connected to Service:
+     * DISCONNECTED: Manager cannot execute calls on service currently because the client either
+     * never called connect() on the manager OR the device is disconnected. In the later case the
+     * client will have to eventually call connect() again.
+     * CONNECTING: This state persists from calling onBind on the service, until we have
+     * successfully received onConnect from the service.
+     * CONNECTED: The service is successfully connected and ready to receive commands.
+     */
+    private static final int DISCONNECTED = 0;
+    private static final int SUSPENDED = 1;
+    private static final int CONNECTING = 2;
+    private static final int CONNECTED = 3;
 
-      // Error codes returned via the onError call.
+    // Error codes returned via the onError call.
 
-      // On connection suspended the Manager will callback with onConnect when the service is
-      // restarted and connected by android binder.
-      public static final int ERROR_CONNECT_SUSPENDED = 0;
-      // Connection between the service (backed by this Manager) and the remote device has failed.
-      // It can be due to variety of reasons such as obex transport failure or the device going out
-      // of range. The client will need to either call connect() again. In cases where the device
-      // goes out of range, calling connect agian will lead to this error being throw again but if
-      // it was a transient failure due to obex transport or other binder issue, then this call will
-      // succeed.
-      public static final int ERROR_CONNECT_FAILED = 1;
+    // On connection suspended the Manager will callback with onConnect when the service is
+    // restarted and connected by android binder.
+    public static final int ERROR_CONNECT_SUSPENDED = 0;
+    // Connection between the service (backed by this Manager) and the remote device has failed.
+    // It can be due to variety of reasons such as obex transport failure or the device going out
+    // of range. The client will need to either call connect() again. In cases where the device
+    // goes out of range, calling connect agian will lead to this error being throw again but if
+    // it was a transient failure due to obex transport or other binder issue, then this call will
+    // succeed.
+    public static final int ERROR_CONNECT_FAILED = 1;
 
-      // String representation of operations.
-      private static final String OP_NONE = "";
-      private static final String OP_PUSH_MESSAGE = "pushMessage";
-      private static final String OP_GET_MESSAGE = "getMessage";
-      private static final String OP_GET_MESSAGES_LISTING = "getMessagesListing";
-      private static final String OP_ENABLE_NOTIFICATIONS = "enableNotifications";
+    // String representation of operations.
+    private static final String OP_NONE = "";
+    private static final String OP_PUSH_MESSAGE = "pushMessage";
+    private static final String OP_GET_MESSAGE = "getMessage";
+    private static final String OP_GET_MESSAGES_LISTING = "getMessagesListing";
+    private static final String OP_ENABLE_NOTIFICATIONS = "enableNotifications";
 
-      private static final boolean DBG = true;
-      private static final String TAG = "BluetoothMapManager";
-      private final Context mContext;
-      private final ConnectionCallbacks mCallbacks;
-      private final BluetoothDevice mDevice;
-      // We have a handler to make sure that all code modifying/accessing the final non-final
-      // objects are serialized. This is done by ensuring the following:
-      // a) All calls done by the client (using this manager) should be on the main thread.
-      // b) All calls done by the manager not-on main thread (binder threads) should be posted back
-      // to main thread using this handler.
-      private final Handler mHandler = new Handler();
+    private static final boolean DBG = true;
+    private static final String TAG = "BluetoothMapManager";
+    private final Context mContext;
+    private final ConnectionCallbacks mCallbacks;
+    private final BluetoothDevice mDevice;
+    // We have a handler to make sure that all code modifying/accessing the final non-final
+    // objects are serialized. This is done by ensuring the following:
+    // a) All calls done by the client (using this manager) should be on the main thread.
+    // b) All calls done by the manager not-on main thread (binder threads) should be posted back
+    // to main thread using this handler.
+    private final Handler mHandler = new Handler();
 
-      private IBluetoothMapService mServiceBinder;
-      private IBluetoothMapServiceCallbacks mServiceCallbacks;
-      private BluetoothMapServiceConnection mServiceConnection;
-      private int mConnectionState = DISCONNECTED;
-      private String mOpInflight = OP_NONE;
+    private IBluetoothMapService mServiceBinder;
+    private IBluetoothMapServiceCallbacks mServiceCallbacks;
+    private BluetoothMapServiceConnection mServiceConnection;
+    private int mConnectionState = DISCONNECTED;
+    private String mOpInflight = OP_NONE;
 
-      public BluetoothMapManager(
-          Context context, BluetoothDevice device, ConnectionCallbacks callbacks) {
-          if (device == null) {
-            throw new IllegalArgumentException("Device cannot be null.");
-          }
-          if (callbacks == null) {
-              throw new IllegalArgumentException(TAG + ": Callbacks cannot be null!");
-          }
+    public BluetoothMapManager(
+        Context context, BluetoothDevice device, ConnectionCallbacks callbacks) {
+        if (device == null) {
+          throw new IllegalArgumentException("Device cannot be null.");
+        }
+        if (callbacks == null) {
+            throw new IllegalArgumentException(TAG + ": Callbacks cannot be null!");
+        }
 
-          if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
-              throw new IllegalStateException(
-                  "Client needs to call the manager from the main UI thread.");
-          }
+        if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                "Client needs to call the manager from the main UI thread.");
+        }
 
-          mDevice = device;
-          mContext = context;
-          mCallbacks = callbacks;
-      }
+        mDevice = device;
+        mContext = context;
+        mCallbacks = callbacks;
+    }
 
-      /**
-       * Defines the callback interface that clients using the Manager should implement in order to
-       * receive callbacks and notification for changes happening to either the binder connection
-       * between the Manager and Service or MAP profile changes.
-       */
-      public static abstract class ConnectionCallbacks {
-          /**
-           * Called when connection has been established successfully with the service and the
-           * service itself is successfully connected to MAP profile.
-           * See connect().
-           */
-          public abstract void onConnected();
+    /**
+     * Defines the callback interface that clients using the Manager should implement in order to
+     * receive callbacks and notification for changes happening to either the binder connection
+     * between the Manager and Service or MAP profile changes.
+     */
+    public static abstract class ConnectionCallbacks {
+        /**
+         * Called when connection has been established successfully with the service and the
+         * service itself is successfully connected to MAP profile.
+         * See connect().
+         */
+        public abstract void onConnected();
 
-          /**
-           * Called when the Manager is no longer able to execute commands on the service.
-           * Client who holds this manager should consider all in-flight commands sent till now as
-           * cancelled. For permanent failures such as device going out of range and not coming back
-           * the client should call connect() again too continue working otherwise
-           * when the Manager does connect back it will call onConnected() (see above).
-           */
-          public abstract void onError(int errorCode);
+        /**
+         * Called when the Manager is no longer able to execute commands on the service.
+         * Client who holds this manager should consider all in-flight commands sent till now as
+         * cancelled. For permanent failures such as device going out of range and not coming back
+         * the client should call connect() again too continue working otherwise
+         * when the Manager does connect back it will call onConnected() (see above).
+         */
+        public abstract void onError(int errorCode);
 
-          /**
-           * Callen when notification status has been adjusted.
-           * See enableNotifications().
-           */
-          public abstract void onEnableNotifications();
+        /**
+         * Callen when notification status has been adjusted.
+         * See enableNotifications().
+         */
+        public abstract void onEnableNotifications();
 
-          /**
-           * Called when the message has been queued for sending.
-           * The argument always contains a "valid" handle.
-           * See pushMessage().
-           */
-          public abstract void onPushMessage(String handle);
+        /**
+         * Called when the message has been queued for sending.
+         * The argument always contains a "valid" handle.
+         * See pushMessage().
+         */
+        public abstract void onPushMessage(String handle);
 
-          /**
-           * Called when the message is fetched.
-           * See getMessage().
-           */
-          public abstract void onGetMessage(BluetoothMapMessage msg);
+        /**
+         * Called when the message is fetched.
+         * See getMessage().
+         */
+        public abstract void onGetMessage(BluetoothMapMessage msg);
 
-          /**
-           * Called when the messages listing is retrieved.
-           * See getMessagesListing().
-           */
-          public abstract void onGetMessagesListing(List<BluetoothMapMessagesListing> msgsListing);
+        /**
+         * Called when the messages listing is retrieved.
+         * See getMessagesListing().
+         */
+        public abstract void onGetMessagesListing(List<BluetoothMapMessagesListing> msgsListing);
 
-          /**
-           * Called when an event has occured.
-           * See BluetoothMapEventReport for a description of what an event may look like.
-           */
-          public abstract void onEvent(BluetoothMapEventReport eventReport);
+        /**
+         * Called when an event has occured.
+         * See BluetoothMapEventReport for a description of what an event may look like.
+         */
+        public abstract void onEvent(BluetoothMapEventReport eventReport);
 
-      }
+    }
 
-      /**
-       * Bind to the service and register the callback passed in the constructor.
-       */
-      public boolean connect() {
-          checkMainThread();
+    /**
+     * Bind to the service and register the callback passed in the constructor.
+     */
+    public boolean connect() {
+        checkMainThread();
 
-          if (mConnectionState != DISCONNECTED && mConnectionState != SUSPENDED) {
-              Log.w(TAG, "Not in disconnected state, connection will eventually resume: " +
-                  mConnectionState);
-              return true;
-          }
-          mConnectionState = CONNECTING;
-          mServiceConnection = new BluetoothMapServiceConnection();
+        if (mConnectionState != DISCONNECTED && mConnectionState != SUSPENDED) {
+            Log.w(TAG, "Not in disconnected state, connection will eventually resume: " +
+                mConnectionState);
+            return true;
+        }
+        mConnectionState = CONNECTING;
+        mServiceConnection = new BluetoothMapServiceConnection();
 
-          boolean bound = false;
-          ComponentName cName =
-              new ComponentName(
-                  "com.google.android.auto.mapservice",
-                  "com.google.android.auto.mapservice.BluetoothMapService");
-          final Intent intent = new Intent();
-          intent.setComponent(cName);
-          try {
-              bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
-          } catch (Exception ex) {
-              Log.e(TAG, "Failed binding to service." + ex);
-              dumpState();
-              return false;
-          }
+        boolean bound = false;
+        ComponentName cName =
+            new ComponentName(
+                "com.google.android.auto.mapservice",
+                "com.google.android.auto.mapservice.BluetoothMapService");
+        final Intent intent = new Intent();
+        intent.setComponent(cName);
+        try {
+            bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed binding to service." + ex);
+            dumpState();
+            return false;
+        }
 
-          if (!bound) {
-              forceCloseConnection();
-              dumpState();
-              return false;
-          }
+        if (!bound) {
+            forceCloseConnection();
+            dumpState();
+            return false;
+        }
 
-          dumpState();
-          return true;
-      }
+        dumpState();
+        return true;
+    }
 
-      /**
-       * Unregister the callbacks and unbind from the service.
-       */
-      public void disconnect() {
-          checkMainThread();
+    /**
+     * Unregister the callbacks and unbind from the service.
+     */
+    public void disconnect() {
+        checkMainThread();
 
-          if (DBG) {
-              Log.d(TAG, "Calling IBluetoothMapService.disconnect ...");
-          }
+        if (DBG) {
+            Log.d(TAG, "Calling IBluetoothMapService.disconnect ...");
+        }
 
-          // In case the manager is already disconnected, we don't need to do anything more here.
-          if (mServiceBinder != null) {
-              try {
-                  mServiceBinder.disconnect(mServiceCallbacks);
-              } catch (RemoteException ex) {
-                  Log.w(TAG, "RemoteException during disconnect for " + mServiceConnection);
-              } catch (IllegalStateException ex) {
-                  sendError(ex);
-              }
-          }
-          forceCloseConnection();
-          dumpState();
-      }
+        // In case the manager is already disconnected, we don't need to do anything more here.
+        if (mServiceBinder != null) {
+            try {
+                mServiceBinder.disconnect(mServiceCallbacks);
+            } catch (RemoteException ex) {
+                Log.w(TAG, "RemoteException during disconnect for " + mServiceConnection);
+            } catch (IllegalStateException ex) {
+                sendError(ex);
+            }
+        }
+        forceCloseConnection();
+        dumpState();
+    }
 
-      /**
-       * Enable notifications.
-       */
-      public void enableNotifications(boolean status) {
-          checkMainThread();
+    /**
+     * Enable notifications.
+     */
+    public void enableNotifications(boolean status) {
+        checkMainThread();
 
-          if (DBG) {
-              Log.d(TAG, "Calling IBluetoothMapService.enableNotifications ..." + status);
-          }
+        if (DBG) {
+            Log.d(TAG, "Calling IBluetoothMapService.enableNotifications ..." + status);
+        }
 
-          if (mConnectionState != CONNECTED) {
-              if (DBG) {
-                  Log.d(TAG, "Not connected to service.");
-              }
-              throw new IllegalStateException(
-                  "Service is not connected, either connect() is not called or a disconnect " +
-                  "event is not handled correctly.");
-          }
+        if (mConnectionState != CONNECTED) {
+            if (DBG) {
+                Log.d(TAG, "Not connected to service.");
+            }
+            throw new IllegalStateException(
+                "Service is not connected, either connect() is not called or a disconnect " +
+                "event is not handled correctly.");
+        }
 
-          if (!mOpInflight.equals(OP_NONE)) {
-              throw new IllegalStateException(
-                  TAG + "Operation already in flight: " + mOpInflight +
-                  ". Please wait for an appropriate callback from your previous operation.");
-          }
+        if (!mOpInflight.equals(OP_NONE)) {
+            throw new IllegalStateException(
+                TAG + "Operation already in flight: " + mOpInflight +
+                ". Please wait for an appropriate callback from your previous operation.");
+        }
 
-          try {
-              mServiceBinder.enableNotifications(mServiceCallbacks, status);
-          } catch (RemoteException ex) {
-              // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
-              Log.e(TAG, "", ex);
-              return;
-          } catch (IllegalStateException ex) {
-              sendError(ex);
-          }
-          mOpInflight = OP_ENABLE_NOTIFICATIONS;
-      }
+        try {
+            mServiceBinder.enableNotifications(mServiceCallbacks, status);
+        } catch (RemoteException ex) {
+            // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+            Log.e(TAG, "", ex);
+            return;
+        } catch (IllegalStateException ex) {
+            sendError(ex);
+        }
+        mOpInflight = OP_ENABLE_NOTIFICATIONS;
+    }
 
-      /**
-       * Push a message.
-       */
-      public void pushMessage(BluetoothMapMessage msg) {
-          checkMainThread();
+    /**
+     * Push a message.
+     */
+    public void pushMessage(BluetoothMapMessage msg) {
+        checkMainThread();
 
-          if (mConnectionState != CONNECTED) {
-              throw new IllegalStateException(
-                  "Service is not connected, either connect() is not called or a disconnect " +
-                  "event is not handled correctly.");
-          }
+        if (mConnectionState != CONNECTED) {
+            throw new IllegalStateException(
+                "Service is not connected, either connect() is not called or a disconnect " +
+                "event is not handled correctly.");
+        }
 
-          if (!mOpInflight.equals(OP_NONE)) {
-              throw new IllegalStateException(
-                  TAG + "Operation already in flight: " + mOpInflight +
-                  ". Please wait for an appropriate callback from your previous operation.");
-          }
+        if (!mOpInflight.equals(OP_NONE)) {
+            throw new IllegalStateException(
+                TAG + "Operation already in flight: " + mOpInflight +
+                ". Please wait for an appropriate callback from your previous operation.");
+        }
 
-          try {
-              mServiceBinder.pushMessage(mServiceCallbacks, msg);
-          } catch (RemoteException ex) {
-              // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
-              Log.e(TAG, "", ex);
-              return;
-          } catch (IllegalStateException ex) {
-              sendError(ex);
-          }
-          mOpInflight = OP_PUSH_MESSAGE;
-      }
+        try {
+            mServiceBinder.pushMessage(mServiceCallbacks, msg);
+        } catch (RemoteException ex) {
+            // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+            Log.e(TAG, "", ex);
+            return;
+        } catch (IllegalStateException ex) {
+            sendError(ex);
+        }
+        mOpInflight = OP_PUSH_MESSAGE;
+    }
 
-      /**
-       * Get a message by its handle.
-       */
-      public void getMessage(String handle) {
-          checkMainThread();
+    /**
+     * Get a message by its handle.
+     */
+    public void getMessage(String handle) {
+        checkMainThread();
 
-          if (mConnectionState != CONNECTED) {
-              throw new IllegalStateException(
-                  "Service is not connected, either connect() is not called or a disconnect " +
-                  "event is not handled correctly.");
-          }
+        if (mConnectionState != CONNECTED) {
+            throw new IllegalStateException(
+                "Service is not connected, either connect() is not called or a disconnect " +
+                "event is not handled correctly.");
+        }
 
-          if (!mOpInflight.equals(OP_NONE)) {
-              throw new IllegalStateException(
-                  TAG + "Operation already in flight: " + mOpInflight +
-                  ". Please wait for an appropriate callback from your previous operation.");
-          }
+        if (!mOpInflight.equals(OP_NONE)) {
+            throw new IllegalStateException(
+                TAG + "Operation already in flight: " + mOpInflight +
+                ". Please wait for an appropriate callback from your previous operation.");
+        }
 
-          try {
-              mServiceBinder.getMessage(mServiceCallbacks, handle);
-          } catch (RemoteException ex) {
-              // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
-              Log.e(TAG, "", ex);
-              return;
-          } catch (IllegalStateException ex) {
-              sendError(ex);
-          }
-          mOpInflight = OP_GET_MESSAGE;
-      }
+        try {
+            mServiceBinder.getMessage(mServiceCallbacks, handle);
+        } catch (RemoteException ex) {
+            // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+            Log.e(TAG, "", ex);
+            return;
+        } catch (IllegalStateException ex) {
+            sendError(ex);
+        }
+        mOpInflight = OP_GET_MESSAGE;
+    }
 
-      public void getMessagesListing(String folder) {
-          // If count is not specified the cap is put by the Bluetooth spec, we pass a large number
-          // to use the cap provided by spec implementation on MAS server.
-          getMessagesListing(folder, 65535, 0);
-      }
-      public void getMessagesListing(String folder, int count) {
-          getMessagesListing(folder, count, 0);
-      }
-      public void getMessagesListing(String folder, int count, int offset) {
-          checkMainThread();
+    public void getMessagesListing(String folder) {
+        // If count is not specified the cap is put by the Bluetooth spec, we pass a large number
+        // to use the cap provided by spec implementation on MAS server.
+        getMessagesListing(folder, 65535, 0);
+    }
+    public void getMessagesListing(String folder, int count) {
+        getMessagesListing(folder, count, 0);
+    }
+    public void getMessagesListing(String folder, int count, int offset) {
+        checkMainThread();
 
-          if (mConnectionState != CONNECTED) {
-              throw new IllegalStateException(
-                  "Service is not connected, either connect() is not called or a disconnect " +
-                  "event is not handled correctly.");
-          }
+        if (mConnectionState != CONNECTED) {
+            throw new IllegalStateException(
+                "Service is not connected, either connect() is not called or a disconnect " +
+                "event is not handled correctly.");
+        }
 
-          if (!mOpInflight.equals(OP_NONE)) {
-              throw new IllegalStateException(
-                  TAG + "Operation already in flight: " + mOpInflight +
-                  ". Please wait for an appropriate callback from your previous operation.");
-          }
+        if (!mOpInflight.equals(OP_NONE)) {
+            throw new IllegalStateException(
+                TAG + "Operation already in flight: " + mOpInflight +
+                ". Please wait for an appropriate callback from your previous operation.");
+        }
 
-          try {
-              mServiceBinder.getMessagesListing(mServiceCallbacks, folder, count, offset);
-          } catch (RemoteException ex) {
-              // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
-              Log.e(TAG, "", ex);
-              return;
-          } catch (IllegalStateException ex) {
-              sendError(ex);
-          }
-          mOpInflight = OP_GET_MESSAGES_LISTING;
-      }
+        try {
+            mServiceBinder.getMessagesListing(mServiceCallbacks, folder, count, offset);
+        } catch (RemoteException ex) {
+            // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+            Log.e(TAG, "", ex);
+            return;
+        } catch (IllegalStateException ex) {
+            sendError(ex);
+        }
+        mOpInflight = OP_GET_MESSAGES_LISTING;
+    }
 
-      /**
-       * Checks if the current thread is main thread.
-       *
-       * Throws an IllegalStateException otherwise.
-       */
-      void checkMainThread() {
-          if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
-              throw new IllegalStateException(
-                  "Manager APIs should be called only from main thread.");
-          }
-      }
+    /**
+     * Checks if the current thread is main thread.
+     *
+     * Throws an IllegalStateException otherwise.
+     */
+    void checkMainThread() {
+        if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                "Manager APIs should be called only from main thread.");
+        }
+    }
 
-      /**
-       * Implements the callbacks when changes in the binder connection of Manager (this) and the
-       * BluetoothMapService occur.
-       * For a list of possible states that the binder connection can exist, see DISCONNECTED,
-       * CONNECTING and CONNECTED states above.
-       */
-      private class BluetoothMapServiceConnection implements ServiceConnection {
-          /**
-           * Called when the Manager connects to service via the binder */
-          @Override
-          public void onServiceConnected(ComponentName name, IBinder binder) {
-              // onServiceConnected can be called either because we called onBind and in which case
-              // connection state should already be CONNECTING, or it could be called because the
-              // service came back up in which case we need to set it to CONNECTING (from
-              // DISCONNECTED).
-              if (mConnectionState == CONNECTED) {
-                  throw new IllegalStateException(
-                      "Cannot be in connected state while (re)connecting.");
-              }
-              // We may either be in CONNECTING or DISCONNECTED state here. Its safe to set to
-              // CONNECTING in any scenario.
-              mConnectionState = CONNECTING;
+    /**
+     * Implements the callbacks when changes in the binder connection of Manager (this) and the
+     * BluetoothMapService occur.
+     * For a list of possible states that the binder connection can exist, see DISCONNECTED,
+     * CONNECTING and CONNECTED states above.
+     */
+    private class BluetoothMapServiceConnection implements ServiceConnection {
+        /**
+         * Called when the Manager connects to service via the binder */
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder binder) {
+            // onServiceConnected can be called either because we called onBind and in which case
+            // connection state should already be CONNECTING, or it could be called because the
+            // service came back up in which case we need to set it to CONNECTING (from
+            // DISCONNECTED).
+            if (mConnectionState == CONNECTED) {
+                throw new IllegalStateException(
+                    "Cannot be in connected state while (re)connecting.");
+            }
+            // We may either be in CONNECTING or DISCONNECTED state here. Its safe to set to
+            // CONNECTING in any scenario.
+            mConnectionState = CONNECTING;
 
-              if (DBG) {
-                  Log.d(TAG, "BluetoothMapServiceConnection.onServiceConnected name=" +
-                      name + " binder=" + binder);
-              }
+            if (DBG) {
+                Log.d(TAG, "BluetoothMapServiceConnection.onServiceConnected name=" +
+                    name + " binder=" + binder);
+            }
 
-              // Save the binder for future calls to service.
-              mServiceBinder = IBluetoothMapService.Stub.asInterface(binder);
+            // Save the binder for future calls to service.
+            mServiceBinder = IBluetoothMapService.Stub.asInterface(binder);
 
             // Register the callbacks to the service.
             mServiceCallbacks = new ServiceCallbacks(BluetoothMapManager.this);
@@ -688,3 +688,4 @@
         Log.d(TAG, "dumpState(). Connection State: " + mConnectionState);
     }
 }
+
diff --git a/car-cluster-demo-renderer/res/layout/instrument_cluster.xml b/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
index 7a22e53..64fb4ae 100644
--- a/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
+++ b/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
@@ -23,7 +23,7 @@
     <LinearLayout
             android:orientation="vertical"
             android:layout_width="214dp"
-            android:layout_height="match_parent" android:layout_weight="0.14">
+            android:layout_height="match_parent">
     </LinearLayout>
     <LinearLayout
             android:orientation="vertical"
@@ -64,4 +64,33 @@
             />
         </LinearLayout>
     </LinearLayout>
+    <LinearLayout android:orientation="vertical" android:layout_width="wrap_content"
+                  android:layout_height="match_parent" android:layout_weight="0.63"
+                  android:layout_gravity="center_vertical" android:weightSum="1"
+                  android:layout_marginTop="0dp">
+        <LinearLayout android:orientation="vertical" android:layout_width="match_parent"
+                      android:layout_height="wrap_content" android:layout_weight="0.63"
+                      android:layout_gravity="center_horizontal" android:weightSum="1"
+                      android:layout_marginTop="100dp"
+                      android:background="@android:color/background_dark"
+                      android:id="@+id/media_layout">
+            <ImageView android:layout_width="160dp" android:layout_height="100dp"
+                       android:id="@+id/media_image" android:layout_gravity="center_horizontal"
+                       android:background="@android:color/holo_blue_light"
+                       android:scaleType="center"/>
+            <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
+                      android:textAppearance="?android:attr/textAppearanceMedium"
+                      android:id="@+id/media_album" android:gravity="center"
+                      android:layout_gravity="center_horizontal" android:visibility="gone"/>
+            <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
+                      android:textAppearance="?android:attr/textAppearanceMedium"
+                      android:id="@+id/media_track" android:gravity="center"
+                      android:layout_gravity="center_horizontal" android:textSize="28sp"/>
+            <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
+                      android:textAppearance="?android:attr/textAppearanceMedium"
+                      android:id="@+id/media_artist" android:gravity="center"
+                      android:textIsSelectable="false"
+            />
+        </LinearLayout>
+    </LinearLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
index 8bfd2d4..f3874d3 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
@@ -15,86 +15,47 @@
  */
 package android.car.cluster.demorenderer;
 
-import android.car.cluster.InstrumentClusterRenderer;
-import android.car.cluster.NavigationRenderer;
 import android.car.navigation.CarNavigationInstrumentCluster;
+import android.car.cluster.renderer.DisplayConfiguration;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
+import android.car.cluster.renderer.MediaRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
 import android.content.Context;
-import android.os.Looper;
-import android.util.Log;
-import android.view.Display;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import android.view.View;
 
 /**
  * Demo implementation of {@code InstrumentClusterRenderer}.
  */
 public class DemoInstrumentClusterRenderer extends InstrumentClusterRenderer {
-    private final static String TAG = DemoInstrumentClusterRenderer.class.getSimpleName();
-
-    private static int TIMEOUT_MS = 5000;
 
     private DemoInstrumentClusterView mView;
-    private DemoPresentation mPresentation;
-    private CountDownLatch mPresentationCreatedLatch = new CountDownLatch(1);
-    private Looper mUiLooper;
+    private Context mContext;
 
     @Override
-    public void onCreate(final Context context, final Display display) {
-        new Thread() {
-            @Override
-            public void run() {
-                Log.d(TAG, "UI thread started.");
-                Looper.prepare();
-                mPresentation = new DemoPresentation(context, display);
-                mView = new DemoInstrumentClusterView(mPresentation.getContext());
-                mPresentation.setContentView(mView);
-                mPresentationCreatedLatch.countDown();
-                mUiLooper = Looper.myLooper();
-                Looper.loop();
-            }
-        }.start();
-    }
-
-    private boolean waitForPresentation() {
-        try {
-            boolean ready = mPresentationCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            if (!ready) {
-                Log.w(TAG, "Presentation was not created within " + TIMEOUT_MS + "ms.",
-                        new RuntimeException() /* for stack trace */);
-            }
-            return ready;
-        } catch (InterruptedException e) {
-            Log.e(TAG, "Presentation creation interrupted.", e);
-            throw new IllegalStateException(e);
-        }
+    public void onCreate(Context context) {
+        mContext = context;
     }
 
     @Override
-    public void onStart() {
-        if (!waitForPresentation()) {
-            return;
-        }
-
-        mPresentation.show();
+    public View onCreateView(DisplayConfiguration displayConfiguration) {
+        mView = new DemoInstrumentClusterView(mContext);
+        return mView;
     }
 
     @Override
-    public void onStop() {
-        if (!waitForPresentation()) {
-            return;
-        }
+    public void onStart() { }
 
-        mPresentation.dismiss();
-    }
+    @Override
+    public void onStop() { }
 
     @Override
     protected NavigationRenderer createNavigationRenderer() {
-        if (!waitForPresentation()) {
-            return null;
-        }
+        return new DemoNavigationRenderer(mView);
+    }
 
-        return new DemoNavigationRenderer(mView, mUiLooper);
+    @Override
+    protected MediaRenderer createMediaRenderer() {
+        return new DemoMediaRenderer(mView);
     }
 
     @Override
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
index 2e6ef30..fbec788 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
@@ -20,12 +20,13 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 /**
  * This class is responsible for drawing the whole instrument cluster.
  */
-public class DemoInstrumentClusterView extends FrameLayout{
+public class DemoInstrumentClusterView extends FrameLayout {
 
     private final String TAG = DemoInstrumentClusterView.class.getSimpleName();
 
@@ -33,6 +34,11 @@
     private TextView eventTitleView;
     private TextView distanceView;
     private View navPanel;
+    private TextView mediaArtistView;
+    private TextView mediaAlbumView;
+    private TextView mediaTrackView;
+    private ImageView mediaImageView;
+    private View mediaPanel;
 
     public DemoInstrumentClusterView(Context context) {
         super(context);
@@ -78,6 +84,27 @@
         distanceView.setText(distance);
     }
 
+    public void setMediaData(final CharSequence artist, final CharSequence album,
+            final CharSequence track, final Bitmap image) {
+        Log.d(TAG, "setMediaData" + " artist = " + artist + ", album: " + album + ", track: " +
+                track + ", bitmap: " + image);
+
+        mediaArtistView.setText(artist);
+        mediaAlbumView.setText(album);
+        mediaTrackView.setText(track);
+        mediaImageView.setImageBitmap(image);
+    }
+
+    public void showMedia() {
+        Log.d(TAG, "showMedia");
+        mediaPanel.setVisibility(VISIBLE);
+    }
+
+    public void hideMedia() {
+        Log.d(TAG, "hideMedia");
+        mediaPanel.setVisibility(INVISIBLE);
+    }
+
     private void init() {
         Log.d(TAG, "init");
         View rootView = inflate(getContext(), R.layout.instrument_cluster, null);
@@ -86,6 +113,12 @@
         distanceView = (TextView) rootView.findViewById(R.id.nav_distance);
         navPanel = rootView.findViewById(R.id.nav_layout);
 
+        mediaArtistView = (TextView) rootView.findViewById(R.id.media_artist);
+        mediaAlbumView = (TextView) rootView.findViewById(R.id.media_album);
+        mediaTrackView = (TextView) rootView.findViewById(R.id.media_track);
+        mediaImageView = (ImageView) rootView.findViewById(R.id.media_image);
+        mediaPanel = rootView.findViewById(R.id.media_layout);
+
         setSpeed("0");
 
         addView(rootView);
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java
new file mode 100644
index 0000000..4e4703a
--- /dev/null
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.demorenderer;
+
+import android.car.cluster.renderer.MediaRenderer;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
+import android.util.Log;
+
+/**
+ * Demo of rendering media data in instrument cluster.
+ */
+public class DemoMediaRenderer extends MediaRenderer {
+
+    private static final String TAG = DemoMediaRenderer.class.getSimpleName();
+
+
+    private final DemoInstrumentClusterView mView;
+
+    private static final String[] PREFERRED_BITMAP_ORDER = {
+            MediaMetadata.METADATA_KEY_ALBUM_ART,
+            MediaMetadata.METADATA_KEY_ART,
+            MediaMetadata.METADATA_KEY_DISPLAY_ICON
+    };
+
+
+    public DemoMediaRenderer(DemoInstrumentClusterView view) {
+        mView = view;
+    }
+
+    @Override
+    public void onPlaybackStateChanged(PlaybackState playbackState) {
+        Log.d(TAG, "onPlaybackStateChanged: " + playbackState);
+    }
+
+    @Override
+    public void onMetadataChanged(MediaMetadata metadata) {
+        Log.d(TAG, "onMetadataChanged: " + metadata);
+        if (metadata != null) {
+            CharSequence artist = metadata.getText(MediaMetadata.METADATA_KEY_ARTIST);
+            CharSequence album = metadata.getText(MediaMetadata.METADATA_KEY_ALBUM);
+            CharSequence track = metadata.getText(MediaMetadata.METADATA_KEY_TITLE);
+            Bitmap bitmap = getMetadataBitmap(metadata);
+
+            mView.setMediaData(artist, album, track, bitmap);
+            mView.showMedia();
+        } else {
+            mView.setMediaData(null, null, null, null);
+            mView.hideMedia();
+        }
+    }
+
+    private Bitmap getMetadataBitmap(MediaMetadata metadata) {
+        // Get the best art bitmap we can find
+        for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
+            Bitmap bitmap = metadata.getBitmap(PREFERRED_BITMAP_ORDER[i]);
+            if (bitmap != null) {
+                return bitmap;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
index a1deddf..5ddb822 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
@@ -19,11 +19,9 @@
 import static android.car.navigation.CarNavigationManager.TURN_SIDE_RIGHT;
 import static android.car.navigation.CarNavigationManager.TURN_TURN;
 
-import android.car.cluster.NavigationRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import android.util.Pair;
 
@@ -38,7 +36,6 @@
     private static final String TAG = DemoNavigationRenderer.class.getSimpleName();
 
     private final DemoInstrumentClusterView mView;
-    private final Handler mHandler;
     private final Context mContext;
 
     private final static Map<Pair<Integer, Integer>, Integer> sTurns;
@@ -50,30 +47,19 @@
         // TODO: add more localized strings here.
     }
 
-    DemoNavigationRenderer(DemoInstrumentClusterView view, Looper looper) {
+    DemoNavigationRenderer(DemoInstrumentClusterView view) {
         mView = view;
-        mHandler = new Handler(looper);
         mContext = view.getContext();
     }
 
     @Override
     public void onStartNavigation() {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mView.showNavigation();
-            }
-        });
+        mView.showNavigation();
     }
 
     @Override
     public void onStopNavigation() {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mView.hideNavigation();
-            }
-        });
+        mView.hideNavigation();
     }
 
     @Override
@@ -83,30 +69,12 @@
         final String localizedTitle = String.format(
                 mContext.getString(R.string.nav_event_title_format), localizedAction, road);
 
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mView.setNextTurn(image, localizedTitle);
-            }
-        });
+        mView.setNextTurn(image, localizedTitle);
     }
 
     @Override
     public void onNextTurnDistanceChanged(final int distanceMeters, int timeSeconds) {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mView.setNextTurnDistance(toHumanReadableDistance(distanceMeters));
-            }
-        });
-    }
-
-    public void runOnUiThread(Runnable action) {
-        if (Looper.myLooper() == mHandler.getLooper()) {
-            action.run();
-        } else {
-            mHandler.post(action);
-        }
+        mView.setNextTurnDistance(toHumanReadableDistance(distanceMeters));
     }
 
     private String getLocalizedNavigationAction(int event, int turnSide) {
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
index 4477c21..744d550 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
@@ -15,7 +15,7 @@
  */
 package android.car.cluster.demorenderer;
 
-import android.car.cluster.InstrumentClusterRenderer;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
 
 /**
  * This factory class will be requested by android.car using reflection in order to get an instance
diff --git a/car-lib/Android.mk b/car-lib/Android.mk
index 9af9498..4ab6af6 100644
--- a/car-lib/Android.mk
+++ b/car-lib/Android.mk
@@ -14,6 +14,9 @@
 #
 #
 
+#disble build in PDK, missing aidl import breaks build
+ifneq ($(TARGET_BUILD_PDK),true)
+
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
@@ -24,3 +27,5 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
 
 include $(BUILD_JAVA_LIBRARY)
+
+endif #TARGET_BUILD_PDK
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index cb55d07..93ad7cf 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -29,13 +29,13 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.car.content.pm.CarPackageManager;
+import android.car.hardware.camera.CarCameraManager;
 import android.car.hardware.CarSensorManager;
 import android.car.hardware.hvac.CarHvacManager;
 import android.car.hardware.radio.CarRadioManager;
 import android.car.media.CarAudioManager;
 import android.car.navigation.CarNavigationManager;
 import android.car.test.CarTestManagerBinderWrapper;
-import android.car.CarLibLog;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -43,8 +43,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -79,6 +77,9 @@
     public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
 
     @SystemApi
+    public static final String CAMERA_SERVICE = "camera";
+
+    @SystemApi
     public static final String RADIO_SERVICE = "radio";
 
     @SystemApi
@@ -110,6 +111,10 @@
     public static final String PERMISSION_CONTROL_APP_BLOCKING =
             "android.car.permission.CONTROL_APP_BLOCKING";
 
+    /** Permission necessary to access Car Camera APIs. */
+    @SystemApi
+    public static final String PERMISSION_CAR_CAMERA = "android.car.permission.CAR_CAMERA";
+
     /** Permission necessary to access Car HVAC APIs. */
     @SystemApi
     public static final String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC";
@@ -125,9 +130,15 @@
 
     /** Type of car connection: platform runs directly in car. */
     public static final int CONNECTION_TYPE_EMBEDDED = 5;
+    /**
+     * Type of car connection: platform runs directly in car but with mocked vehicle hal.
+     * This will only happen in testing environment.
+     * @hide
+     */
+    public static final int CONNECTION_TYPE_EMBEDDED_MOCKING = 6;
 
     /** @hide */
-    @IntDef({CONNECTION_TYPE_EMBEDDED})
+    @IntDef({CONNECTION_TYPE_EMBEDDED, CONNECTION_TYPE_EMBEDDED_MOCKING})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ConnectionType {}
 
@@ -185,23 +196,6 @@
     @GuardedBy("mCarManagerLock")
     private final HashMap<String, CarManagerBase> mServiceMap = new HashMap<>();
 
-    private final ICarConnectionListenerImpl mICarConnectionListenerImpl =
-            new ICarConnectionListenerImpl(this);
-    /**
-     * CarConnectionListener which did not get connected notification yet.
-     */
-    private final HashSet<CarConnectionListener> mCarConnectionNotConnectedListeners =
-            new HashSet<>();
-    /**
-     * CarConnectionListener which got connected notification already. Only listener with
-     * connected notification will get disconnected notification when disconnect event happens.
-     */
-    private final HashSet<CarConnectionListener> mCarConnectionConnectedListeners =
-            new HashSet<>();
-    /** CarConnectionListener to get current event. */
-    private final LinkedList<CarConnectionListener> mCarConnectionListenersForEvent =
-            new LinkedList<>();
-
     /** Handler for generic event dispatching. */
     private final Handler mEventHandler;
 
@@ -324,14 +318,6 @@
     }
 
     /**
-     * Tells if connected to car. Same as isConnected. Necessary for compatibility with support lib.
-     * @return
-     */
-    public boolean isConnectedToCar() {
-        return isConnected();
-    }
-
-    /**
      * Get car specific service as in {@link Context#getSystemService(String)}. Returned
      * {@link Object} should be type-casted to the desired service.
      * For example, to get sensor service,
@@ -378,59 +364,6 @@
     }
 
     /**
-     * Registers a {@link CarConnectionListener}.
-     *
-     * Avoid reregistering unregistered listeners. If an unregistered listener is reregistered,
-     * it may receive duplicate calls to {@link CarConnectionListener#onConnected}.
-     *
-     * @throws IllegalStateException if service is not connected.
-     */
-    public void registerCarConnectionListener(CarConnectionListener listener)
-            throws IllegalStateException {
-        ICar service = getICarOrThrow();
-        synchronized (this) {
-            if (mCarConnectionNotConnectedListeners.size() == 0 &&
-                    mCarConnectionConnectedListeners.size() == 0) {
-                try {
-                    service.registerCarConnectionListener(mICarConnectionListenerImpl);
-                } catch (RemoteException e) {
-                    // ignore
-                }
-            }
-            mCarConnectionNotConnectedListeners.add(listener);
-        }
-        mEventHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                handleCarConnected();
-            }
-        });
-    }
-
-    /**
-     * Unregisters a {@link CarConnectionListener}.
-     *
-     * <b>Note:</b> If this method is called from a thread besides the client's looper thread,
-     * there is no guarantee that the unregistered listener will not receive callbacks after
-     * this method returns.
-     */
-    public void unregisterCarConnectionListener(CarConnectionListener listener) {
-        synchronized (this) {
-            mCarConnectionNotConnectedListeners.remove(listener);
-            mCarConnectionConnectedListeners.remove(listener);
-            if (mCarConnectionNotConnectedListeners.size() == 0 &&
-                    mCarConnectionConnectedListeners.size() == 0) {
-                try {
-                    ICar service = getICarOrThrow();
-                    service.unregisterCarConnectionListener(mICarConnectionListenerImpl);
-                } catch (IllegalStateException | RemoteException e) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    /**
      * IllegalStateException from XyzCarService with special message is re-thrown as a different
      * exception. If the IllegalStateException is not understood then this message will throw the
      * original exception.
@@ -453,7 +386,7 @@
         CarManagerBase manager = null;
         switch (serviceName) {
             case AUDIO_SERVICE:
-                manager = new CarAudioManager(binder);
+                manager = new CarAudioManager(binder, mContext);
                 break;
             case SENSOR_SERVICE:
                 manager = new CarSensorManager(binder, mContext, mLooper);
@@ -470,6 +403,9 @@
             case CAR_NAVIGATION_SERVICE:
                 manager = new CarNavigationManager(binder, mLooper);
                 break;
+            case CAMERA_SERVICE:
+                manager = new CarCameraManager(binder, mContext);
+                break;
             case HVAC_SERVICE:
                 manager = new CarHvacManager(binder, mContext, mLooper);
                 break;
@@ -513,65 +449,4 @@
             mServiceMap.clear();
         }
     }
-
-    private void handleCarConnected() {
-        synchronized (this) {
-            mCarConnectionListenersForEvent.clear();
-            mCarConnectionConnectedListeners.addAll(mCarConnectionNotConnectedListeners);
-            mCarConnectionListenersForEvent.addAll(mCarConnectionNotConnectedListeners);
-            mCarConnectionNotConnectedListeners.clear();
-        }
-        for (CarConnectionListener listener : mCarConnectionListenersForEvent) {
-            listener.onConnected(CONNECTION_TYPE_EMBEDDED);
-        }
-    }
-
-    private void handleCarDisconnected() {
-        synchronized (this) {
-            mCarConnectionListenersForEvent.clear();
-            mCarConnectionNotConnectedListeners.addAll(mCarConnectionConnectedListeners);
-            mCarConnectionListenersForEvent.addAll(mCarConnectionConnectedListeners);
-            mCarConnectionConnectedListeners.clear();
-        }
-        for (CarConnectionListener listener : mCarConnectionListenersForEvent) {
-            listener.onDisconnected();
-        }
-    }
-
-    private static class ICarConnectionListenerImpl extends ICarConnectionListener.Stub {
-
-        private final WeakReference<Car> mCar;
-
-        private ICarConnectionListenerImpl(Car car) {
-            mCar = new WeakReference<>(car);
-        }
-
-        @Override
-        public void onConnected() {
-            final Car car = mCar.get();
-            if (car == null) {
-                return;
-            }
-            car.mEventHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    car.handleCarConnected();
-                }
-            });
-        }
-
-        @Override
-        public void onDisconnected() {
-            final Car car = mCar.get();
-            if (car == null) {
-                return;
-            }
-            car.mEventHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    car.handleCarDisconnected();
-                }
-            });
-        }
-    }
 }
diff --git a/car-lib/src/android/car/CarConnectionListener.java b/car-lib/src/android/car/CarConnectionListener.java
deleted file mode 100644
index 603f393..0000000
--- a/car-lib/src/android/car/CarConnectionListener.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car;
-
-/**
- * Listener for monitoring car's connection status.
- * Callbacks are called from the looper specified when constructing {@link Car}.
- */
-public interface CarConnectionListener {
-    /**
-     * Car has been connected. Does not guarantee that the car is still connected whilst this
-     * callback is running, so {@link CarNotConnectedException}s may still be thrown from
-     * {@link Car} method calls.
-     * @param connectionType Type of car connected.
-     */
-    void onConnected(@Car.ConnectionType int connectionType);
-    /** Car disconnected */
-    void onDisconnected();
-}
diff --git a/car-lib/src/android/car/ICar.aidl b/car-lib/src/android/car/ICar.aidl
index 01551bc..1510438 100644
--- a/car-lib/src/android/car/ICar.aidl
+++ b/car-lib/src/android/car/ICar.aidl
@@ -23,7 +23,5 @@
 /** @hide */
 interface ICar {
     IBinder getCarService(in String serviceName) = 0;
-    boolean isConnectedToCar() = 1;
-    void registerCarConnectionListener(in ICarConnectionListener listener) = 2;
-    void unregisterCarConnectionListener(in ICarConnectionListener listener) = 3;
+    int getCarConnectionType() = 1;
 }
diff --git a/car-lib/src/android/car/app/menu/CarMenuCallbacks.java b/car-lib/src/android/car/app/menu/CarMenuCallbacks.java
new file mode 100644
index 0000000..4e44428
--- /dev/null
+++ b/car-lib/src/android/car/app/menu/CarMenuCallbacks.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.app.menu;
+
+import android.os.Bundle;
+
+/**
+ * The callbacks that a car app needs to pass to a car ui provider for car menu interactions.
+ */
+public abstract class CarMenuCallbacks {
+    /**
+     * Called when the car menu wants to get the root.
+     *
+     * @param hints Hints that the Drawer can use to modify behavior. It can be null.
+     * @return The {@link RootMenu} which contains the root id and any hints
+     */
+    public abstract RootMenu getRootMenu(Bundle hints);
+
+    /**
+     * Query for information about the menu items that are contained within
+     * the specified id and subscribes to receive updates when they change.
+     *
+     * @param parentId The id of the parent menu item whose list of children
+     *            will be subscribed.
+     * @param callback The callback to receive the list of children.
+     */
+    public abstract void subscribe(String parentId, SubscriptionCallbacks callback);
+
+    /**
+     * Unsubscribe for changes to the children of the specified id.
+     * @param parentId The id of the parent menu item whose list of children
+     *            will be unsubscribed.
+     */
+    public abstract void unsubscribe(String parentId, SubscriptionCallbacks callbacks);
+
+    /**
+     * Called when the car menu has been opened.
+     */
+    public abstract void onCarMenuOpened();
+
+    /**
+     * Called when the car menu has been closed.
+     */
+    public abstract void onCarMenuClosed();
+
+    /**
+     * Called when a car menu item with the specified id has been clicked.
+     */
+    public abstract void onItemClicked(String id);
+
+    /**
+     * Called when a car menu item with the specified id has been long clicked.
+     */
+    public abstract boolean onItemLongClicked(String id);
+
+    /**
+     * Called when the menu button is clicked.
+     */
+    public abstract boolean onMenuClicked();
+
+    /**
+     * Called when the menu is opening.
+     */
+    public abstract void onCarMenuOpening();
+
+    /**
+     * Called when the menu is closing.
+     */
+    public abstract void onCarMenuClosing();
+}
diff --git a/car-lib/src/android/car/app/menu/CarMenuConstants.java b/car-lib/src/android/car/app/menu/CarMenuConstants.java
new file mode 100644
index 0000000..fe4b7fe
--- /dev/null
+++ b/car-lib/src/android/car/app/menu/CarMenuConstants.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.app.menu;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains keys to the metadata of car menu, such as id, title, icon, etc.
+ */
+public class CarMenuConstants {
+    public static class MenuItemConstants {
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true,
+                value = {FLAG_BROWSABLE, FLAG_FIRSTITEM})
+        public @interface MenuItemFlags {}
+
+        /**
+         * Flag: Indicates that the item has children of its own
+         */
+        public static final int FLAG_BROWSABLE = 0x1;
+
+        /**
+         * Flag: Indicates that the menu should scroll to this item
+         */
+        public static final int FLAG_FIRSTITEM = 0x2;
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(value = {WIDGET_CHECKBOX, WIDGET_TEXT_VIEW})
+        public @interface WidgetTypes {}
+
+        /**
+         * Use a checkbox widget.
+         */
+        public static final int WIDGET_CHECKBOX = 0x1;
+
+        /**
+         * Use a TextView widget
+         */
+        public static final int WIDGET_TEXT_VIEW = 0x2;
+
+        /**
+         * Key for the car menu title.
+         */
+        public static final String KEY_TITLE = "android.car.app.menu.title";
+
+        /**
+         * Key for the item title.
+         */
+        public static final String KEY_TEXT = "android.car.app.menu.text";
+
+        /**
+         * Key for the left icon.
+         */
+        public static final String KEY_LEFTICON = "android.car.app.menu.leftIcon";
+
+        /**
+         * Key for the right icon.
+         */
+        public static final String KEY_RIGHTICON = "android.car.app.menu.rightIcon";
+
+        /**
+         * Key for the text to be shown to the right of the item.
+         */
+        public static final String KEY_RIGHTTEXT = "android.car.app.menu.rightText";
+
+        /**
+         * Key for the widget type.
+         */
+        public static final String KEY_WIDGET = "android.car.app.menu.widget";
+
+        /**
+         * Key for the widget state.
+         */
+        public static final String KEY_WIDGET_STATE = "android.car.app.menu.widget_state";
+
+        /**
+         * Key for the value of whether the item is a place holder.
+         */
+        public static final String KEY_EMPTY_PLACEHOLDER = "android.car.app.menu.empty_placeholder";
+
+        /**
+         * Key for the flags.
+         */
+        public static final String KEY_FLAGS = "android.car.app.menu.flags";
+
+        /**
+         * Key for the menu item id.
+         */
+        public static final String KEY_ID = "android.car.app.menu.id";
+
+        /**
+         * Key for the remote views.
+         */
+        public static final String KEY_REMOTEVIEWS = "android.car.app.menu.remoteViews";
+    }
+}
\ No newline at end of file
diff --git a/car-lib/src/android/car/app/menu/CarUiEntry.java b/car-lib/src/android/car/app/menu/CarUiEntry.java
index b4968c8..c91e486 100644
--- a/car-lib/src/android/car/app/menu/CarUiEntry.java
+++ b/car-lib/src/android/car/app/menu/CarUiEntry.java
@@ -18,9 +18,8 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
 import android.view.View;
+import android.widget.EditText;
 
 /**
  * A base class for a car ui entry which is used for loading and manipulating common car
@@ -35,64 +34,184 @@
  * the set of must have functions in a CarUiProvider.
  */
 public abstract class CarUiEntry {
-    protected Context mAppContext;
-    protected Context mUiLibContext;
+    protected final Context mAppContext;
+    protected final Context mUiLibContext;
 
     public CarUiEntry(Context uiLibContext, Context appContext) {
-        mAppContext = appContext;
         mUiLibContext = uiLibContext.createConfigurationContext(
                 appContext.getResources().getConfiguration());
+        mAppContext = appContext;
     }
 
+    /**
+     * Return the content view.
+     */
     abstract public View getContentView();
 
-    abstract public void setCarMenuBinder(IBinder binder) throws RemoteException;
+    /**
+     * Set {@link android.car.app.menu.CarMenuCallbacks} from a car app for car menu interactions.
+     */
+    abstract public void setCarMenuCallbacks(CarMenuCallbacks callbacks);
 
+    /**
+     * Return the id of the main container in which app can render its own content.
+     */
     abstract public int getFragmentContainerId();
 
+    /**
+     * Set the background bitmap.
+     */
     abstract public void setBackground(Bitmap bitmap);
 
+    /**
+     * Set the background image resource.
+     */
     abstract public void setBackgroundResource(int resId);
 
+    /**
+     * Replace the menu button with the given bitmap.
+     */
+    abstract public void setMenuButtonBitmap(Bitmap bitmap);
+
+    /**
+     * Hide the menu button.
+     */
     abstract public void hideMenuButton();
 
+    /**
+     * Restore the menu button.
+     */
     abstract public void restoreMenuDrawable();
 
-    abstract public void setMenuProgress(float progress);
-
+    /**
+     * Set the color of the car menu scrim.
+     */
     abstract public void setScrimColor(int color);
 
+    /**
+     * Set the title of the car menu.
+     */
     abstract public void setTitle(CharSequence title);
 
-    abstract public void setTitleText(CharSequence title);
-
+    /**
+     * Close the car menu.
+     */
     abstract public void closeDrawer();
 
+    /**
+     * Open the car menu.
+     */
     abstract public void openDrawer();
 
+    /**
+     * Show the menu associated with the specified id, and set the car menu title.
+     */
     abstract public void showMenu(String id, String title);
 
+    /**
+     * Set the car menu button color.
+     */
     abstract public void setMenuButtonColor(int color);
 
+    /**
+     * Make the menu title visible.
+     */
     abstract public void showTitle();
 
+    /**
+     * Hide the menu title.
+     */
     abstract public void hideTitle();
 
+    /**
+     * Use the light car theme.
+     */
     abstract public void setLightMode();
 
+    /**
+     * Use the dark car theme.
+     */
     abstract public void setDarkMode();
 
+    /**
+     * Use automatic light/dark car theme based on ui mode.
+     */
     abstract public void setAutoLightDarkMode();
 
+    /**
+     * Called when the activity's onRestoreInstanceState is called.
+     */
     abstract public void onRestoreInstanceState(Bundle savedInstanceState);
 
+    /**
+     * Called when the activity's onSaveInstanceState is called.
+     */
     abstract public void onSaveInstanceState(Bundle outState);
 
+    /**
+     * Show the search box and set the click listener for the search box.
+     */
+    abstract public void showSearchBox(View.OnClickListener listener);
+
+    /**
+     * Set the color of the search box.
+     */
+    abstract public void setSearchBoxColors(int backgroundColor, int searchLogoColor,
+            int textColor, int hintTextColor);
+
+    /**
+     * Set the search box edit listener for monitoring input.
+     */
+    abstract public void setSearchBoxEditListener(SearchBoxEditListener listener);
+
+    /**
+     * Called when activity's onStart is called.
+     */
     abstract public void onStart();
 
+    /**
+     * Called when activity's onResume is called.
+     */
     abstract public void onResume();
 
+    /**
+     * Called when activity's onPause is called.
+     */
     abstract public void onPause();
 
+    /**
+     * Called when activity's onStop is called.
+     */
     abstract public void onStop();
+
+    /**
+     * Start input on the search box and show IME.
+     * @param hint hint text to show in the search box.
+     * @param searchBoxClickListener search box click listener.
+     * @return The search box {@link android.widget.EditText}.
+     */
+    abstract public EditText startInput(String hint,
+            View.OnClickListener searchBoxClickListener);
+
+    /**
+     * Set the view in the end of the search box as the search result is loading.
+     */
+    abstract public void setSearchBoxEndView(View view);
+
+    /**
+     * Returns the current user entered text in the search box.
+     */
+    abstract public CharSequence getSearchBoxText();
+
+    /**
+     * Called when input should be stopped.
+     */
+    abstract public void stopInput();
+
+    /**
+     * Show a toast message.
+     * @param msg text to show
+     * @param duration toast duration in millisecond.
+     */
+    abstract public void showToast(String msg, long duration);
 }
diff --git a/car-support-lib/src/android/support/car/app/menu/Root.java b/car-lib/src/android/car/app/menu/RootMenu.java
similarity index 72%
copy from car-support-lib/src/android/support/car/app/menu/Root.java
copy to car-lib/src/android/car/app/menu/RootMenu.java
index fbdf0e7..30eca92 100644
--- a/car-support-lib/src/android/support/car/app/menu/Root.java
+++ b/car-lib/src/android/car/app/menu/RootMenu.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -13,25 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.support.car.app.menu;
+package android.car.app.menu;
 
 import android.os.Bundle;
 
 /**
- * Stores the Root id for the menu. The Root is the main menu.
+ * Stores the root id for the menu. The RootMenu is the main menu.
  * Also allows passing hints through bundles. Hints allow the
  * the recipient to alter its behavior based on the hints.
  */
-public class Root {
+public class RootMenu {
     private final Bundle mBundle;
+    private final String mRootId;
 
     /**
      * Create a root with no extra hints.
      *
      * @param id Root id
      */
-    public Root(String id) {
+    public RootMenu(String id) {
         this(id, null);
     }
 
@@ -41,12 +41,9 @@
      * @param id Root id
      * @param extras Hints to pass along
      */
-    public Root(String id, Bundle extras) {
-        mBundle = new Bundle();
-        mBundle.putString(Constants.CarMenuConstants.KEY_ID, id);
-        if (extras != null) {
-            mBundle.putAll(extras);
-        }
+    public RootMenu(String id, Bundle extras) {
+        mRootId = id;
+        mBundle = extras;
     }
 
     /**
@@ -55,7 +52,7 @@
      * @return The root id
      */
     public String getId() {
-        return mBundle.getString(Constants.CarMenuConstants.KEY_ID);
+        return mRootId;
     }
 
     /**
diff --git a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl b/car-lib/src/android/car/app/menu/SearchBoxEditListener.java
similarity index 81%
rename from car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
rename to car-lib/src/android/car/app/menu/SearchBoxEditListener.java
index b8b6c55..c7057d3 100644
--- a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
+++ b/car-lib/src/android/car/app/menu/SearchBoxEditListener.java
@@ -13,16 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.app.menu;
+package android.car.app.menu;
 
-interface ISearchBoxEditListener {
+public abstract class SearchBoxEditListener {
     /**
      * The user hit enter on the keyboard.
      */
-    void onSearch(in String text) = 0;
+    public abstract void onSearch(String text);
 
     /**
      * The user changed the text in the search box with the keyboard.
      */
-    void onEdit(in String text) = 1;
+    public abstract void onEdit(String text);
 }
diff --git a/car-lib/src/android/car/app/menu/SubscriptionCallbacks.java b/car-lib/src/android/car/app/menu/SubscriptionCallbacks.java
new file mode 100644
index 0000000..9902436
--- /dev/null
+++ b/car-lib/src/android/car/app/menu/SubscriptionCallbacks.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.app.menu;
+
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * The callbacks to receive menu items updates.
+ */
+public abstract class SubscriptionCallbacks {
+    /**
+     * Called when the items with the specified parent id are loaded.
+     *
+     * @param items The list of menu items. To retrieve the content of the item, use the keys
+     *              defined in {@link CarMenuConstants.CarMenuConstants}.
+     */
+    public abstract void onChildrenLoaded(String parentId, List<Bundle> items);
+
+    /**
+     * Called when there is an error loading the items with the specified id.
+     */
+    public abstract void onError(String id);
+
+    /**
+     * Called when the car menu items with the specified parent id are changed.
+     *
+     * @param item The new menu item. To retrieve the content of the item, use the keys
+     *              defined in {@link CarMenuConstants.CarMenuConstants}.
+     */
+    public abstract void onChildChanged(String parentId, Bundle item);
+}
\ No newline at end of file
diff --git a/car-lib/src/android/car/cluster/InstrumentClusterRenderer.java b/car-lib/src/android/car/cluster/InstrumentClusterRenderer.java
deleted file mode 100644
index 737cbb8..0000000
--- a/car-lib/src/android/car/cluster/InstrumentClusterRenderer.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.car.cluster;
-
-import android.annotation.SystemApi;
-import android.car.navigation.CarNavigationInstrumentCluster;
-import android.content.Context;
-import android.view.Display;
-
-/**
- * Interface for instrument cluster rendering.
- *
- * TODO: implement instrument cluster feature list and extend API.
- *
- * @hide
- */
-@SystemApi
-public abstract class InstrumentClusterRenderer {
-
-    private NavigationRenderer mNavigationRenderer;
-
-    /**
-     * Calls once when instrument cluster should be created.
-     * @param context
-     * @param display
-     */
-    abstract public void onCreate(Context context, Display display);
-
-    abstract public void onStart();
-
-    abstract public void onStop();
-
-    /**
-     * Returns properties of instrument cluster for navigation.
-     */
-    abstract public CarNavigationInstrumentCluster getNavigationProperties();
-
-    abstract protected NavigationRenderer createNavigationRenderer();
-
-    public NavigationRenderer getNavigationRenderer() {
-        if (mNavigationRenderer == null) {
-            mNavigationRenderer = createNavigationRenderer();
-        }
-        return mNavigationRenderer;
-    }
-}
diff --git a/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java b/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
new file mode 100644
index 0000000..2b9f0a0
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.renderer;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * TODO: need to properly define this class and make it immutable.
+ *
+ * @hide
+ */
+@SystemApi
+public class DisplayConfiguration implements Parcelable {
+    private final Rect mPrimaryRegion;
+
+    @Nullable
+    private final Rect mSecondaryRegion;
+
+    public static final Parcelable.Creator<DisplayConfiguration> CREATOR =
+            new Parcelable.Creator<DisplayConfiguration>() {
+
+                public DisplayConfiguration createFromParcel(Parcel in) {
+                    return new DisplayConfiguration(in);
+                }
+
+                public DisplayConfiguration[] newArray(int size) {
+                    return new DisplayConfiguration[size];
+                }
+            };
+
+
+    public DisplayConfiguration(Parcel in) {
+        mPrimaryRegion = in.readTypedObject(Rect.CREATOR);
+        mSecondaryRegion = in.readTypedObject(Rect.CREATOR);
+    }
+
+    public DisplayConfiguration(Rect primaryRegion) {
+        this(primaryRegion, null);
+    }
+
+    public DisplayConfiguration(Rect primaryRegion, @Nullable Rect secondaryRegion) {
+        mPrimaryRegion = primaryRegion;
+        mSecondaryRegion = secondaryRegion;
+    }
+
+
+    /** Region that will be fully visible in instrument cluster */
+    public Rect getPrimaryRegion() {
+        return mPrimaryRegion;
+    }
+
+    /**
+     * The region that includes primary region + may include some additional space that might
+     * be partially visible in the instrument cluster. It is useful to fade-out primary
+     * rendering for example or adding background image.
+     */
+    @Nullable
+    public Rect getSecondaryRegion() {
+        return mSecondaryRegion;
+    }
+
+    /** Returns true if secondary region is strongly greater then primary region */
+    public boolean hasSecondaryRegion() {
+        return mSecondaryRegion != null
+                && mSecondaryRegion.width() > mPrimaryRegion.width()
+                && mSecondaryRegion.height() > mPrimaryRegion.height();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedObject(mPrimaryRegion, 0);
+        dest.writeTypedObject(mSecondaryRegion, 0);
+    }
+}
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderer.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderer.java
new file mode 100644
index 0000000..77eac4b
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.renderer;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.UiThread;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.content.Context;
+import android.view.View;
+
+/**
+ * Interface for instrument cluster rendering.
+ *
+ * TODO: implement instrument cluster feature list and extend API.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class InstrumentClusterRenderer {
+
+    @Nullable private NavigationRenderer mNavigationRenderer;
+    @Nullable private MediaRenderer mMediaRenderer;
+
+    /**
+     * Calls once when instrument cluster should be created.
+     */
+    abstract public void onCreate(Context context);
+
+    @UiThread
+    abstract public View onCreateView(DisplayConfiguration displayConfiguration);
+
+    @UiThread
+    abstract public void onStart();
+
+    @UiThread
+    abstract public void onStop();
+
+    /**
+     * Returns properties of instrument cluster for navigation.
+     */
+    abstract public CarNavigationInstrumentCluster getNavigationProperties();
+
+    @UiThread
+    abstract protected NavigationRenderer createNavigationRenderer();
+
+    @UiThread
+    abstract protected MediaRenderer createMediaRenderer();
+
+    /** The method is thread-safe, callers should cache returned object. */
+    @Nullable
+    public synchronized NavigationRenderer getNavigationRenderer() {
+        return mNavigationRenderer;
+    }
+
+    /** The method is thread-safe, callers should cache returned object. */
+    @Nullable
+    public synchronized MediaRenderer getMediaRenderer() {
+        return mMediaRenderer;
+    }
+
+    /**
+     * This method is called by car service after onCreateView to initialize private members. The
+     * method should not be overridden by subclasses.
+     */
+    @UiThread
+    public synchronized final void initialize() {
+        mNavigationRenderer = createNavigationRenderer();
+        mMediaRenderer = createMediaRenderer();
+    }
+}
diff --git a/car-lib/src/android/car/cluster/renderer/MediaRenderer.java b/car-lib/src/android/car/cluster/renderer/MediaRenderer.java
new file mode 100644
index 0000000..4620cc3
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/MediaRenderer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.renderer;
+
+import android.annotation.SystemApi;
+import android.annotation.UiThread;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
+
+/**
+ * Represents renderer of current media status in instrument cluster.
+ *
+ * @hide
+ */
+@SystemApi
+@UiThread
+public abstract class MediaRenderer {
+    public abstract void onPlaybackStateChanged(PlaybackState playbackState);
+    public abstract void onMetadataChanged(MediaMetadata metadata);
+}
diff --git a/car-lib/src/android/car/cluster/NavigationRenderer.java b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
similarity index 93%
rename from car-lib/src/android/car/cluster/NavigationRenderer.java
rename to car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
index 810f6e0..67eac5e 100644
--- a/car-lib/src/android/car/cluster/NavigationRenderer.java
+++ b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
@@ -13,9 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.car.cluster;
+package android.car.cluster.renderer;
 
 import android.annotation.SystemApi;
+import android.annotation.UiThread;
 import android.graphics.Bitmap;
 
 /**
@@ -25,6 +26,7 @@
  * @hide
  */
 @SystemApi
+@UiThread
 public abstract class NavigationRenderer {
     abstract public void onStartNavigation();
     abstract public void onStopNavigation();
diff --git a/car-lib/src/android/car/hardware/CarSensorEvent.java b/car-lib/src/android/car/hardware/CarSensorEvent.java
index afc655f..d99e84d 100644
--- a/car-lib/src/android/car/hardware/CarSensorEvent.java
+++ b/car-lib/src/android/car/hardware/CarSensorEvent.java
@@ -16,11 +16,8 @@
 
 package android.car.hardware;
 
-import android.location.GpsSatellite;
-import android.location.Location;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemClock;
 
 
 /**
@@ -43,12 +40,12 @@
     public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0;
     /**
      * Index in {@link #floatValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
-     * sensor. This value is fuel level in coverable distance.
+     * sensor. This value is fuel level in coverable distance. The unit is Km.
      */
     public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1;
     /**
-     * Index in {@link #byteValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
-     * sensor. This value is set to 0 if fuel low level warning is on.
+     * Index in {@link #intValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
+     * sensor. This value is set to 1 if fuel low level warning is on.
      */
     public static final int INDEX_FUEL_LOW_WARNING = 0;
 
@@ -110,19 +107,6 @@
     public static final int DRIVE_STATUS_FULLY_RESTRICTED = DRIVE_STATUS_NO_VIDEO |
             DRIVE_STATUS_NO_KEYBOARD_INPUT | DRIVE_STATUS_NO_VOICE_INPUT | DRIVE_STATUS_NO_CONFIG |
             DRIVE_STATUS_LIMIT_MESSAGE_LEN;
-    /**
-     * Index for {@link CarSensorManager#SENSOR_TYPE_LOCATION} in floatValues.
-     * Each bit intValues[0] represents whether the corresponding data is present.
-     */
-    public static final int INDEX_LOCATION_LATITUDE  = 0;
-    public static final int INDEX_LOCATION_LONGITUDE = 1;
-    public static final int INDEX_LOCATION_ACCURACY  = 2;
-    public static final int INDEX_LOCATION_ALTITUDE  = 3;
-    public static final int INDEX_LOCATION_SPEED     = 4;
-    public static final int INDEX_LOCATION_BEARING   = 5;
-    public static final int INDEX_LOCATION_MAX = INDEX_LOCATION_BEARING;
-    public static final int INDEX_LOCATION_LATITUDE_INTS = 1;
-    public static final int INDEX_LOCATION_LONGITUDE_INTS = 2;
 
     /**
      * Index for {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT} in floatValues.
@@ -135,52 +119,6 @@
      */
     public static final int INDEX_ENVIRONMENT_PRESSURE = 1;
 
-    /**
-     * Indices for {@link CarSensorManager#SENSOR_TYPE_COMPASS} in floatValues.
-     * Angles are in degrees. Pitch or/and roll can be NaN if it is not available.
-     */
-    public static final int INDEX_COMPASS_BEARING = 0;
-    public static final int INDEX_COMPASS_PITCH   = 1;
-    public static final int INDEX_COMPASS_ROLL    = 2;
-
-    /**
-     * Indices for {@link CarSensorManager#SENSOR_TYPE_ACCELEROMETER} in floatValues.
-     * Acceleration (gravity) is in m/s^2. Any component can be NaN if it is not available.
-     */
-    public static final int INDEX_ACCELEROMETER_X = 0;
-    public static final int INDEX_ACCELEROMETER_Y = 1;
-    public static final int INDEX_ACCELEROMETER_Z = 2;
-
-    /**
-     * Indices for {@link CarSensorManager#SENSOR_TYPE_GYROSCOPE} in floatValues.
-     * Rotation speed is in rad/s. Any component can be NaN if it is not available.
-     */
-    public static final int INDEX_GYROSCOPE_X = 0;
-    public static final int INDEX_GYROSCOPE_Y = 1;
-    public static final int INDEX_GYROSCOPE_Z = 2;
-
-    /**
-     * Indices for {@link CarSensorManager#SENSOR_TYPE_GPS_SATELLITE}.
-     * Both byte values and float values are used.
-     * Two first bytes encode number of satellites in-use/in-view (or 0xFF if unavailable).
-     * Then optionally with INDEX_GPS_SATELLITE_ARRAY_BYTE_OFFSET offset and interval
-     * INDEX_GPS_SATELLITE_ARRAY_BYTE_INTERVAL between elements are encoded boolean flags of whether
-     * particular satellite from in-view participate in in-use subset.
-     * Float values with INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET offset and interval
-     * INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL between elements can optionally contain
-     * per-satellite values of signal strength and other values or NaN if unavailable.
-     */
-    public static final int INDEX_GPS_SATELLITE_NUMBER_IN_USE = 0;
-    public static final int INDEX_GPS_SATELLITE_NUMBER_IN_VIEW = 1;
-    public static final int INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET = 2;
-    public static final int INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL = 1;
-    public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET = 0;
-    public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL = 4;
-    public static final int INDEX_GPS_SATELLITE_PRN_OFFSET = 0;
-    public static final int INDEX_GPS_SATELLITE_SNR_OFFSET = 1;
-    public static final int INDEX_GPS_SATELLITE_AZIMUTH_OFFSET = 2;
-    public static final int INDEX_GPS_SATELLITE_ELEVATION_OFFSET = 3;
-
     private static final long MILLI_IN_NANOS = 1000000L;
 
     /** Sensor type for this event like {@link CarSensorManager#SENSOR_TYPE_CAR_SPEED}. */
@@ -357,10 +295,10 @@
 
     public static class FuelLevelData {
         public long timeStampNs;
-        /** If unsupported by the car, this value is 0. */
+        /** Fuel level in %. If unsupported by the car, this value is -1. */
         public int level;
-        /** If unsupported by the car, this value is 0. */
-        public int range;
+        /** Fuel as possible range in Km. If unsupported by the car, this value is -1. */
+        public float range;
         /** If unsupported by the car, this value is false. */
         public boolean lowFuelWarning;
     }
@@ -379,8 +317,21 @@
             data = new FuelLevelData();
         }
         data.timeStampNs = timeStampNs;
-        data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
-        data.range = (int) floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+        if (floatValues == null) {
+            data.level = -1;
+            data.range = -1;
+        } else {
+            if (floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE] < 0) {
+                data.level = -1;
+            } else {
+                data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
+            }
+            if (floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE] < 0) {
+                data.range = -1;
+            } else {
+                data.range = floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+            }
+        }
         data.lowFuelWarning = intValues[0] == 1;
         return data;
     }
@@ -454,81 +405,6 @@
         return data;
     }
 
-    public static class CompassData {
-        public long timeStampNs;
-        /** If unsupported by the car, this value is NaN. */
-        public float bearing;
-        /** If unsupported by the car, this value is NaN. */
-        public float pitch;
-        /** If unsupported by the car, this value is NaN. */
-        public float roll;
-    }
-
-    /**
-     * Convenience method for obtaining a {@link CompassData} object from a CarSensorEvent
-     * object with type {@link CarSensorManager#SENSOR_TYPE_COMPASS}.
-     *
-     * @param data an optional output parameter which, if non-null, will be used by this method
-     *     instead of a newly created object.
-     * @return a CompassData object corresponding to the data contained in the CarSensorEvent.
-     */
-    public CompassData getCompassData(CompassData data) {
-        checkType(CarSensorManager.SENSOR_TYPE_COMPASS);
-        if (data == null) {
-            data = new CompassData();
-        }
-        data.bearing = floatValues[INDEX_COMPASS_BEARING];
-        data.pitch = floatValues[INDEX_COMPASS_PITCH];
-        data.roll = floatValues[INDEX_COMPASS_ROLL];
-        return data;
-    }
-
-    /**
-     * Convenience method for obtaining a {@link Location} object from a CarSensorEvent
-     * object with type {@link CarSensorManager#SENSOR_TYPE_LOCATION}.
-     *
-     * @param location an optional output parameter which, if non-null, will be used by this method
-     *     instead of a newly created object.
-     * @return a Location object corresponding to the data contained in the CarSensorEvent.
-     */
-    public Location getLocation(Location location) {
-        checkType(CarSensorManager.SENSOR_TYPE_LOCATION);
-        if (location == null) {
-            location = new Location("Car-GPS");
-        }
-        // intValues[0]: bit flags for the presence of other values following.
-        int presense = intValues[0];
-        if ((presense & (0x1 << INDEX_LOCATION_LATITUDE)) != 0) {
-            int latE7 = intValues[INDEX_LOCATION_LATITUDE_INTS];
-            location.setLatitude(latE7 * 1e-7);
-        }
-        if ((presense & (0x1 << INDEX_LOCATION_LONGITUDE)) != 0) {
-            int longE7 = intValues[INDEX_LOCATION_LONGITUDE_INTS];
-            location.setLongitude(longE7 * 1e-7);
-        }
-        if ((presense & (0x1 << INDEX_LOCATION_ACCURACY)) != 0) {
-            location.setAccuracy(floatValues[INDEX_LOCATION_ACCURACY]);
-        }
-        if ((presense & (0x1 << INDEX_LOCATION_ALTITUDE)) != 0) {
-            location.setAltitude(floatValues[INDEX_LOCATION_ALTITUDE]);
-        }
-        if ((presense & (0x1 << INDEX_LOCATION_SPEED)) != 0) {
-            location.setSpeed(floatValues[INDEX_LOCATION_SPEED]);
-        }
-        if ((presense & (0x1 << INDEX_LOCATION_BEARING)) != 0) {
-            location.setBearing(floatValues[INDEX_LOCATION_BEARING]);
-        }
-        location.setElapsedRealtimeNanos(timeStampNs);
-        // There is a risk of scheduler delaying 2nd elapsedRealtimeNs value.
-        // But will not try to fix it assuming that is acceptable as UTC time's accuracy is not
-        // guaranteed in Location data.
-        long currentTimeMs = System.currentTimeMillis();
-        long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
-        location.setTime(
-                currentTimeMs - (elapsedRealtimeNs - timeStampNs) / MILLI_IN_NANOS);
-        return location;
-    }
-
     public static class DrivingStatusData {
         public long timeStampNs;
         public int status;
@@ -551,153 +427,6 @@
         return data;
     }
 
-    public static class AccelerometerData  {
-        public long timeStampNs;
-        /** If unsupported by the car, this value is NaN. */
-        public float x;
-        /** If unsupported by the car, this value is NaN. */
-        public float y;
-        /** If unsupported by the car, this value is NaN. */
-        public float z;
-    }
-
-    /**
-     * Convenience method for obtaining an {@link AccelerometerData} object from a CarSensorEvent
-     * object with type {@link CarSensorManager#SENSOR_TYPE_ACCELEROMETER}.
-     *
-     * @param data an optional output parameter which, if non-null, will be used by this method
-     *     instead of a newly created object.
-     * @return a AccelerometerData object corresponding to the data contained in the CarSensorEvent.
-     */
-    public AccelerometerData getAccelerometerData(AccelerometerData data) {
-        checkType(CarSensorManager.SENSOR_TYPE_ACCELEROMETER);
-        if (data == null) {
-            data = new AccelerometerData();
-        }
-        data.x = floatValues[INDEX_ACCELEROMETER_X];
-        data.y = floatValues[INDEX_ACCELEROMETER_Y];
-        data.z = floatValues[INDEX_ACCELEROMETER_Z];
-        return data;
-    }
-
-    public static class GyroscopeData {
-        public long timeStampNs;
-        /** If unsupported by the car, this value is NaN. */
-        public float x;
-        /** If unsupported by the car, this value is NaN. */
-        public float y;
-        /** If unsupported by the car, this value is NaN. */
-        public float z;
-    }
-
-    /**
-     * Convenience method for obtaining a {@link GyroscopeData} object from a CarSensorEvent
-     * object with type {@link CarSensorManager#SENSOR_TYPE_GYROSCOPE}.
-     *
-     * @param data an optional output parameter which, if non-null, will be used by this method
-     *     instead of a newly created object.
-     * @return a GyroscopeData object corresponding to the data contained in the CarSensorEvent.
-     */
-    public GyroscopeData getGyroscopeData(GyroscopeData data) {
-        checkType(CarSensorManager.SENSOR_TYPE_GYROSCOPE);
-        if (data == null) {
-            data = new GyroscopeData();
-        }
-        data.x = floatValues[INDEX_GYROSCOPE_X];
-        data.y = floatValues[INDEX_GYROSCOPE_Y];
-        data.z = floatValues[INDEX_GYROSCOPE_Z];
-        return data;
-    }
-
-    // android.location.GpsSatellite doesn't have a public constructor, so that can't be used.
-    /**
-     * Class that contains GPS satellite status. For more info on meaning of these fields refer
-     * to the documentation to the {@link GpsSatellite} class.
-     */
-    public static class GpsSatelliteData {
-        public long timeStampNs;
-        /**
-         * Number of satellites used in GPS fix or -1 of unavailable.
-         */
-        public int numberInUse = -1;
-        /**
-         * Number of satellites in view or -1 of unavailable.
-         */
-        public int numberInView = -1;
-        /**
-         * Per-satellite flag if this satellite was used for GPS fix.
-         * Can be null if per-satellite data is unavailable.
-         */
-        public boolean[] usedInFix = null;
-        /**
-         * Per-satellite pseudo-random id.
-         * Can be null if per-satellite data is unavailable.
-         */
-        public int[] prn = null;
-        /**
-         * Per-satellite signal to noise ratio.
-         * Can be null if per-satellite data is unavailable.
-         */
-        public float[] snr = null;
-        /**
-         * Per-satellite azimuth.
-         * Can be null if per-satellite data is unavailable.
-         */
-        public float[] azimuth = null;
-        /**
-         * Per-satellite elevation.
-         * Can be null if per-satellite data is unavailable.
-         */
-        public float[] elevation = null;
-    }
-
-    /**
-     * Convenience method for obtaining a {@link GpsSatelliteData} object from a CarSensorEvent
-     * object with type {@link CarSensorManager#SENSOR_TYPE_HVAC} with optional per-satellite info.
-     *
-     * @param data an optional output parameter which, if non-null, will be used by this method
-     *     instead of a newly created object.
-     * @param withPerSatellite whether to include per-satellite data.
-     * @return a GpsSatelliteData object corresponding to the data contained in the CarSensorEvent.
-     */
-    public GpsSatelliteData getGpsSatelliteData(GpsSatelliteData data,
-            boolean withPerSatellite) {
-        checkType(CarSensorManager.SENSOR_TYPE_GPS_SATELLITE);
-        if (data == null) {
-            data = new GpsSatelliteData();
-        }
-        final int intOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET;
-        final int intInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL;
-        final int floatOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET;
-        final int floatInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL;
-        final int numberOfSats = (floatValues.length - floatOffset) / floatInterval;
-
-        data.numberInUse = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_USE];
-        data.numberInView = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_VIEW];
-        if (withPerSatellite && data.numberInView >= 0) {
-            data.usedInFix = new boolean[numberOfSats];
-            data.prn = new int[numberOfSats];
-            data.snr = new float[numberOfSats];
-            data.azimuth = new float[numberOfSats];
-            data.elevation = new float[numberOfSats];
-
-            for (int i = 0; i < numberOfSats; ++i) {
-                int iInt = intOffset + intInterval * i;
-                int iFloat = floatOffset + floatInterval * i;
-                data.usedInFix[i] = intValues[iInt] != 0;
-                data.prn[i] = Math.round(
-                        floatValues[iFloat + CarSensorEvent.INDEX_GPS_SATELLITE_PRN_OFFSET]);
-                data.snr[i] =
-                        floatValues[iFloat + CarSensorEvent.INDEX_GPS_SATELLITE_SNR_OFFSET];
-                data.azimuth[i] = floatValues[iFloat
-                        + CarSensorEvent.INDEX_GPS_SATELLITE_AZIMUTH_OFFSET];
-                data.elevation[i] = floatValues[iFloat
-                        + CarSensorEvent.INDEX_GPS_SATELLITE_ELEVATION_OFFSET];
-            }
-        }
-        return data;
-    }
-
     /** @hide */
     @Override
     public String toString() {
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index 1573d69..fedf3de 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -42,13 +42,8 @@
  *  API for monitoring car sensor data.
  */
 public class CarSensorManager implements CarManagerBase {
-    /**
-     * SENSOR_TYPE_* represents type of sensor supported from the connected car.
-     * This sensor represents the direction of the car as an angle in degree measured clockwise
-     * with 0 degree pointing to north.
-     * Sensor data in {@link CarSensorEvent} is a float (floatValues[0]).
-     */
-    public static final int SENSOR_TYPE_COMPASS           = 1;
+    /** @hide */
+    public static final int SENSOR_TYPE_RESERVED1           = 1;
     /**
      * This sensor represents vehicle speed in m/s.
      * Sensor data in {@link CarSensorEvent} is a float which will be >= 0.
@@ -88,55 +83,39 @@
      * {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
      */
     public static final int SENSOR_TYPE_GEAR              = 7;
-    public static final int SENSOR_TYPE_RESERVED8         = 8;
     /**
      * Day/night sensor. Sensor data is intValues[0].
      */
-    public static final int SENSOR_TYPE_NIGHT             = 9;
-    /**
-     * Sensor type for location. Sensor data passed in floatValues.
-     */
-    public static final int SENSOR_TYPE_LOCATION          = 10;
+    public static final int SENSOR_TYPE_NIGHT             = 8;
+    /** @hide */
+    public static final int SENSOR_TYPE_REVERVED9        = 9;
     /**
      * Represents the current driving status of car. Different user interaction should be used
      * depending on the current driving status. Driving status is intValues[0].
      */
-    public static final int SENSOR_TYPE_DRIVING_STATUS    = 11;
+    public static final int SENSOR_TYPE_DRIVING_STATUS    = 10;
     /**
      * Environment like temperature and pressure.
      */
-    public static final int SENSOR_TYPE_ENVIRONMENT       = 12;
-
+    public static final int SENSOR_TYPE_ENVIRONMENT       = 11;
+    /** @hide */
+    public static final int SENSOR_TYPE_RESERVED12        = 12;
     /** @hide */
     public static final int SENSOR_TYPE_RESERVED13        = 13;
     /** @hide */
-    public static final int SENSOR_TYPE_ACCELEROMETER     = 14;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED15        = 15;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED16        = 16;
-    /** @hide */
-    public static final int SENSOR_TYPE_GPS_SATELLITE     = 17;
-    /** @hide */
-    public static final int SENSOR_TYPE_GYROSCOPE         = 18;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED19        = 19;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED20        = 20;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED21        = 21;
+    public static final int SENSOR_TYPE_RESERVED14        = 14;
 
     /**
      * Sensor type bigger than this is invalid. Always update this after adding a new sensor.
      * @hide
      */
-    private static final int SENSOR_TYPE_MAX              = SENSOR_TYPE_RESERVED21;
+    private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_ENVIRONMENT;
 
     /**
      * Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
      * {@link #SENSOR_TYPE_VENDOR_EXTENSION_END}] is for each car vendor's to use.
      * This should be only used for system app to access sensors not defined as standard types.
-     * So the sensor supproted in this range can vary depending on car models / manufacturers.
+     * So the sensor supported in this range can vary depending on car models / manufacturers.
      * 3rd party apps should not use sensors in this range as they are not compatible across
      * different cars. Additionally 3rd party apps trying to access sensor in this range will get
      * security exception as their access is restricted to system apps.
@@ -272,11 +251,9 @@
      * If the same listener is registered again for the same sensor, it will be either ignored or
      * updated depending on the rate.
      * <p>
-     * Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} for
-     * {@link #SENSOR_TYPE_LOCATION}, {@link Car#PERMISSION_SPEED} for
-     * {@link #SENSOR_TYPE_CAR_SPEED}, {@link Car#PERMISSION_MILEAGE} for
-     * {@link #SENSOR_TYPE_ODOMETER}, or {@link Car#PERMISSION_FUEL} for
-     * {@link #SENSOR_TYPE_FUEL_LEVEL}.
+     * Requires {@link Car#PERMISSION_SPEED} for {@link #SENSOR_TYPE_CAR_SPEED},
+     *  {@link Car#PERMISSION_MILEAGE} for {@link #SENSOR_TYPE_ODOMETER},
+     *  or {@link Car#PERMISSION_FUEL} for {@link #SENSOR_TYPE_FUEL_LEVEL}.
      *
      * @param listener
      * @param sensorType sensor type to subscribe.
@@ -388,7 +365,9 @@
     }
 
     /**
-     * Get the most recent CarSensorEvent for the given type.
+     * Get the most recent CarSensorEvent for the given type. Note that latest sensor data from car
+     * will not be available if it was never subscribed before. This call will return immediately
+     * with null if there is no data available.
      * @param type A sensor to request
      * @return null if there was no sensor update since connected to the car.
      * @throws CarNotConnectedException
diff --git a/car-lib/src/android/car/hardware/camera/CarCamera.java b/car-lib/src/android/car/hardware/camera/CarCamera.java
new file mode 100644
index 0000000..02be070
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/CarCamera.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.hardware.camera;
+
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * API for controlling camera system in cars
+ */
+@SystemApi
+public class CarCamera {
+    public final static String TAG = CarCamera.class.getSimpleName();
+    public final int mCameraType;
+    private final ICarCamera mService;
+
+    public CarCamera(ICarCamera service, int cameraType) {
+        mService = service;
+        mCameraType = cameraType;
+    }
+
+    public int getCapabilities() {
+        int capabilities;
+        try {
+            capabilities = mService.getCapabilities(mCameraType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in getCapabilities", e);
+            capabilities = 0;
+        }
+        return capabilities;
+    }
+
+    public Rect getCameraCrop() {
+        Rect rect;
+        try {
+            rect = mService.getCameraCrop(mCameraType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in getCameraCrop", e);
+            rect = null;
+        }
+        return rect;
+    }
+
+    public void setCameraCrop(Rect rect) {
+        try {
+            mService.setCameraCrop(mCameraType, rect);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in setCameraCrop", e);
+        }
+    }
+
+    public Rect getCameraPosition() {
+        Rect rect;
+        try {
+            rect = mService.getCameraPosition(mCameraType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in getCameraPosition", e);
+            rect = null;
+        }
+        return rect;
+    }
+
+    public void setCameraPosition(Rect rect) {
+        try {
+            mService.setCameraPosition(mCameraType, rect);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in setCameraPosition", e);
+        }
+    }
+
+    public CarCameraState getCameraState() {
+        CarCameraState state;
+        try {
+            state = mService.getCameraState(mCameraType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in getCameraState", e);
+            state = null;
+        }
+        return state;
+    }
+
+    public void setCameraState(CarCameraState state) {
+        try {
+            mService.setCameraState(mCameraType, state);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in setCameraState", e);
+        }
+    }
+}
+
diff --git a/car-lib/src/android/car/hardware/camera/CarCameraManager.java b/car-lib/src/android/car/hardware/camera/CarCameraManager.java
new file mode 100644
index 0000000..d531c95
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/CarCameraManager.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.hardware.camera;
+
+import android.annotation.SystemApi;
+import android.car.Car;
+import android.car.CarManagerBase;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * API for controlling camera system in cars
+ */
+@SystemApi
+public class CarCameraManager implements CarManagerBase {
+    public final static boolean DBG = true;
+    public final static String TAG = CarCameraManager.class.getSimpleName();
+
+    // Camera capabilities flags
+    public static final int ANDROID_OVERLAY_SUPPORT_FLAG    = 0x1;
+    public static final int CAMERA_CROP_SUPPORT_FLAG        = 0x2;
+    public static final int CAMERA_POSITIONING_SUPPORT_FLAG = 0x4;
+
+    // Camera types
+    public static final int CAR_CAMERA_TYPE_NONE            = 0;
+    public static final int CAR_CAMERA_TYPE_RVC             = 1;
+
+    private int[] mCameraList;
+    private final ICarCamera mService;
+
+    /**
+     * Get an instance of the CarCameraManager.
+     *
+     * Should not be obtained directly by clients, use {@link Car.getCarManager()} instead.
+     * @hide
+     */
+    public CarCameraManager(IBinder service, Context context) {
+        mService = ICarCamera.Stub.asInterface(service);
+        try {
+            mCameraList = mService.getCameraList();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in getCameraList", e);
+            mCameraList = null;
+        }
+    }
+
+    /**
+     *
+     * @return Array of CAR_CAMERA_TYPE_* telling which cameras are present
+     */
+    public int[] getCameraList() {
+        return mCameraList;
+    }
+
+    /**
+     *
+     * @param cameraType Camera type to query capabilites
+     * @return Bitmask of camera capabilities available for this device
+     */
+    public int getCameraCapabilities(int cameraType) {
+        int capabilities;
+        try {
+            capabilities = mService.getCapabilities(cameraType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in getCameraCapabilities", e);
+            capabilities = 0;
+        }
+        return capabilities;
+    }
+
+    public CarCamera openCamera(int cameraType) {
+        CarCamera camera = null;
+
+        // Find cameraType in the list of available cameras
+        for (int i : mCameraList) {
+            if(i == cameraType) {
+                camera = new CarCamera(mService, cameraType);
+                break;
+            }
+        }
+        return camera;
+    }
+
+    public void closeCamera(CarCamera camera) {
+        // TODO:  What should we do?
+    }
+
+    /** @hide */
+    @Override
+    public void onCarDisconnected() {
+    }
+}
+
diff --git a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl b/car-lib/src/android/car/hardware/camera/CarCameraState.aidl
similarity index 67%
copy from car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
copy to car-lib/src/android/car/hardware/camera/CarCameraState.aidl
index b8b6c55..a9cf858 100644
--- a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
+++ b/car-lib/src/android/car/hardware/camera/CarCameraState.aidl
@@ -13,16 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.app.menu;
 
-interface ISearchBoxEditListener {
-    /**
-     * The user hit enter on the keyboard.
-     */
-    void onSearch(in String text) = 0;
+package android.car.hardware.camera;
 
-    /**
-     * The user changed the text in the search box with the keyboard.
-     */
-    void onEdit(in String text) = 1;
-}
+parcelable CarCameraState;
diff --git a/car-lib/src/android/car/hardware/camera/CarCameraState.java b/car-lib/src/android/car/hardware/camera/CarCameraState.java
new file mode 100644
index 0000000..6f25903
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/CarCameraState.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.hardware.camera;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * CarCameraState object corresponds to a property of the car's Camera system
+ * @hide
+ */
+@SystemApi
+public class CarCameraState implements Parcelable {
+    private boolean mCameraIsOn;
+    private boolean mOverlayIsOn;
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCameraIsOn ? 1 : 0);
+        out.writeInt(mOverlayIsOn ? 1 : 0);
+    }
+
+    public static final Parcelable.Creator<CarCameraState> CREATOR
+            = new Parcelable.Creator<CarCameraState>() {
+        public CarCameraState createFromParcel(Parcel in) {
+            return new CarCameraState(in);
+        }
+
+        public CarCameraState[] newArray(int size) {
+            return new CarCameraState[size];
+        }
+    };
+
+    private CarCameraState(Parcel in) {
+        mCameraIsOn = in.readInt() == 1;
+        mOverlayIsOn = in.readInt() == 1;
+    }
+
+    /**
+     * Copy constructor
+     * @param that
+     */
+    public CarCameraState(CarCameraState that) {
+        mCameraIsOn = that.getCameraIsOn();
+        mOverlayIsOn = that.getOverlayIsOn();
+    }
+
+    /**
+     * Constructor
+     */
+    public CarCameraState(boolean overlayIsOn, boolean cameraIsOn) {
+        mCameraIsOn = cameraIsOn;
+        mOverlayIsOn = overlayIsOn;
+    }
+
+    // Getters.
+    public boolean getCameraIsOn()  { return mCameraIsOn;  }
+    public boolean getOverlayIsOn() { return mOverlayIsOn; }
+
+    // Setters.
+    public void setCameraIsOn(boolean v)  { mCameraIsOn = v; }
+    public void setOverlayIsOn(boolean v) { mOverlayIsOn = v; }
+
+    // Printer.
+    public String toString() {
+        String myString = "mCameraIsOn: "  + mCameraIsOn + " mOverlayIsOn: "  + mOverlayIsOn;
+        return myString;
+    }
+
+    // Comparator.
+    public boolean equals(Object o) {
+        if (o instanceof CarCameraState) {
+            CarCameraState that = (CarCameraState) o;
+
+            return (that.getCameraIsOn()  == mCameraIsOn &&
+                    that.getOverlayIsOn() == mOverlayIsOn);
+        }
+        return false;
+    }
+}
diff --git a/car-lib/src/android/car/hardware/camera/ICarCamera.aidl b/car-lib/src/android/car/hardware/camera/ICarCamera.aidl
new file mode 100644
index 0000000..53eee36
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/ICarCamera.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.hardware.camera;
+
+import android.car.hardware.camera.CarCameraState;
+import android.graphics.Rect;
+
+/** @hide */
+interface ICarCamera {
+    int[] getCameraList() = 0;
+
+    int getCapabilities(in int cameraType) = 1;
+
+    Rect getCameraCrop(in int cameraType) = 2;
+
+    void setCameraCrop(in int cameraType, in Rect rect) = 3;
+
+    Rect getCameraPosition(in int cameraType) = 4;
+
+    void setCameraPosition(in int cameraType, in Rect rect) = 5;
+
+    CarCameraState getCameraState(in int cameraType) = 6;
+
+    void setCameraState(in int cameraType, in CarCameraState state) = 7;
+}
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 55c6ea2..7abe90d 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -16,7 +16,10 @@
 package android.car.media;
 
 import android.annotation.IntDef;
+import android.content.Context;
 import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.car.CarManagerBase;
@@ -82,6 +85,7 @@
     public @interface CarAudioUsage {}
 
     private final ICarAudio mService;
+    private final AudioManager mAudioManager;
 
     /**
      * Get {@link AudioAttrbutes} relevant for the given usage in car.
@@ -98,13 +102,39 @@
         }
     }
 
+    /**
+     * Request audio focus.
+     * Send a request to obtain the audio focus.
+     * @param l
+     * @param requestAttributes
+     * @param durationHint
+     * @param flags
+     */
+    public int requestAudioFocus(OnAudioFocusChangeListener l,
+                                 AudioAttributes requestAttributes,
+                                 int durationHint,
+                                 int flags) throws IllegalArgumentException {
+        return mAudioManager.requestAudioFocus(l, requestAttributes, durationHint, flags);
+    }
+
+    /**
+     * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
+     * @param l
+     * @param aa
+     * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
+     */
+    public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
+        return mAudioManager.abandonAudioFocus(l, aa);
+    }
+
     @Override
     public void onCarDisconnected() {
         // TODO Auto-generated method stub
     }
 
     /** @hide */
-    public CarAudioManager(IBinder service) {
+    public CarAudioManager(IBinder service, Context context) {
         mService = ICarAudio.Stub.asInterface(service);
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
     }
 }
diff --git a/car-lib/src/android/car/navigation/CarNavigationManager.java b/car-lib/src/android/car/navigation/CarNavigationManager.java
index 090980b..d1df559 100644
--- a/car-lib/src/android/car/navigation/CarNavigationManager.java
+++ b/car-lib/src/android/car/navigation/CarNavigationManager.java
@@ -148,10 +148,8 @@
      *        used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}.  -1 if unused.
      * @param turnNumber turn number, counting around from the roundabout entry to the exit.  Only
      *        used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}.  -1 if unused.
-     * @param image image to be shown in the instrument cluster (PNG format).  Null if instrument
-     *        cluster type is {@link #INSTRUMENT_CLUSTER_TYPE_ENUM}, or if
-     *        the image parameters are malformed (length or width non-positive, or illegal
-     *        imageColorDepthBits) in the initial NavigationStatusService call.
+     * @param image image to be shown in the instrument cluster.  Null if instrument
+     *        cluster type doesn't support images.
      * @param turnSide turn side ({@link #TURN_SIDE_LEFT}, {@link #TURN_SIDE_RIGHT} or
      *        {@link #TURN_SIDE_UNSPECIFIED}).
      * @return true if successful.
@@ -210,7 +208,7 @@
     }
 
     /**
-     * @param listener {@link CarNavigationStatusListener} to be registered, replacing any existing
+     * @param listener {@link CarNavigationListener} to be registered, replacing any existing
      *        listeners.
      * @throws CarNotConnectedException
      */
@@ -231,7 +229,7 @@
     }
 
     /**
-     * Unregisters {@link CarNavigationStatusListener}.
+     * Unregisters {@link CarNavigationListener}.
      */
     public void unregisterListener() {
         try {
@@ -266,12 +264,11 @@
         mHandler.sendMessage(mHandler.obtainMessage(STOP));
     }
 
-    private static class ICarNavigationEventListenerImpl extends
-            ICarNavigationEventListener.Stub {
+    private static class ICarNavigationEventListenerImpl extends ICarNavigationEventListener.Stub {
         private final WeakReference<CarNavigationManager> mManager;
 
         public ICarNavigationEventListenerImpl(CarNavigationManager manager) {
-            mManager = new WeakReference<CarNavigationManager>(manager);
+            mManager = new WeakReference<>(manager);
         }
 
         @Override
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index df2febd..42fc9b5 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -82,6 +82,12 @@
     public static final int CONNECTION_TYPE_ADB_EMULATOR = 4;
     /** Type of car connection: platform runs directly in car. */
     public static final int CONNECTION_TYPE_EMBEDDED = 5;
+    /**
+     * Type of car connection: platform runs directly in car but with mocked vehicle hal.
+     * This will only happen in testing environment.
+     * @hide
+     */
+    public static final int CONNECTION_TYPE_EMBEDDED_MOCKING = 6;
 
     /** @hide */
     @IntDef({CONNECTION_TYPE_EMULATOR, CONNECTION_TYPE_USB, CONNECTION_TYPE_WIFI,
diff --git a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
index 69e608c..bb02d87 100644
--- a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
+++ b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
@@ -19,8 +19,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ServiceConnection;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.Message;
 import android.support.car.Car;
 import android.support.car.content.pm.CarPackageManagerEmbedded;
 import android.support.car.hardware.CarSensorManagerEmbedded;
@@ -50,13 +52,15 @@
     };
 
     private final android.car.Car mCar;
-    private final LinkedList<CarConnectionListenerProxy> mCarConnectionListenerProxies =
+    private final LinkedList<CarConnectionListener> mCarConnectionListeners =
             new LinkedList<>();
+    private final CallbackHandler mHandler;
 
     public CarServiceLoaderEmbedded(Context context, ServiceConnectionListener listener,
             Looper looper) {
         super(context, listener, looper);
         mCar = android.car.Car.createCar(context, mServiceConnection, looper);
+        mHandler = new CallbackHandler(looper);
     }
 
     @Override
@@ -71,7 +75,8 @@
 
     @Override
     public boolean isConnectedToCar() {
-        return mCar.isConnectedToCar();
+        // for embedded, connected to service means connected to car.
+        return mCar.isConnected();
     }
 
     @Override
@@ -80,42 +85,18 @@
     }
 
     @Override
-    public void registerCarConnectionListener(CarConnectionListener listener) {
-        CarConnectionListenerProxy newProxy = null;
+    public void registerCarConnectionListener(final CarConnectionListener listener) {
         synchronized (this) {
-            boolean alreadyRegistered = false;
-            for (CarConnectionListenerProxy proxy : mCarConnectionListenerProxies) {
-                if (proxy.isSameListener(listener)) {
-                    alreadyRegistered = true;
-                    break;
-                }
-            }
-            if (!alreadyRegistered) {
-                newProxy = new CarConnectionListenerProxy(listener);
-                mCarConnectionListenerProxies.add(newProxy);
-            }
+            mCarConnectionListeners.add(listener);
         }
-        if (newProxy != null) {
-            mCar.registerCarConnectionListener(newProxy);
-        }
+        // car service connection is checked when this is called. So just dispatch it.
+        mHandler.dispatchCarConnectionCall(listener, getCarConnectionType());
     }
 
     @Override
     public void unregisterCarConnectionListener(CarConnectionListener listener) {
-        CarConnectionListenerProxy matchingProxy = null;
         synchronized (this) {
-            for (CarConnectionListenerProxy proxy : mCarConnectionListenerProxies) {
-                if (proxy.isSameListener(listener)) {
-                    matchingProxy = proxy;
-                    break;
-                }
-            }
-            if (matchingProxy != null) {
-                mCarConnectionListenerProxies.remove(matchingProxy);
-            }
-        }
-        if (matchingProxy != null) {
-            mCar.unregisterCarConnectionListener(matchingProxy);
+            mCarConnectionListeners.remove(listener);
         }
     }
 
@@ -144,25 +125,26 @@
         }
     }
 
-    private static class CarConnectionListenerProxy implements android.car.CarConnectionListener {
-        private final CarConnectionListener mListener;
+    private static class CallbackHandler extends Handler {
 
-        public CarConnectionListenerProxy(CarConnectionListener listener) {
-            mListener = listener;
+        private  static final int MSG_DISPATCH_CAR_CONNECTION = 0;
+
+        private CallbackHandler(Looper looper) {
+            super(looper);
         }
 
-        public boolean isSameListener(CarConnectionListener listener) {
-            return mListener == listener;
+        private void dispatchCarConnectionCall(CarConnectionListener listener, int connectionType) {
+            sendMessage(obtainMessage(MSG_DISPATCH_CAR_CONNECTION, connectionType, 0, listener));
         }
 
         @Override
-        public void onConnected(int connectionType) {
-            mListener.onConnected(connectionType);
-        }
-
-        @Override
-        public void onDisconnected() {
-           mListener.onDisconnected();
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_DISPATCH_CAR_CONNECTION:
+                    CarConnectionListener listener = (CarConnectionListener) msg.obj;
+                    listener.onConnected(msg.arg1);
+                    break;
+            }
         }
     }
 }
diff --git a/car-support-lib/src/android/support/car/app/CarAppUtil.java b/car-support-lib/src/android/support/car/app/CarAppUtil.java
new file mode 100644
index 0000000..2371025
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/CarAppUtil.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.car.app;
+
+import android.content.Context;
+
+/**
+ * @hide
+ */
+public final class CarAppUtil {
+
+    /**
+     * PackageManager.FEATURE_AUTOMOTIVE from M. But redefine here to support L.
+     */
+    private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+    public static boolean isEmbeddedCar(Context context) {
+       return context.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE);
+    }
+}
diff --git a/car-support-lib/src/android/support/car/app/CarFragmentActivity.java b/car-support-lib/src/android/support/car/app/CarFragmentActivity.java
index a24b308..41a2d2a 100644
--- a/car-support-lib/src/android/support/car/app/CarFragmentActivity.java
+++ b/car-support-lib/src/android/support/car/app/CarFragmentActivity.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package android.support.car.app;
 
 import android.content.Context;
diff --git a/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java b/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java
index 92f0c6d..67d5f1f 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java
@@ -19,43 +19,27 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
 import android.support.annotation.LayoutRes;
 import android.support.car.Car;
 import android.support.car.app.CarFragmentActivity;
-import android.support.car.input.CarEditable;
-import android.support.car.input.CarEditableListener;
+import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
 import android.support.car.input.CarInputManager;
-import android.support.car.input.CarRestrictedEditText;
 import android.support.v4.app.Fragment;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionWrapper;
-import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Base class for a car app which wants to use a drawer.
  */
 public abstract class CarDrawerActivity extends CarFragmentActivity {
     private static final String TAG = "CarDrawerActivity";
+
     private static final String KEY_DRAWERSHOWING =
             "android.support.car.app.CarDrawerActivity.DRAWER_SHOWING";
     private static final String KEY_INPUTSHOWING =
@@ -67,7 +51,7 @@
     private final CarUiController mUiController;
 
     private CarMenuCallbacks mMenuCallbacks;
-    private CarMenuCallbacksBinder mBinder;
+    private OnMenuClickListener mMenuClickListener;
     private boolean mDrawerShowing;
     private boolean mShowingSearchBox;
     private boolean mSearchBoxEnabled;
@@ -77,24 +61,29 @@
     private CarInputManager mInputManager;
     private EditText mSearchBoxView;
 
-    /**
-     * Simple interface to listen for keyboard events.
-     */
-    public interface SearchBoxEditListener {
+    public interface OnMenuClickListener {
         /**
-         * The user hit enter on the keyboard.
+         * Called when the menu button is clicked.
+         *
+         * @return True if event was handled. This will prevent the drawer from executing its
+         *         default action (opening/closing/going back). False if the event was not handled
+         *         so the drawer will execute the default action.
          */
-        void onSearch(String text);
-
-        /**
-         * The user changed the text in the search box with the keyboard.
-         */
-        void onEdit(String text);
+        boolean onClicked();
     }
 
     public CarDrawerActivity(Proxy proxy, Context context, Car car) {
         super(proxy, context, car);
-        mUiController = new CarUiController(this);
+        mUiController = createCarUiController();
+    }
+
+    /**
+     * Create a {@link android.support.car.app.menu.CarUiController}.
+     *
+     * Derived class can override this function to return a customized ui controller.
+     */
+    protected CarUiController createCarUiController() {
+        return CarUiController.createCarUiController(this);
     }
 
     @Override
@@ -110,16 +99,56 @@
         inflater.inflate(resourceId, parent, true);
     }
 
-    public void setContentFragment(Fragment fragment) {
-        super.setContentFragment(fragment, mUiController.getFragmentContainerId());
-    }
-
     @Override
     public View findViewById(@LayoutRes int id) {
         return super.findViewById(mUiController.getFragmentContainerId()).findViewById(id);
     }
 
     @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        super.setContentView(mUiController.getContentView());
+        mInputManager = getInputManager();
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mMenuCallbacks != null) {
+                    mMenuCallbacks.registerOnChildrenChangedListener(mMenuListener);
+                }
+                mOnCreateCalled = true;
+            }
+        });
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mMenuCallbacks != null) {
+                    mMenuCallbacks.unregisterOnChildrenChangedListener(mMenuListener);
+                    mMenuCallbacks = null;
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mDrawerShowing = savedInstanceState.getBoolean(KEY_DRAWERSHOWING);
+        mUiController.onRestoreInstanceState(savedInstanceState);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_DRAWERSHOWING, mDrawerShowing);
+        mUiController.onSaveInstanceState(outState);
+    }
+
+    @Override
     protected void onStart() {
         super.onStart();
         getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
@@ -144,27 +173,30 @@
         mUiController.onStop();
     }
 
+    /**
+     * Set the fragment in the main fragment container.
+     */
+    public void setContentFragment(Fragment fragment) {
+        super.setContentFragment(fragment, mUiController.getFragmentContainerId());
+    }
+
+    /**
+     * Return the main fragment container id for the app.
+     */
     public int getFragmentContainerId() {
         return mUiController.getFragmentContainerId();
     }
 
-    public interface OnMenuClickListener {
-        /**
-         * Called when the menu button is clicked.
-         *
-         * @return True if event was handled. This will prevent the drawer from executing its
-         *         default action (opening/closing/going back). False if the event was not handled
-         *         so the drawer will execute the default action.
-         */
-        boolean onClicked();
-    }
-
+    /**
+     * Set the callbacks for car menu interactions.
+     */
     public void setCarMenuCallbacks(final CarMenuCallbacks callbacks) {
         if (mOnCreateCalled) {
             throw new IllegalStateException(
                     "Cannot call setCarMenuCallbacks after onCreate has been called.");
         }
         mMenuCallbacks = callbacks;
+        mUiController.registerCarMenuCallbacks(callbacks);
     }
 
     /**
@@ -173,7 +205,7 @@
      * @param listener {@link OnMenuClickListener} that will listen for menu button clicks.
      */
     public void setOnMenuClickedListener(OnMenuClickListener listener) {
-        mBinder.setOnMenuClickedListener(listener);
+        mMenuClickListener = listener;
     }
 
     /**
@@ -239,6 +271,9 @@
         mUiController.setBackgroundResource(resId);
     }
 
+    /**
+     * Sets the color of the scrim to the right of the car menu drawer.
+     */
     public void setScrimColor(int color) {
         mUiController.setScrimColor(color);
     }
@@ -253,8 +288,19 @@
         mUiController.showMenu(id, title);
     }
 
-    private void registerCarMenuCallbacks(IBinder callbacks) {
-        mUiController.registerCarMenuCallbacks(callbacks);
+    public boolean onMenuClicked() {
+        if (mMenuClickListener != null) {
+            mMenuClickListener.onClicked();
+            return true;
+        }
+        return false;
+    }
+
+    public void restoreSearchBox() {
+        if (isSearchBoxEnabled()) {
+            mUiController.showSearchBox(mSearchBoxOnClickListener);
+            mShowingSearchBox = true;
+        }
     }
 
     private final CarMenuCallbacks.OnChildrenChangedListener mMenuListener =
@@ -262,71 +308,29 @@
                 @Override
                 public void onChildrenChanged(String parentId) {
                     if (mOnCreateCalled) {
-                        mBinder.onChildrenChanged(parentId);
+                        mUiController.onChildrenChanged(parentId);
                     }
                 }
 
                 @Override
                 public void onChildChanged(String parentId, Bundle item,
-                                           Drawable leftIcon, Drawable rightIcon) {
+                        Drawable leftIcon, Drawable rightIcon) {
                     DisplayMetrics metrics = getResources().getDisplayMetrics();
                     if (leftIcon != null) {
-                        item.putParcelable(Constants.CarMenuConstants.KEY_LEFTICON,
+                        item.putParcelable(MenuItemConstants.KEY_LEFTICON,
                                 Utils.snapshot(metrics, leftIcon));
                     }
 
                     if (rightIcon != null) {
-                        item.putParcelable(Constants.CarMenuConstants.KEY_RIGHTICON,
+                        item.putParcelable(MenuItemConstants.KEY_RIGHTICON,
                                 Utils.snapshot(metrics, rightIcon));
                     }
                     if (mOnCreateCalled) {
-                        mBinder.onChildChanged(parentId, item);
+                        mUiController.onChildChanged(parentId, item);
                     }
                 }
             };
 
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        super.setContentView(mUiController.getContentView());
-        mBinder = new CarMenuCallbacksBinder(this);
-        mInputManager = getInputManager();
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                registerCarMenuCallbacks(mBinder);
-                if (mMenuCallbacks != null) {
-                    mMenuCallbacks.registerOnChildrenChangedListener(mMenuListener);
-                }
-                mOnCreateCalled = true;
-            }
-        });
-    }
-
-    protected void onDestroy() {
-        super.onDestroy();
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mMenuCallbacks != null) {
-                    mMenuCallbacks.unregisterOnChildrenChangedListener(mMenuListener);
-                    mMenuCallbacks = null;
-                }
-            }
-        });
-    }
-
-    protected void onRestoreInstanceState(Bundle savedInstanceState) {
-        super.onRestoreInstanceState(savedInstanceState);
-        mDrawerShowing = savedInstanceState.getBoolean(KEY_DRAWERSHOWING);
-        mUiController.onRestoreInstanceState(savedInstanceState);
-    }
-
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putBoolean(KEY_DRAWERSHOWING, mDrawerShowing);
-        mUiController.onSaveInstanceState(outState);
-    }
-
     public void closeDrawer() {
         mUiController.closeDrawer();
     }
@@ -339,6 +343,18 @@
         return mDrawerShowing;
     }
 
+    public void setDrawerShowing(boolean showing) {
+        mDrawerShowing = showing;
+    }
+
+    public boolean isSearchBoxEnabled() {
+        return mSearchBoxEnabled;
+    }
+
+    public boolean isShowingSearchBox() {
+        return mShowingSearchBox;
+    }
+
     /**
      * Shows a small clickable {@link android.widget.EditText}.
      *
@@ -356,6 +372,10 @@
         mSearchBoxOnClickListener = listener;
     }
 
+    public void showSearchBox() {
+        showSearchBox(mSearchBoxOnClickListener);
+    }
+
     public void hideSearchBox() {
         if (isShowingSearchBox()) {
             stopInput();
@@ -363,12 +383,8 @@
         mSearchBoxEnabled = false;
     }
 
-    public boolean isShowingSearchBox() {
-        return mShowingSearchBox;
-    }
-
     public void setSearchBoxEditListener(SearchBoxEditListener listener) {
-        mUiController.setSearchBoxEditListener(new SearchBoxEditListenerBinder(listener));
+        mUiController.setSearchBoxEditListener(listener);
     }
 
     public void stopInput() {
@@ -399,15 +415,16 @@
      */
     public void startInput(final String hint, final View.OnClickListener onClickListener) {
         mInputManager = getInputManager();
-        EditText inputView = mUiController.startSearchInput(hint, onClickListener);
+        EditText inputView = mUiController.startInput(hint, onClickListener);
         getInputManager().startInput(inputView);
         mSearchBoxView = inputView;
         mShowingSearchBox = true;
     }
 
-    public void setSearchBoxColors(int backgroundColor, int googleLogoColor, int textColor,
+    public void setSearchBoxColors(int backgroundColor, int searchLogoColor, int textColor,
             int hintTextColor) {
-        mUiController.setSearchBoxColors(backgroundColor, googleLogoColor, textColor, hintTextColor);
+        mUiController.setSearchBoxColors(backgroundColor, searchLogoColor,
+                textColor, hintTextColor);
     }
 
     public void setSearchBoxEndView(View endView) {
@@ -415,234 +432,6 @@
     }
 
     public void showToast(String text, int duration) {
-    }
-
-    private static class SearchBoxEditListenerBinder extends ISearchBoxEditListener.Stub {
-        private final SearchBoxEditListener mListener;
-
-        public SearchBoxEditListenerBinder(SearchBoxEditListener listener) {
-            if (listener == null) {
-                throw new IllegalArgumentException("Listener cannot be null");
-            }
-            mListener = listener;
-        }
-
-        @Override
-        public void onSearch(String text) {
-            mListener.onSearch(text);
-        }
-
-        @Override
-        public void onEdit(String text) {
-            mListener.onEdit(text);
-        }
-    }
-
-    private static class CarMenuCallbacksBinder extends ICarMenuCallbacks.Stub {
-        // Map of subscribed ids to their respective callbacks.
-        private final Map<String, List<ISubscriptionCallbacks>> mSubscriptionMap = new HashMap<>();
-        private OnMenuClickListener mMenuClickListener;
-        private final WeakReference<CarDrawerActivity> mActivity;
-
-        CarMenuCallbacksBinder(CarDrawerActivity activity) {
-            mActivity = new WeakReference<>(activity);
-        }
-
-        @Override
-        public int getVersion() {
-            return 0;
-        }
-
-        @Override
-        public Bundle getRoot() throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null && activity.mMenuCallbacks != null) {
-                Root root = activity.mMenuCallbacks.onGetRoot(null);
-                return root.getBundle();
-            } else {
-                return null;
-            }
-        }
-
-        @Override
-        public synchronized void subscribe(final String parentId,
-                                           final ISubscriptionCallbacks callbacks) throws RemoteException {
-            if (!mSubscriptionMap.containsKey(parentId)) {
-                mSubscriptionMap.put(parentId, new ArrayList<ISubscriptionCallbacks>());
-            }
-            mSubscriptionMap.get(parentId).add(callbacks);
-            loadResultsForClient(parentId, callbacks);
-        }
-
-        @Override
-        public synchronized void unsubscribe(String id, ISubscriptionCallbacks callbacks)
-                throws RemoteException {
-            mSubscriptionMap.get(id).remove(callbacks);
-        }
-
-        @Override
-        public void onCarMenuOpened() throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null) {
-                activity.mDrawerShowing = true;
-                activity.mMenuCallbacks.onCarMenuOpened();
-            }
-        }
-
-
-        @Override
-        public void onCarMenuClosing() throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null) {
-                if (activity.mSearchBoxEnabled) {
-                    activity.mUiController.showSearchBox(activity.mSearchBoxOnClickListener);
-                    activity.mShowingSearchBox = true;
-                }
-            }
-        }
-
-        @Override
-        public void onCarMenuClosed() throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null) {
-                activity.mDrawerShowing = false;
-                if (activity.mSearchBoxEnabled && !activity.mShowingSearchBox) {
-                    activity.showSearchBox(activity.mSearchBoxOnClickListener);
-                }
-            }
-        }
-
-        @Override
-        public void onCarMenuOpening() throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null) {
-                activity.stopInput();
-            }
-        }
-
-        @Override
-        public void onItemClicked(String id) throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null) {
-                activity.mMenuCallbacks.onItemClicked(id);
-                // TODO: Add support for IME
-                activity.stopInput();
-            }
-        }
-
-        @Override
-        public boolean onItemLongClicked(String id) throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null) {
-                return activity.mMenuCallbacks.onItemLongClicked(id);
-            } else {
-                return false;
-            }
-        }
-
-        @Override
-        public void onStateChanged(int newState) throws RemoteException {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity != null) {
-                activity.mMenuCallbacks.onStateChanged(newState);
-            }
-        }
-
-        public void setOnMenuClickedListener(OnMenuClickListener listener) {
-            mMenuClickListener = listener;
-        }
-
-        @Override
-        public boolean onMenuClicked() {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity == null) {
-                return false;
-            }
-
-            if (mMenuClickListener != null) {
-                if (mMenuClickListener.onClicked()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public void onChildrenChanged(String parentId) {
-            if (mSubscriptionMap.containsKey(parentId)) {
-                loadResultsForAllClients(parentId);
-            }
-        }
-
-        public void reloadSubscribedMenus() {
-            for (String parentId : mSubscriptionMap.keySet()) {
-                loadResultsForAllClients(parentId);
-            }
-        }
-
-        private void loadResultsForClient(final String parentId,
-                                          final ISubscriptionCallbacks callbacks) {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity == null) {
-                return;
-            }
-
-            final CarMenu result = new CarMenu(activity.getResources().getDisplayMetrics()) {
-                @Override
-                protected void onResultReady(List<Bundle> list) {
-                    synchronized (CarMenuCallbacksBinder.this) {
-                        try {
-                            callbacks.onChildrenLoaded(parentId, list);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error calling onChildrenLoaded: ", e);
-                        }
-                    }
-                }
-            };
-
-            activity.mMenuCallbacks.onLoadChildren(parentId, result);
-            if (!result.isDone()) {
-                throw new IllegalStateException("You must either call sendResult() or detach() " +
-                        "before returning!");
-            }
-        }
-
-        private void loadResultsForAllClients(final String parentId) {
-            CarDrawerActivity activity = mActivity.get();
-            if (activity == null) {
-                return;
-            }
-
-            final CarMenu result = new CarMenu(activity.getResources().getDisplayMetrics()) {
-                @Override
-                protected void onResultReady(List<Bundle> list) {
-                    synchronized (CarMenuCallbacksBinder.this) {
-                        if (mSubscriptionMap.containsKey(parentId)) {
-                            try {
-                                for (ISubscriptionCallbacks callbacks :
-                                        mSubscriptionMap.get(parentId)) {
-                                    callbacks.onChildrenLoaded(parentId, list);
-                                }
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Error calling onChildrenLoaded: ", e);
-                            }
-                        }
-                    }
-                }
-            };
-
-            activity.mMenuCallbacks.onLoadChildren(parentId, result);
-        }
-
-        private synchronized void onChildChanged(String parentId, Bundle item) {
-            if (mSubscriptionMap.containsKey(parentId)) {
-                for (ISubscriptionCallbacks callbacks : mSubscriptionMap.get(parentId)) {
-                    try {
-                        callbacks.onChildChanged(parentId, item);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error calling onChildChanged: ", e);
-                    }
-                }
-            }
-        }
+        mUiController.showToast(text, duration);
     }
 }
diff --git a/car-support-lib/src/android/support/car/app/menu/CarMenu.java b/car-support-lib/src/android/support/car/app/menu/CarMenu.java
index 2b7658c..aeb7d7c 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarMenu.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarMenu.java
@@ -19,13 +19,10 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.support.annotation.IntDef;
-import android.support.car.app.menu.Constants.CarMenuConstants;
+import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
 import android.util.DisplayMetrics;
 import android.widget.RemoteViews;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -34,39 +31,6 @@
  * Use the {@link Builder} to populate the contents of the sublevel.
  */
 public class CarMenu {
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {CarMenu.FLAG_BROWSABLE, CarMenu.FLAG_FIRSTITEM})
-    public @interface MenuItemFlags {}
-
-    /**
-     * Flag: Indicates that the item has children of its own
-     */
-    public static final int FLAG_BROWSABLE = 0x1;
-
-    /**
-     * Flag: Indicates that the menu should scroll to this item
-     */
-    public static final int FLAG_FIRSTITEM = 0x2;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {WIDGET_CHECKBOX, WIDGET_TEXT_VIEW})
-    public @interface WidgetTypes {}
-
-    /**
-     * Use a checkbox widget.
-     * For use with {@link Builder#setWidget(int)}
-     */
-    public static final int WIDGET_CHECKBOX = 0x1;
-
-    /**
-     * Use a TextView widget
-     * For use with {@link Builder#setWidget(int)}
-     */
-    public static final int WIDGET_TEXT_VIEW = 0x2;
-
     private boolean mDetachCalled;
     private boolean mSendResultCalled;
     private final DisplayMetrics mMetrics;
@@ -88,11 +52,11 @@
         for (Item item : results) {
             ItemImpl impl = (ItemImpl) item;
             if (impl.mIcon != null) {
-                impl.mBundle.putParcelable(CarMenuConstants.KEY_LEFTICON, snapshot(impl.mIcon));
+                impl.mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, snapshot(impl.mIcon));
             }
             if (impl.mRightIcon != null) {
                 impl.mBundle.putParcelable(
-                        CarMenuConstants.KEY_RIGHTICON, snapshot(impl.mRightIcon));
+                        MenuItemConstants.KEY_RIGHTICON, snapshot(impl.mRightIcon));
             }
             resultBundle.add(impl.mBundle);
         }
@@ -165,7 +129,8 @@
         /**
          * Gets the integer constant for the widget.
          *
-         * @return Either {@link #WIDGET_CHECKBOX} or -1, if no widget was set.
+         * @return Either {@link MenuItemConstants.WidgetTypes#WIDGET_CHECKBOX} or -1,
+         *         if no widget was set.
          */
         int getWidget();
 
@@ -198,32 +163,32 @@
 
         @Override
         public String getId() {
-            return mBundle.getString(CarMenuConstants.KEY_ID);
+            return mBundle.getString(MenuItemConstants.KEY_ID);
         }
 
         @Override
         public String getTitle() {
-            return mBundle.getString(CarMenuConstants.KEY_TITLE);
+            return mBundle.getString(MenuItemConstants.KEY_TITLE);
         }
 
         @Override
         public String getText() {
-            return mBundle.getString(CarMenuConstants.KEY_TEXT);
+            return mBundle.getString(MenuItemConstants.KEY_TEXT);
         }
 
         @Override
         public int getWidget() {
-            return mBundle.getInt(CarMenuConstants.KEY_WIDGET, -1);
+            return mBundle.getInt(MenuItemConstants.KEY_WIDGET, -1);
         }
 
         @Override
         public boolean getWidgetState() {
-            return mBundle.getBoolean(CarMenuConstants.KEY_WIDGET_STATE);
+            return mBundle.getBoolean(MenuItemConstants.KEY_WIDGET_STATE);
         }
 
         @Override
         public int getFlags() {
-            return mBundle.getInt(CarMenuConstants.KEY_FLAGS);
+            return mBundle.getInt(MenuItemConstants.KEY_FLAGS);
         }
     }
 
@@ -246,7 +211,7 @@
             if (id == null) {
                 throw new IllegalStateException("Cannot pass a null id to the Builder.");
             }
-            mBundle.putString(CarMenuConstants.KEY_ID, id);
+            mBundle.putString(MenuItemConstants.KEY_ID, id);
         }
 
         /**
@@ -256,7 +221,7 @@
          * @return This to chain calls
          */
         public Builder setTitle(String title) {
-            mBundle.putString(CarMenuConstants.KEY_TITLE, title);
+            mBundle.putString(MenuItemConstants.KEY_TITLE, title);
             return this;
         }
 
@@ -267,7 +232,7 @@
          * @return This {@link Builder} to chain calls
          */
         public Builder setText(String text) {
-            mBundle.putString(CarMenuConstants.KEY_TEXT, text);
+            mBundle.putString(MenuItemConstants.KEY_TEXT, text);
             return this;
         }
 
@@ -278,7 +243,7 @@
          * @return This {@link Builder} to chain calls
          */
         public Builder setIcon(Bitmap bitmap) {
-            mBundle.putParcelable(CarMenuConstants.KEY_LEFTICON, bitmap);
+            mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, bitmap);
             return this;
         }
 
@@ -304,7 +269,7 @@
          * @return This {@link Builder} to chain calls
          */
         public Builder setRightIcon(Bitmap bitmap) {
-            mBundle.putParcelable(CarMenuConstants.KEY_RIGHTICON, bitmap);
+            mBundle.putParcelable(MenuItemConstants.KEY_RIGHTICON, bitmap);
             return this;
         }
 
@@ -332,7 +297,7 @@
          * @return This {@link Builder} to chain calls
          */
         public Builder setWidget(int widget) {
-            mBundle.putInt(CarMenuConstants.KEY_WIDGET, widget);
+            mBundle.putInt(MenuItemConstants.KEY_WIDGET, widget);
             return this;
         }
 
@@ -345,7 +310,7 @@
          * @return This {@link Builder} to chain calls
          */
         public Builder setWidgetState(boolean on) {
-            mBundle.putBoolean(CarMenuConstants.KEY_WIDGET_STATE, on);
+            mBundle.putBoolean(MenuItemConstants.KEY_WIDGET_STATE, on);
             return this;
         }
 
@@ -358,36 +323,36 @@
          * @return This {@link Builder} to chain calls
          */
         public Builder setIsEmptyPlaceHolder(boolean isEmptyPlaceHolder) {
-            mBundle.putBoolean(CarMenuConstants.KEY_EMPTY_PLACEHOLDER, isEmptyPlaceHolder);
+            mBundle.putBoolean(MenuItemConstants.KEY_EMPTY_PLACEHOLDER, isEmptyPlaceHolder);
             return this;
         }
 
         /**
-         * If the widget is {@link #WIDGET_TEXT_VIEW}, then this will allow setting
+         * If the widget is {@link MenuItemConstants#WIDGET_TEXT_VIEW}, then this will allow setting
          * the right text.
          *
          * @param text The text to set
          * @return this {@link Builder} to chain calls
          */
         public Builder setRightText(String text) {
-            mBundle.putString(CarMenuConstants.KEY_RIGHTTEXT, text);
+            mBundle.putString(MenuItemConstants.KEY_RIGHTTEXT, text);
             return this;
         }
 
         public Builder setRemoteViews(RemoteViews views) {
-            mBundle.putParcelable(CarMenuConstants.KEY_REMOTEVIEWS, views);
+            mBundle.putParcelable(MenuItemConstants.KEY_REMOTEVIEWS, views);
             return this;
         }
 
         /**
          * Sets additional flags for this item.
-         * {@link #FLAG_BROWSABLE} is the only one that can be currently set
+         * {@link MenuItemConstants#FLAG_BROWSABLE} is the only one that can be currently set
          *
          * @param flags flags to set
          * @return This {@link Builder} to chain calls
          */
-        public Builder setFlags(@MenuItemFlags int flags) {
-            mBundle.putInt(CarMenuConstants.KEY_FLAGS, flags);
+        public Builder setFlags(@MenuItemConstants.MenuItemFlags int flags) {
+            mBundle.putInt(MenuItemConstants.KEY_FLAGS, flags);
             return this;
         }
 
diff --git a/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java b/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java
index b544690..5b92196 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java
@@ -47,9 +47,9 @@
      * Called when the CarMenu wants to get the root
      *
      * @param hints Hints that the Drawer can use to modify behavior. It can be null.
-     * @return The {@link Root} which contains the root id and any hints
+     * @return The {@link RootMenu} which contains the root id and any hints
      */
-    public abstract Root onGetRoot(Bundle hints);
+    public abstract RootMenu onGetRoot(Bundle hints);
 
     /**
      * Called when the CarMenu subscribes to to a certain id
diff --git a/car-support-lib/src/android/support/car/app/menu/CarUiController.java b/car-support-lib/src/android/support/car/app/menu/CarUiController.java
index bcb8909..c3f45b2 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarUiController.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarUiController.java
@@ -16,259 +16,98 @@
 
 package android.support.car.app.menu;
 
-import android.content.Context;
-import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
+import android.support.car.app.CarAppUtil;
 import android.view.View;
-import android.view.inputmethod.EditorInfo;
 import android.widget.EditText;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 
 /**
- * A controller for a {@link android.support.car.app.CarActivity} to access its car UI.
- *
- * @hide
+ * A controller for a {@link android.support.car.app.CarActivity} to manipulate its car UI, and
+ * under the hood it talks to a car ui provider.
  */
-public class CarUiController {
-    private static final String TAG = "CarUiController";
-    // TODO: load the package name and class name from resources
-    private static final String UI_ENTRY_CLASS_NAME = ".CarUiEntry";
-    private static final String CAR_UI_PROVIDER_PKG = "android.support.car.ui.provider";
+public abstract class CarUiController {
+    static final String PROJECTED_UI_CONTROLLER =
+            "com.google.android.car.ProjectedCarUiController";
 
-    private static final String PROJECTED_UI_ENTRY_CLASS_NAME = ".sdk.SdkEntry2";
-    private static final String PROJECTED_UI_PROVIDER_PKG =
-            "com.google.android.projection.gearhead";
-
-    private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
-
-    private final CarDrawerActivity mActivity;
-    // TODO: Add more UI control methods
-    private Method mGetContentViewMethod;
-    private Method mSetScrimColor;
-    private Method mSetTitle;
-    private Method mSetCarMenuBinder;
-    private Method mRestoreMenuButtonDrawable;
-    private Method mSetMenuButtonBitmap;
-    private Method mGetFragmentContainerId;
-    private Method mSetLightMode;
-    private Method mSetAutoLightDarkMode;
-    private Method mSetDarkMode;
-    private Method mSetBackground;
-    private Method mSetBackgroundResource;
-    private Method mOnSaveInstanceState;
-    private Method mOnRestoreInstanceState;
-    private Method mCloseDrawer;
-    private Method mOpenDrawer;
-    private Method mShowMenu;
-    private Method mOnStart;
-    private Method mOnResume;
-    private Method mOnPause;
-    private Method mOnStop;
-    private Method mShowSearchBox;
-    private Method mSetSearchBoxEditListener;
-    private Method mStartInput;
-    private Method mGetText;
-    private Method mStopInput;
-    private Method mStartSearchInput;
-    private Method mSetSearchBoxEndView;
-    private Method mSetSearchBoxColors;
-
-    private Object mCarUiEntryClass;
+    protected final CarDrawerActivity mActivity;
 
     public CarUiController(CarDrawerActivity activity) {
         mActivity = activity;
-        init();
+        validateCarUiPackage();
     }
 
-    public void init() {
-        try{
-            String className;
-            String pkg;
-            if (mActivity.getContext().getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
-                className = UI_ENTRY_CLASS_NAME;
-                pkg = CAR_UI_PROVIDER_PKG;
-            } else {
-                className = PROJECTED_UI_ENTRY_CLASS_NAME;
-                pkg = PROJECTED_UI_PROVIDER_PKG;
-            }
-
-            // STOPSHIP: need to verify the certificate of the CarUiProvider apk.
-            Context carUiContext = mActivity.getContext().createPackageContext(
-                    pkg,
-                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
-            ClassLoader classLoader = carUiContext.getClassLoader();
-            Class<?> loadedClass = classLoader.loadClass(pkg + className);
-            for (Method m : loadedClass.getDeclaredMethods()) {
-                switch(m.getName()) {
-                    case "getContentView":
-                        mGetContentViewMethod = m;
-                        break;
-                    case "setScrimColor":
-                        mSetScrimColor = m;
-                        break;
-                    case "setTitle":
-                        mSetTitle = m;
-                        break;
-                    case "setCarMenuBinder":
-                        mSetCarMenuBinder = m;
-                        break;
-                    case "restoreMenuDrawable":
-                        mRestoreMenuButtonDrawable = m;
-                        break;
-                    case "setMenuButtonBitmap":
-                        mSetMenuButtonBitmap = m;
-                        break;
-                    case "getFragmentContainerId":
-                        mGetFragmentContainerId = m;
-                        break;
-                    case "setLightMode":
-                        mSetLightMode = m;
-                        break;
-                    case "setDarkMode":
-                        mSetDarkMode = m;
-                        break;
-                    case "setAutoLightDarkMode":
-                        mSetAutoLightDarkMode = m;
-                        break;
-                    case "setBackground":
-                        mSetBackground = m;
-                        break;
-                    case "setBackgroundResource":
-                        mSetBackgroundResource = m;
-                        break;
-                    case "onSaveInstanceState":
-                        mOnSaveInstanceState = m;
-                        break;
-                    case "onRestoreInstanceState":
-                        mOnRestoreInstanceState = m;
-                        break;
-                    case "closeDrawer":
-                        mCloseDrawer = m;
-                        break;
-                    case "openDrawer":
-                        mOpenDrawer = m;
-                        break;
-                    case "showMenu":
-                        mShowMenu = m;
-                        break;
-                    case "onStart":
-                        mOnStart = m;
-                        break;
-                    case "onResume":
-                        mOnResume = m;
-                        break;
-                    case "onPause":
-                        mOnPause = m;
-                        break;
-                    case "onStop":
-                        mOnStop = m;
-                        break;
-                    case "showSearchBox":
-                        mShowSearchBox = m;
-                        break;
-                    case "setSearchBoxEditListener":
-                        mSetSearchBoxEditListener = m;
-                        break;
-                    case "startInput":
-                        mStartInput = m;
-                        break;
-                    case "getText":
-                        mGetText = m;
-                        break;
-                    case "stopInput":
-                        mStopInput = m;
-                        break;
-                    case "startSearchInput":
-                        mStartSearchInput = m;
-                        break;
-                    case "setSearchBoxEndView":
-                        mSetSearchBoxEndView = m;
-                        break;
-                    case "setSearchBoxColors":
-                        mSetSearchBoxColors = m;
-                        break;
-                }
-            }
-            mCarUiEntryClass = loadedClass.getConstructors()[0].newInstance(
-                    carUiContext, mActivity.getContext());
-        } catch (PackageManager.NameNotFoundException | ClassNotFoundException
-                | InvocationTargetException | InstantiationException
-                | IllegalAccessException e) {
-            Log.e(TAG, "Unable to load Car ui entry class.", e);
+    public static CarUiController createCarUiController(CarDrawerActivity activity) {
+        if (CarAppUtil.isEmbeddedCar(activity.getContext())) {
+            return new EmbeddedCarUiController(activity);
+        } else {
+            return getProjectedCarUiController(PROJECTED_UI_CONTROLLER, activity);
         }
     }
 
-    public int getFragmentContainerId() {
-        return (int) invoke(mGetFragmentContainerId);
-    }
-
-    public void setTitle(CharSequence title) {
-        invoke(mSetTitle, title);
-    }
-
-    public void setScrimColor(int color) {
-        invoke(mSetScrimColor, color);
-    }
-
-    public View getContentView() {
-        return (View) invoke(mGetContentViewMethod);
-    }
-
-    private Object invoke(Method m, Object... params) {
+    private static CarUiController getProjectedCarUiController(String className,
+            CarDrawerActivity activity) {
+        Class uiControllerClass = null;
         try {
-            return m.invoke(mCarUiEntryClass, params);
-        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-            Log.e(TAG, "Error invoking: " + m.getName(), e);
+            uiControllerClass = Class.forName(className);
+        } catch (ClassNotFoundException e) {
+            throw new IllegalArgumentException("Cannot find ProjectedCarUiController:" +
+                    className, e);
         }
-        return null;
+        Constructor<?> ctor;
+        try {
+            ctor = uiControllerClass.getDeclaredConstructor(CarDrawerActivity.class);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException("Cannot construct ProjectedCarUiController," +
+                    " no constructor: " + className, e);
+        }
+        try {
+            return (CarUiController) ctor.newInstance(activity);
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+                | InvocationTargetException e) {
+            throw new IllegalArgumentException(
+                    "Cannot construct ProjectedCarUiController, constructor failed for "
+                            + uiControllerClass.getName(), e);
+        }
     }
 
-    public void registerCarMenuCallbacks(IBinder callbacks) {
-        invoke(mSetCarMenuBinder, callbacks);
-    }
+    public abstract void validateCarUiPackage();
 
-    public void restoreMenuButtonDrawable() {
-        invoke(mRestoreMenuButtonDrawable);
-    }
+    public abstract int getFragmentContainerId();
 
-    public void setMenuButtonBitmap(Bitmap bitmap) {
-        invoke(mSetMenuButtonBitmap, bitmap);
-    }
+    public abstract void setTitle(CharSequence title);
 
-    /**
-     * Set the System UI to be light.
-     */
-    public void setLightMode() {
-        invoke(mSetLightMode);
-    }
+    public abstract void setScrimColor(int color);
+
+    public abstract View getContentView();
+
+    public abstract void registerCarMenuCallbacks(CarMenuCallbacks callbacks);
+
+    public abstract void restoreMenuButtonDrawable();
+
+    public abstract void setMenuButtonBitmap(Bitmap bitmap);
+
+    public abstract void setLightMode();
 
     /**
      * Set the System UI to be dark.
      */
-    public void setDarkMode() {
-        invoke(mSetDarkMode);
-    }
+    public abstract void setDarkMode();
 
     /**
      * Set the System UI to be dark during day mode and light during night mode.
      */
-    public void setAutoLightDarkMode() {
-        invoke(mSetAutoLightDarkMode);
-    }
+    public abstract void setAutoLightDarkMode();
 
     /**
      * Sets the application background to the given {@link android.graphics.Bitmap}.
      *
      * @param bitmap to use as background.
      */
-    public void setBackground(Bitmap bitmap) {
-        invoke(mSetBackground, bitmap);
-    }
+    public abstract void setBackground(Bitmap bitmap);
 
     /**
      * Sets the background to a given resource.
@@ -276,77 +115,45 @@
      *
      * @param resId The identifier of the resource.
      */
-    public void setBackgroundResource(int resId) {
-        invoke(mSetBackgroundResource, resId);
-    }
+    public abstract void setBackgroundResource(int resId);
 
-    public void onRestoreInstanceState(Bundle savedState) {
-        invoke(mOnRestoreInstanceState, savedState);
-    }
+    public abstract void onRestoreInstanceState(Bundle savedState);
 
-    public void onSaveInstanceState(Bundle outState) {
-        invoke(mOnSaveInstanceState, outState);
-    }
+    public abstract void onSaveInstanceState(Bundle outState);
 
-    public void closeDrawer() {
-        invoke(mCloseDrawer);
-    }
+    public abstract void closeDrawer();
 
-    public void openDrawer() {
-        invoke(mOpenDrawer);
-    }
+    public abstract void openDrawer();
 
-    public void showMenu(String id, String title) {
-        invoke(mShowMenu, id, title);
-    }
+    public abstract void showMenu(String id, String title);
 
-    public void onStart() {
-        invoke(mOnStart);
-    }
+    public abstract void onStart();
 
-    public void onResume() {
-        invoke(mOnResume);
-    }
+    public abstract void onResume();
 
-    public void onPause() {
-        invoke(mOnPause);
-    }
+    public abstract void onPause();
 
-    public void onStop() {
-        invoke(mOnStop);
-    }
+    public abstract void onStop();
 
-    public void showSearchBox(View.OnClickListener listener) {
-        invoke(mShowSearchBox, listener);
-    }
+    public abstract void showSearchBox(View.OnClickListener listener);
 
-    public void setSearchBoxColors(int backgroundColor, int googleLogoColor, int textColor,
-                                   int hintTextColor) {
-        invoke(mSetSearchBoxColors, backgroundColor, googleLogoColor, textColor, hintTextColor);
-    }
+    public abstract void setSearchBoxColors(int backgroundColor, int googleLogoColor, int textColor,
+                                            int hintTextColor);
 
-    public void setSearchBoxEditListener(IBinder listener) {
-        invoke(mSetSearchBoxEditListener, listener);
-    }
+    public abstract void setSearchBoxEditListener(SearchBoxEditListener listener);
 
-    public Object startInput(
-            EditorInfo outAttrs, String hint, View.OnClickListener searchBoxClickListener) {
-        return invoke(mStartInput, outAttrs, hint, searchBoxClickListener);
-    }
+    public abstract EditText startInput(
+            String hint, View.OnClickListener searchBoxClickListener);
 
-    public CharSequence getText() {
-        return (CharSequence) invoke(mGetText);
-    }
+    public abstract CharSequence getText();
 
-    public void stopInput() {
-        invoke(mStopInput);
-    }
+    public abstract void stopInput();
 
-    public EditText startSearchInput(String hint, View.OnClickListener listener) {
-        return (EditText) invoke(mStartSearchInput, hint, listener);
-    }
+    public abstract void setSearchBoxEndView(View view);
 
-    public void setSearchBoxEndView(View view) {
-        invoke(mSetSearchBoxEndView, view);
-    }
+    public abstract void onChildrenChanged(String parentId);
+
+    public abstract void onChildChanged(String parentId, Bundle item);
+
+    public abstract void showToast(String msg, int duration);
 }
diff --git a/car-support-lib/src/android/support/car/app/menu/CarUiEntry.java b/car-support-lib/src/android/support/car/app/menu/CarUiEntry.java
deleted file mode 100644
index b042793..0000000
--- a/car-support-lib/src/android/support/car/app/menu/CarUiEntry.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.support.car.app.menu;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.view.View;
-
-/**
- * A base class for a car ui entry which is used for loading and manipulating common car
- * app decor window (CarUi).
- *
- * A CarUi provider provides essential ui elements that a car app may want to use. The CarUi is
- * loaded by apps at runtime, similar to a shared library, but via reflection through a class that
- * extends {@link android.support.car.app.menu.CarUiEntry} from a separate apk
- * called CarUiProvider. Depending on the different platforms, the CarUiProvider may
- * be different and can be customized by different car makers. However, it is required that a
- * set of basic ui elements and functionalities exist in the CarUiProvider. This class defines
- * the set of must have functions in a CarUiProvider.
- */
-public abstract class CarUiEntry {
-    protected Context mAppContext;
-    protected Context mUiLibContext;
-
-    public CarUiEntry(Context uiLibContext, Context appContext) {
-        mAppContext = appContext;
-        mUiLibContext = uiLibContext.createConfigurationContext(
-                appContext.getResources().getConfiguration());
-    }
-
-    abstract public View getContentView();
-
-    abstract public void setCarMenuBinder(IBinder binder) throws RemoteException;
-
-    abstract public int getFragmentContainerId();
-
-    abstract public void setBackground(Bitmap bitmap);
-
-    abstract public void setBackgroundResource(int resId);
-
-    abstract public void hideMenuButton();
-
-    abstract public void restoreMenuDrawable();
-
-    abstract public void setMenuProgress(float progress);
-
-    abstract public void setScrimColor(int color);
-
-    abstract public void setTitle(CharSequence title);
-
-    abstract public void setTitleText(CharSequence title);
-
-    abstract public void closeDrawer();
-
-    abstract public void openDrawer();
-
-    abstract public void showMenu(String id, String title);
-
-    abstract public void setMenuButtonColor(int color);
-
-    abstract public void showTitle();
-
-    abstract public void hideTitle();
-
-    abstract public void setLightMode();
-
-    abstract public void setDarkMode();
-
-    abstract public void setAutoLightDarkMode();
-
-    abstract public void onRestoreInstanceState(Bundle savedInstanceState);
-
-    abstract public void onSaveInstanceState(Bundle outState);
-
-    abstract public void onStart();
-
-    abstract public void onResume();
-
-    abstract public void onPause();
-
-    abstract public void onStop();
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/Constants.java b/car-support-lib/src/android/support/car/app/menu/Constants.java
deleted file mode 100644
index f37cace..0000000
--- a/car-support-lib/src/android/support/car/app/menu/Constants.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.car.app.menu;
-
-/**
- * Constants shared by SDK and GearHead.
- *
- * @hide
- */
-public class Constants {
-    public class CarMenuConstants {
-        public static final String KEY_TITLE = "title";
-        public static final String KEY_TEXT = "text";
-        public static final String KEY_LEFTICON = "leftIcon";
-        public static final String KEY_RIGHTICON = "rightIcon";
-        public static final String KEY_RIGHTTEXT = "rightText";
-        public static final String KEY_WIDGET = "widget";
-        public static final String KEY_WIDGET_STATE = "widget_state";
-        public static final String KEY_EMPTY_PLACEHOLDER = "empty_placeholder";
-        public static final String KEY_FLAGS = "flags";
-        public static final String KEY_ID = "id";
-        public static final String KEY_REMOTEVIEWS = "remoteViews";
-    }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java b/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java
new file mode 100644
index 0000000..9e16204
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.car.app.menu;
+
+import android.car.app.menu.CarUiEntry;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.car.app.menu.compat.EmbeddedCarMenuCallbacksCompat;
+import android.support.car.app.menu.compat.EmbeddedSearchBoxEditListenerCompat;
+import android.view.View;
+import android.widget.EditText;
+
+import java.lang.reflect.InvocationTargetException;
+/**
+ * A {@link android.support.car.app.menu.CarUiController} that talks to embedded car ui provider.
+ * @hide
+ */
+public class EmbeddedCarUiController extends CarUiController {
+
+    private static final String TAG = "EmbeddedCarUiController";
+    // TODO: load the package name and class name from resources
+    private static final String UI_ENTRY_CLASS_NAME = ".CarUiEntry";
+    private static final String CAR_UI_PROVIDER_PKG = "android.car.ui.provider";
+    private static final String CAR_SERVICE_PKG = "com.android.car";
+
+    private CarUiEntry mCarUiEntry;
+    private EmbeddedCarMenuCallbacksCompat mCallback;
+
+    public EmbeddedCarUiController(CarDrawerActivity activity) {
+        super(activity);
+        try {
+            Context carUiContext = mActivity.getContext().createPackageContext(
+                    CAR_UI_PROVIDER_PKG,
+                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+
+            ClassLoader classLoader = carUiContext.getClassLoader();
+            Class<?> loadedClass = classLoader.loadClass(CAR_UI_PROVIDER_PKG + UI_ENTRY_CLASS_NAME);
+            mCarUiEntry = (CarUiEntry) loadedClass.getConstructor(Context.class, Context.class)
+                    .newInstance(carUiContext, mActivity.getContext());
+        } catch (PackageManager.NameNotFoundException | ClassNotFoundException e) {
+            throw new RuntimeException("Cannot find CarUiEntry from " + CAR_UI_PROVIDER_PKG + "/"
+                    + UI_ENTRY_CLASS_NAME, e);
+        } catch (IllegalAccessException | InvocationTargetException | InstantiationException
+                | NoSuchMethodException  e) {
+            throw new RuntimeException("Cannot cast CarUiEntry.", e);
+        }
+    }
+
+    @Override
+    public void validateCarUiPackage() {
+        try {
+            PackageManager packageManager = mActivity.getContext().getPackageManager();
+            int flag = packageManager.getApplicationInfo(CAR_UI_PROVIDER_PKG, 0).flags;
+            if ((flag & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && (flag & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+                throw new SecurityException("CarUiProvider is not a system app!");
+            }
+
+            int signatureMatchResult =
+                    packageManager.checkSignatures(CAR_SERVICE_PKG, CAR_UI_PROVIDER_PKG);
+            if (signatureMatchResult != PackageManager.SIGNATURE_MATCH) {
+                throw new SecurityException("The signature of car ui provider does not match the" +
+                        "signature of the car service!");
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException("Cannot find CarUiProvider" + CAR_UI_PROVIDER_PKG, e);
+        }
+    }
+
+    @Override
+    public int getFragmentContainerId() {
+        return mCarUiEntry.getFragmentContainerId();
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mCarUiEntry.setTitle(title);
+    }
+
+    @Override
+    public void setScrimColor(int color) {
+        mCarUiEntry.setScrimColor(color);
+    }
+
+    @Override
+    public View getContentView() {
+        return mCarUiEntry.getContentView();
+    }
+
+    @Override
+    public void registerCarMenuCallbacks(final CarMenuCallbacks callbacks) {
+        mCallback = new EmbeddedCarMenuCallbacksCompat(mActivity, callbacks);
+        mCarUiEntry.setCarMenuCallbacks(mCallback);
+    }
+
+    @Override
+    public void restoreMenuButtonDrawable() {
+        mCarUiEntry.restoreMenuDrawable();
+    }
+
+    @Override
+    public void setMenuButtonBitmap(Bitmap bitmap) {
+        mCarUiEntry.setMenuButtonBitmap(bitmap);
+    }
+
+    @Override
+    public void setLightMode() {
+        mCarUiEntry.setLightMode();
+    }
+
+    @Override
+    public void setDarkMode() {
+        mCarUiEntry.setDarkMode();
+    }
+
+    @Override
+    public void setAutoLightDarkMode() {
+        mCarUiEntry.setAutoLightDarkMode();
+    }
+
+    @Override
+    public void setBackground(Bitmap bitmap) {
+        mCarUiEntry.setBackground(bitmap);
+    }
+
+    @Override
+    public void setBackgroundResource(int resId) {
+        mCarUiEntry.setBackgroundResource(resId);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Bundle savedState) {
+        mCarUiEntry.onRestoreInstanceState(savedState);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        mCarUiEntry.onSaveInstanceState(outState);
+    }
+
+    @Override
+    public void closeDrawer() {
+        mCarUiEntry.closeDrawer();
+    }
+
+    @Override
+    public void openDrawer() {
+        mCarUiEntry.openDrawer();
+    }
+
+    @Override
+    public void showMenu(String id, String title) {
+        mCarUiEntry.showMenu(id, title);
+    }
+
+    @Override
+    public void onStart() {
+        mCarUiEntry.onStart();
+    }
+
+    @Override
+    public void onResume() {
+        mCarUiEntry.onResume();
+    }
+
+    @Override
+    public void onPause() {
+        mCarUiEntry.onPause();
+    }
+
+    @Override
+    public void onStop() {
+        mCarUiEntry.onStop();
+    }
+
+    @Override
+    public void showSearchBox(View.OnClickListener listener) {
+        mCarUiEntry.showSearchBox(listener);
+    }
+
+    @Override
+    public void setSearchBoxColors(int backgroundColor, int searchLogocolor, int textColor, int hintTextColor) {
+        mCarUiEntry.setSearchBoxColors(backgroundColor, searchLogocolor, textColor, hintTextColor);
+    }
+
+    @Override
+    public void setSearchBoxEditListener(SearchBoxEditListener listener) {
+        mCarUiEntry.setSearchBoxEditListener(new EmbeddedSearchBoxEditListenerCompat(listener));
+    }
+
+    @Override
+    public CharSequence getText() {
+        return mCarUiEntry.getSearchBoxText();
+    }
+
+    @Override
+    public void stopInput() {
+        mCarUiEntry.stopInput();
+    }
+
+    @Override
+    public EditText startInput(String hint, View.OnClickListener listener) {
+        return mCarUiEntry.startInput(hint, listener);
+    }
+
+    @Override
+    public void setSearchBoxEndView(View view) {
+        mCarUiEntry.setSearchBoxEndView(view);
+    }
+
+    @Override
+    public void onChildChanged(String parentId, Bundle item) {
+        mCallback.onChildChanged(parentId, item);
+    }
+
+    @Override
+    public void onChildrenChanged(String parentId) {
+        mCallback.onChildrenChanged(parentId);
+    }
+
+    @Override
+    public void showToast(String msg, int duration) {
+        mCarUiEntry.showToast(msg, duration);
+    }
+}
diff --git a/car-support-lib/src/android/support/car/app/menu/ICarMenuCallbacks.aidl b/car-support-lib/src/android/support/car/app/menu/ICarMenuCallbacks.aidl
deleted file mode 100644
index a3da39c..0000000
--- a/car-support-lib/src/android/support/car/app/menu/ICarMenuCallbacks.aidl
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.car.app.menu;
-
-import android.os.Bundle;
-import android.support.car.app.menu.ISubscriptionCallbacks;
-
-/** @hide */
-interface ICarMenuCallbacks {
-    int getVersion() = 0;
-    Bundle getRoot() = 1;
-    void subscribe(String parentId, ISubscriptionCallbacks callbacks) = 2;
-    void unsubscribe(String parentId, ISubscriptionCallbacks callbacks) = 3;
-    void onCarMenuOpened() = 4;
-    void onCarMenuClosed() = 5;
-    void onItemClicked(String id) = 6;
-    boolean onItemLongClicked(String id) = 7;
-    void onStateChanged(int newState) = 8;
-    boolean onMenuClicked() = 9;
-    void onCarMenuOpening() = 10;
-    void onCarMenuClosing() = 11;
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/ISubscriptionCallbacks.aidl b/car-support-lib/src/android/support/car/app/menu/ISubscriptionCallbacks.aidl
deleted file mode 100644
index 772cc44..0000000
--- a/car-support-lib/src/android/support/car/app/menu/ISubscriptionCallbacks.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.car.app.menu;
-
-import android.os.Bundle;
-
-import java.util.List;
-
-/** @hide */
-interface ISubscriptionCallbacks {
-        int getVersion() = 0;
-        void onChildrenLoaded(String parentId, in List<Bundle> items) = 1;
-        void onError(String id) = 2;
-        void onChildChanged(String parentId, in Bundle bundle) = 3;
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/Root.java b/car-support-lib/src/android/support/car/app/menu/RootMenu.java
similarity index 79%
rename from car-support-lib/src/android/support/car/app/menu/Root.java
rename to car-support-lib/src/android/support/car/app/menu/RootMenu.java
index fbdf0e7..041f083 100644
--- a/car-support-lib/src/android/support/car/app/menu/Root.java
+++ b/car-support-lib/src/android/support/car/app/menu/RootMenu.java
@@ -17,13 +17,14 @@
 package android.support.car.app.menu;
 
 import android.os.Bundle;
+import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
 
 /**
- * Stores the Root id for the menu. The Root is the main menu.
+ * Stores the root id for the menu. The RootMenu is the main menu.
  * Also allows passing hints through bundles. Hints allow the
  * the recipient to alter its behavior based on the hints.
  */
-public class Root {
+public class RootMenu {
     private final Bundle mBundle;
 
     /**
@@ -31,7 +32,7 @@
      *
      * @param id Root id
      */
-    public Root(String id) {
+    public RootMenu(String id) {
         this(id, null);
     }
 
@@ -41,9 +42,9 @@
      * @param id Root id
      * @param extras Hints to pass along
      */
-    public Root(String id, Bundle extras) {
+    public RootMenu(String id, Bundle extras) {
         mBundle = new Bundle();
-        mBundle.putString(Constants.CarMenuConstants.KEY_ID, id);
+        mBundle.putString(MenuItemConstants.KEY_ID, id);
         if (extras != null) {
             mBundle.putAll(extras);
         }
@@ -55,7 +56,7 @@
      * @return The root id
      */
     public String getId() {
-        return mBundle.getString(Constants.CarMenuConstants.KEY_ID);
+        return mBundle.getString(MenuItemConstants.KEY_ID);
     }
 
     /**
diff --git a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl b/car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
similarity index 79%
copy from car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
copy to car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
index b8b6c55..154249d 100644
--- a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
+++ b/car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
@@ -15,14 +15,17 @@
  */
 package android.support.car.app.menu;
 
-interface ISearchBoxEditListener {
+/**
+ * A listener that listens the user input to the search box.
+ */
+public abstract class SearchBoxEditListener {
     /**
      * The user hit enter on the keyboard.
      */
-    void onSearch(in String text) = 0;
+    public abstract void onSearch(String text);
 
     /**
      * The user changed the text in the search box with the keyboard.
      */
-    void onEdit(in String text) = 1;
-}
+    public abstract void onEdit(String text);
+}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/app/menu/compat/CarMenuConstantsComapt.java b/car-support-lib/src/android/support/car/app/menu/compat/CarMenuConstantsComapt.java
new file mode 100644
index 0000000..2a91928
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/compat/CarMenuConstantsComapt.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.car.app.menu.compat;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains keys to the metadata of car menu, such as id, title, icon, etc.
+ */
+public class CarMenuConstantsComapt {
+    public static class MenuItemConstants {
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true,
+                value = {FLAG_BROWSABLE, FLAG_FIRSTITEM})
+        public @interface MenuItemFlags {}
+
+        /**
+         * Flag: Indicates that the item has children of its own
+         */
+        public static final int FLAG_BROWSABLE = 0x1;
+
+        /**
+         * Flag: Indicates that the menu should scroll to this item
+         */
+        public static final int FLAG_FIRSTITEM = 0x2;
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(value = {WIDGET_CHECKBOX, WIDGET_TEXT_VIEW})
+        public @interface WidgetTypes {}
+
+        /**
+         * Use a checkbox widget.
+         */
+        public static final int WIDGET_CHECKBOX = 0x1;
+
+        /**
+         * Use a TextView widget
+         */
+        public static final int WIDGET_TEXT_VIEW = 0x2;
+
+        /**
+         * Key for the car menu title.
+         */
+        public static final String KEY_TITLE = "android.car.app.menu.title";
+
+        /**
+         * Key for the item title.
+         */
+        public static final String KEY_TEXT = "android.car.app.menu.text";
+
+        /**
+         * Key for the left icon.
+         */
+        public static final String KEY_LEFTICON = "android.car.app.menu.leftIcon";
+
+        /**
+         * Key for the right icon.
+         */
+        public static final String KEY_RIGHTICON = "android.car.app.menu.rightIcon";
+
+        /**
+         * Key for the text to be shown to the right of the item.
+         */
+        public static final String KEY_RIGHTTEXT = "android.car.app.menu.rightText";
+
+        /**
+         * Key for the widget type.
+         */
+        public static final String KEY_WIDGET = "android.car.app.menu.widget";
+
+        /**
+         * Key for the widget state.
+         */
+        public static final String KEY_WIDGET_STATE = "android.car.app.menu.widget_state";
+
+        /**
+         * Key for the value of whether the item is a place holder.
+         */
+        public static final String KEY_EMPTY_PLACEHOLDER = "android.car.app.menu.empty_placeholder";
+
+        /**
+         * Key for the flags.
+         */
+        public static final String KEY_FLAGS = "android.car.app.menu.flags";
+
+        /**
+         * Key for the menu item id.
+         */
+        public static final String KEY_ID = "android.car.app.menu.id";
+
+        /**
+         * Key for the remote views.
+         */
+        public static final String KEY_REMOTEVIEWS = "android.car.app.menu.remoteViews";
+    }
+}
diff --git a/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedCarMenuCallbacksCompat.java b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedCarMenuCallbacksCompat.java
new file mode 100644
index 0000000..bf51758
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedCarMenuCallbacksCompat.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.car.app.menu.compat;
+
+import android.car.app.menu.RootMenu;
+import android.car.app.menu.SubscriptionCallbacks;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.car.app.menu.CarDrawerActivity;
+import android.support.car.app.menu.CarMenu;
+import android.support.car.app.menu.CarMenuCallbacks;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.concurrent.GuardedBy;
+
+public class EmbeddedCarMenuCallbacksCompat extends android.car.app.menu.CarMenuCallbacks {
+
+    private final CarMenuCallbacks mCallbacks;
+    private final CarDrawerActivity mActivity;
+
+    // Map of subscribed ids to their respective callbacks.
+    @GuardedBy("this")
+    private final Map<String, List<SubscriptionCallbacks>> mSubscriptionMap =
+            new HashMap<String, List<SubscriptionCallbacks>>();
+
+    private final Handler mHandler = new Handler();
+
+    public EmbeddedCarMenuCallbacksCompat(CarDrawerActivity activity,
+                                          CarMenuCallbacks callbacks) {
+        mActivity = activity;
+        mCallbacks = callbacks;
+    }
+
+    @Override
+    public RootMenu getRootMenu(Bundle hint) {
+        android.support.car.app.menu.RootMenu rootMenu = mCallbacks.onGetRoot(hint);
+        return new RootMenu(rootMenu.getId(), rootMenu.getBundle());
+    }
+
+    @Override
+    public void subscribe(String parentId, SubscriptionCallbacks callbacks) {
+        synchronized (this) {
+            if (!mSubscriptionMap.containsKey(parentId)) {
+                mSubscriptionMap.put(parentId, new ArrayList<SubscriptionCallbacks>());
+            }
+            mSubscriptionMap.get(parentId).add(callbacks);
+            loadResultsForClient(parentId, callbacks);
+        }
+    }
+
+    @Override
+    public void unsubscribe(String parentId, SubscriptionCallbacks callbacks) {
+        synchronized (this) {
+            mSubscriptionMap.get(parentId).remove(callbacks);
+        }
+    }
+
+    @Override
+    public void onCarMenuOpened() {
+        mActivity.setDrawerShowing(true);
+        mCallbacks.onCarMenuOpened();
+    }
+
+    @Override
+    public void onCarMenuClosed() {
+        mActivity.setDrawerShowing(false);
+        mActivity.restoreSearchBox();
+    }
+
+    @Override
+    public void onItemClicked(String id) {
+        mCallbacks.onItemClicked(id);
+        mActivity.stopInput();
+    }
+
+    @Override
+    public boolean onItemLongClicked(String id) {
+        return mCallbacks.onItemLongClicked(id);
+    }
+
+    @Override
+    public boolean onMenuClicked() {
+
+        return mActivity.onMenuClicked();
+    }
+
+    @Override
+    public void onCarMenuOpening() {
+        mActivity.stopInput();
+    }
+
+    @Override
+    public void onCarMenuClosing() {
+        mActivity.restoreSearchBox();
+    }
+
+    public void onChildrenChanged(final String parentId) {
+        synchronized (this) {
+            if (mSubscriptionMap.containsKey(parentId)) {
+                final List<SubscriptionCallbacks> callbacks = new ArrayList<>();
+                callbacks.addAll(mSubscriptionMap.get(parentId));
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        loadResultsForAllClients(parentId, callbacks);
+                    }
+                });
+            }
+        }
+    }
+
+    public void onChildChanged(final String parentId, final Bundle item) {
+        synchronized (this) {
+            if (mSubscriptionMap.containsKey(parentId)) {
+                final List<SubscriptionCallbacks> callbacks = new ArrayList<>();
+                callbacks.addAll(mSubscriptionMap.get(parentId));
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        for (SubscriptionCallbacks callback : callbacks) {
+                            callback.onChildChanged(parentId, item);
+                        }
+                    }
+                });
+
+            }
+        }
+    }
+
+    private void loadResultsForAllClients(final String parentId,
+            @NonNull final List<SubscriptionCallbacks> callbacks) {
+        final CarMenu result = new CarMenu(mActivity.getResources().getDisplayMetrics()) {
+            @Override
+            protected void onResultReady(List<Bundle> list) {
+                for (SubscriptionCallbacks callback : callbacks) {
+                    callback.onChildrenLoaded(parentId, list);
+                }
+            }
+        };
+
+        mCallbacks.onLoadChildren(parentId, result);
+        if (!result.isDone()) {
+            throw new IllegalStateException("You must either call sendResult() or detach() " +
+                    "before returning!");
+        }
+    }
+
+    private void loadResultsForClient(final String parentId,
+            final SubscriptionCallbacks callbacks) {
+        final CarMenu result = new CarMenu(mActivity.getResources().getDisplayMetrics()) {
+            @Override
+            protected void onResultReady(List<Bundle> list) {
+                callbacks.onChildrenLoaded(parentId, list);
+            }
+        };
+
+        mCallbacks.onLoadChildren(parentId, result);
+        if (!result.isDone()) {
+            throw new IllegalStateException("You must either call sendResult() or detach() " +
+                    "before returning!");
+        }
+    }
+}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedSearchBoxEditListenerCompat.java b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedSearchBoxEditListenerCompat.java
new file mode 100644
index 0000000..6508c2f
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedSearchBoxEditListenerCompat.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.car.app.menu.compat;
+
+import android.support.car.app.menu.SearchBoxEditListener;
+
+public class EmbeddedSearchBoxEditListenerCompat extends
+        android.car.app.menu.SearchBoxEditListener {
+    private final SearchBoxEditListener mListener;
+    public EmbeddedSearchBoxEditListenerCompat(SearchBoxEditListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public void onSearch(String text) {
+        mListener.onSearch(text);
+    }
+
+    @Override
+    public void onEdit(String text) {
+        mListener.onEdit(text);
+    }
+}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
index e428a2f..92ca326 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
@@ -47,12 +47,12 @@
     public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0;
     /**
      * Index in {@link #floatValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
-     * sensor. This value is fuel level in coverable distance.
+     * sensor. This value is fuel level in coverable distance. The unit is Km.
      */
     public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1;
     /**
-     * Index in {@link #byteValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
-     * sensor. This value is set to 0 if fuel low level warning is on.
+     * Index in {@link #intValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
+     * sensor. This value is set to 1 if fuel low level warning is on.
      */
     public static final int INDEX_FUEL_LOW_WARNING = 0;
 
@@ -373,10 +373,10 @@
 
     public static class FuelLevelData {
         public long timeStampNs;
-        /** If unsupported by the car, this value is 0. */
+        /** Fuel level in %. If unsupported by the car, this value is -1. */
         public int level;
-        /** If unsupported by the car, this value is 0. */
-        public int range;
+        /** Fuel as possible range in Km. If unsupported by the car, this value is -1. */
+        public float range;
         /** If unsupported by the car, this value is false. */
         public boolean lowFuelWarning;
     }
@@ -395,8 +395,21 @@
             data = new FuelLevelData();
         }
         data.timeStampNs = timeStampNs;
-        data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
-        data.range = (int) floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+        if (floatValues == null) {
+            data.level = -1;
+            data.range = -1;
+        } else {
+            if (floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE] < 0) {
+                data.level = -1;
+            } else {
+                data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
+            }
+            if (floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE] < 0) {
+                data.range = -1;
+            } else {
+                data.range = floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+            }
+        }
         data.lowFuelWarning = intValues[0] == 1;
         return data;
     }
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
index 7949a46..ebffaf8 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
@@ -87,49 +87,36 @@
      * {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
      */
     public static final int SENSOR_TYPE_GEAR              = 7;
-    public static final int SENSOR_TYPE_RESERVED8         = 8;
     /**
      * Day/night sensor. Sensor data is intValues[0].
      */
-    public static final int SENSOR_TYPE_NIGHT             = 9;
+    public static final int SENSOR_TYPE_NIGHT             = 8;
     /**
      * Sensor type for location. Sensor data passed in floatValues.
      */
-    public static final int SENSOR_TYPE_LOCATION          = 10;
+    public static final int SENSOR_TYPE_LOCATION          = 9;
     /**
      * Represents the current driving status of car. Different user interaction should be used
      * depending on the current driving status. Driving status is intValues[0].
      */
-    public static final int SENSOR_TYPE_DRIVING_STATUS    = 11;
+    public static final int SENSOR_TYPE_DRIVING_STATUS    = 10;
     /**
      * Environment like temperature and pressure.
      */
-    public static final int SENSOR_TYPE_ENVIRONMENT       = 12;
+    public static final int SENSOR_TYPE_ENVIRONMENT       = 11;
 
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED13        = 13;
+    public static final int SENSOR_TYPE_ACCELEROMETER     = 12;
     /** @hide */
-    public static final int SENSOR_TYPE_ACCELEROMETER     = 14;
+    public static final int SENSOR_TYPE_GPS_SATELLITE     = 13;
     /** @hide */
-    public static final int SENSOR_TYPE_RESERVED15        = 15;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED16        = 16;
-    /** @hide */
-    public static final int SENSOR_TYPE_GPS_SATELLITE     = 17;
-    /** @hide */
-    public static final int SENSOR_TYPE_GYROSCOPE         = 18;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED19        = 19;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED20        = 20;
-    /** @hide */
-    public static final int SENSOR_TYPE_RESERVED21        = 21;
+    public static final int SENSOR_TYPE_GYROSCOPE         = 14;
 
     /**
      * Sensor type bigger than this is invalid. Always update this after adding a new sensor.
      * @hide
      */
-    private static final int SENSOR_TYPE_MAX              = SENSOR_TYPE_RESERVED21;
+    private static final int SENSOR_TYPE_MAX              = SENSOR_TYPE_GYROSCOPE;
 
     /**
      * Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
diff --git a/car-support-lib/src/android/support/car/media/CarAudioManager.java b/car-support-lib/src/android/support/car/media/CarAudioManager.java
index 3b1ba21..a8ab19b 100644
--- a/car-support-lib/src/android/support/car/media/CarAudioManager.java
+++ b/car-support-lib/src/android/support/car/media/CarAudioManager.java
@@ -16,6 +16,7 @@
 package android.support.car.media;
 
 import android.media.AudioAttributes;
+import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.os.RemoteException;
 import android.support.annotation.IntDef;
 import android.support.car.CarManagerBase;
@@ -86,4 +87,24 @@
      * @return
      */
     public abstract AudioAttributes getAudioAttributesForCarUsage(@CarAudioUsage int carUsage);
+
+    /**
+     * Request audio focus.
+     * Send a request to obtain the audio focus.
+     * @param l
+     * @param requestAttributes
+     * @param durationHint
+     * @param flags
+     */
+    public abstract int requestAudioFocus(OnAudioFocusChangeListener l,
+                                          AudioAttributes requestAttributes,
+                                          int durationHint,
+                                          int flags) throws IllegalArgumentException;
+    /**
+     * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
+     * @param l
+     * @param aa
+     * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
+     */
+    public abstract int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa);
 }
diff --git a/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java b/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java
index 4fdac71..e254a93 100644
--- a/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java
+++ b/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java
@@ -16,6 +16,7 @@
 package android.support.car.media;
 
 import android.media.AudioAttributes;
+import android.media.AudioManager.OnAudioFocusChangeListener;
 
 /**
  * @hide
@@ -34,6 +35,19 @@
     }
 
     @Override
+    public int requestAudioFocus(OnAudioFocusChangeListener l,
+                                          AudioAttributes requestAttributes,
+                                          int durationHint,
+                                          int flags) throws IllegalArgumentException {
+        return mManager.requestAudioFocus(l, requestAttributes, durationHint, flags);
+    }
+
+    @Override
+    public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
+        return mManager.abandonAudioFocus(l, aa);
+    }
+
+    @Override
     public void onCarDisconnected() {
         //nothing to do
     }
diff --git a/car-systemtest-lib/src/android/car/test/CarTestManager.java b/car-systemtest-lib/src/android/car/test/CarTestManager.java
index 89f55ad..cfd6310 100644
--- a/car-systemtest-lib/src/android/car/test/CarTestManager.java
+++ b/car-systemtest-lib/src/android/car/test/CarTestManager.java
@@ -66,6 +66,10 @@
         // should not happen for embedded
     }
 
+    /**
+     * Inject vehicle prop value event.
+     * @param value
+     */
     public void injectEvent(VehiclePropValue value) {
         try {
             mService.injectEvent(new VehiclePropValueParcelable(value));
@@ -75,6 +79,19 @@
     }
 
     /**
+     * Check if given property is supported by vehicle hal.
+     * @param property
+     * @return
+     */
+    public boolean isPropertySupported(int property) {
+        try {
+            return mService.isPropertySupported(property);
+        } catch (RemoteException e) {
+            handleRemoteException(e);
+        }
+        return false;
+    }
+    /**
      * Start mocking vehicle HAL. It is somewhat strange to re-use interface in lower level
      * API, but this is only for testing, and interface is exactly the same.
      * @param mock
@@ -93,6 +110,9 @@
         }
     }
 
+    /**
+     * Stop previously started mocking.
+     */
     public void stopMocking() {
         IVehicleNetworkHalMockImpl halMockImpl;
         synchronized (this) {
diff --git a/car-systemtest-lib/src/android/car/test/ICarTest.aidl b/car-systemtest-lib/src/android/car/test/ICarTest.aidl
index 2e763b7..05a2adc 100644
--- a/car-systemtest-lib/src/android/car/test/ICarTest.aidl
+++ b/car-systemtest-lib/src/android/car/test/ICarTest.aidl
@@ -27,4 +27,6 @@
     void startMocking(in IVehicleNetworkHalMock mock, int flags)  = 2;
     /** Finish mocking mode. */
     void stopMocking(in IVehicleNetworkHalMock mock)              = 3;
+    /** If given property is supported or not */
+    boolean isPropertySupported(int property)                     = 4;
 }
diff --git a/car-ui-provider/Android.mk b/car-ui-provider/Android.mk
index 6b9960e..a64c485 100644
--- a/car-ui-provider/Android.mk
+++ b/car-ui-provider/Android.mk
@@ -20,7 +20,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_CERTIFICATE := shared
+LOCAL_CERTIFICATE := platform
 LOCAL_MODULE_TAGS := optional
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/car-ui-provider/AndroidManifest.xml b/car-ui-provider/AndroidManifest.xml
index dfd64d8..ffbdfed 100644
--- a/car-ui-provider/AndroidManifest.xml
+++ b/car-ui-provider/AndroidManifest.xml
@@ -16,9 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-        package="android.support.car.ui.provider" >
-    <uses-sdk android:minSdkVersion="22"
-        android:targetSdkVersion="22" />
-    <application android:label="@string/app_name"
-        ></application>
+        package="android.car.ui.provider" >
+    <uses-sdk android:minSdkVersion="23"
+        android:targetSdkVersion="23" />
+    <application android:label="@string/app_name" />
 </manifest>
diff --git a/car-ui-provider/res/layout/car_activity.xml b/car-ui-provider/res/layout/car_activity.xml
index 75cb267..776b1f5 100644
--- a/car-ui-provider/res/layout/car_activity.xml
+++ b/car-ui-provider/res/layout/car_activity.xml
@@ -24,7 +24,7 @@
         android:layout_height="match_parent"
         android:scaleType="centerCrop" />
 
-    <android.support.car.ui.provider.CarDrawerLayout
+    <android.car.ui.provider.CarDrawerLayout
         android:id="@+id/drawer_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
@@ -43,7 +43,7 @@
             android:background="@color/car_card"
             android:paddingTop="@dimen/car_drawer_header_height" >
 
-            <android.support.car.ui.provider.PagedListView
+            <android.car.ui.provider.PagedListView
                 android:id="@+id/list_view"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
@@ -81,7 +81,7 @@
 
             </android.support.v7.widget.CardView>
         </FrameLayout>
-    </android.support.car.ui.provider.CarDrawerLayout>
+    </android.car.ui.provider.CarDrawerLayout>
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/car-ui-provider/res/layout/car_paged_recycler_view.xml b/car-ui-provider/res/layout/car_paged_recycler_view.xml
index c9c15ac..a01bf70 100644
--- a/car-ui-provider/res/layout/car_paged_recycler_view.xml
+++ b/car-ui-provider/res/layout/car_paged_recycler_view.xml
@@ -1,11 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
 <!-- Cloned from car ui lib for use in CarUiProvider. Must be kept in sync. -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
 
-    <android.support.car.ui.provider.PagedScrollBarView
+    <android.car.ui.provider.PagedScrollBarView
         android:id="@+id/paged_scroll_view"
         android:layout_width="@dimen/car_paged_list_view_pagination_width"
         android:layout_height="match_parent"
@@ -13,7 +27,7 @@
         android:paddingTop="16dp"
         android:visibility="invisible" />
 
-    <android.support.car.ui.provider.MaxWidthLayout
+    <android.car.ui.provider.MaxWidthLayout
         android:id="@+id/max_width_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -21,12 +35,12 @@
         android:clipChildren="false"
         app:carMaxWidth="@dimen/car_card_max_width" >
 
-        <android.support.car.ui.provider.CarRecyclerView
+        <android.car.ui.provider.CarRecyclerView
             android:id="@+id/recycler_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="center_horizontal"
             android:clipChildren="false" />
-    </android.support.car.ui.provider.MaxWidthLayout>
+    </android.car.ui.provider.MaxWidthLayout>
 
 </merge>
diff --git a/car-ui-provider/src/android/support/car/ui/provider/CarDrawerLayout.java b/car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
similarity index 99%
rename from car-ui-provider/src/android/support/car/ui/provider/CarDrawerLayout.java
rename to car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
index 9aabb14..cd3e01d 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/CarDrawerLayout.java
+++ b/car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
 import android.content.Context;
 import android.content.res.Resources;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/CarRecyclerView.java b/car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/CarRecyclerView.java
rename to car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
index 33fbcd3..04fcd63 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/CarRecyclerView.java
+++ b/car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/CarUiEntry.java b/car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
similarity index 90%
rename from car-ui-provider/src/android/support/car/ui/provider/CarUiEntry.java
rename to car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
index 7361d79..4375c8b 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/CarUiEntry.java
+++ b/car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
+import android.car.app.menu.CarMenuCallbacks;
+import android.car.app.menu.RootMenu;
+import android.car.app.menu.SearchBoxEditListener;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -22,10 +25,6 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.support.car.app.menu.CarDrawerActivity;
-import android.support.car.app.menu.Constants;
 import android.support.car.input.CarRestrictedEditText;
 import android.support.car.ui.DrawerArrowDrawable;
 import android.support.car.ui.PagedListView;
@@ -45,10 +44,7 @@
 
 import android.support.car.ui.R;
 
-import android.support.car.app.menu.ICarMenuCallbacks;
-import android.support.car.app.menu.ISearchBoxEditListener;
-
-public class CarUiEntry extends android.support.car.app.menu.CarUiEntry {
+public class CarUiEntry extends android.car.app.menu.CarUiEntry {
     private static final String TAG = "Embedded_CarUiEntry";
 
     // These values and setSearchBoxMode exist rather than separate methods to make sure exactly the
@@ -79,7 +75,7 @@
     private ImageView mSearchBoxSuperSearchLogo;
     private FrameLayout mSearchBoxEndView;
     private View mTitleContainer;
-    private CarDrawerActivity.SearchBoxEditListener mSearchBoxEditListener;
+    private SearchBoxEditListener mSearchBoxEditListener;
 
     public interface SearchBoxClickListener {
         /**
@@ -171,12 +167,11 @@
     };
 
     @Override
-    public void setCarMenuBinder(IBinder binder) throws RemoteException {
-        ICarMenuCallbacks callbacks = ICarMenuCallbacks.Stub.asInterface(binder);
-        Bundle root = callbacks.getRoot();
-        if (root != null) {
+    public void setCarMenuCallbacks(CarMenuCallbacks callbacks){
+        RootMenu rootMenu = callbacks.getRootMenu(null);
+        if (rootMenu != null) {
             mDrawerController.setRootAndCallbacks(
-                    root.getString(Constants.CarMenuConstants.KEY_ID), callbacks);
+                    rootMenu.getId(), callbacks);
             mDrawerController.setDrawerEnabled(true);
         } else {
             hideMenuButton();
@@ -215,18 +210,6 @@
         mMenuButton.setImageDrawable(new BitmapDrawable(mUiLibContext.getResources(), bitmap));
     }
 
-    /**
-     * Set the progress of the animated {@link DrawerArrowDrawable}.
-     * @param progress 0f displays a menu button
-     *                 1f displays a back button
-     *                 anything in between will be an interpolation of the drawable between
-     *                 back and menu
-     */
-    @Override
-    public void setMenuProgress(float progress) {
-        mDrawerArrowDrawable.setProgress(progress);
-    }
-
     @Override
     public void setScrimColor(int color) {
         mDrawerLayout.setScrimColor(color);
@@ -238,11 +221,6 @@
     }
 
     @Override
-    public void setTitleText(CharSequence title) {
-        mTitleView.setText(title);
-    }
-
-    @Override
     public void closeDrawer() {
         mDrawerController.closeDrawer();
     }
@@ -257,40 +235,6 @@
         mDrawerController.showMenu(id, title);
     }
 
-    private void adjustDrawer() {
-        Resources resources = mUiLibContext.getResources();
-        float width = resources.getDisplayMetrics().widthPixels;
-        CarDrawerLayout.LayoutParams layoutParams = new CarDrawerLayout.LayoutParams(
-                CarDrawerLayout.LayoutParams.MATCH_PARENT,
-                CarDrawerLayout.LayoutParams.MATCH_PARENT);
-        layoutParams.gravity = Gravity.LEFT;
-        // 1. If the screen width is larger than 800dp, the drawer width is kept as 704dp;
-        // 2. Else the drawer width is adjusted to keep the margin end of drawer as 96dp.
-        if (width > resources.getDimension(R.dimen.car_standard_width)) {
-            layoutParams.setMarginEnd(
-                    (int) (width - resources.getDimension(R.dimen.car_drawer_standard_width)));
-        } else {
-            layoutParams.setMarginEnd(
-                    (int) resources.getDimension(R.dimen.car_card_margin));
-        }
-        mContentView.findViewById(R.id.drawer).setLayoutParams(layoutParams);
-    }
-
-    private final PagedListView.OnScrollBarListener mOnScrollBarListener =
-            new PagedListView.OnScrollBarListener() {
-
-                @Override
-                public void onReachBottom() {
-                    if (mDrawerController.isTruncatedList()) {
-                        mTruncatedListCardView.setVisibility(View.VISIBLE);
-                    }
-                }
-
-                @Override
-                public void onLeaveBottom() {
-                    mTruncatedListCardView.setVisibility(View.GONE);
-                }
-            };
 
     @Override
     public void setMenuButtonColor(int color) {
@@ -323,27 +267,64 @@
         mDrawerController.setAutoLightDarkMode();
     }
 
+    @Override
+    public void showToast(String msg, long duration) {
+        // TODO: add toast support
+    }
 
-    public CharSequence getText() {
+    @Override
+    public CharSequence getSearchBoxText() {
         return mCarRestrictedEditText.getText();
     }
 
-    public EditText startSearchInput(String hint,
+    @Override
+    public EditText startInput(String hint,
             View.OnClickListener searchBoxClickListener) {
         mSearchBoxClickListener = wrapSearchBoxClickListener(searchBoxClickListener);
         setSearchBoxModeLarge(hint);
         return mCarRestrictedEditText;
     }
 
-    public void setSearchBoxModeLarge(String hint) {
-        mCarRestrictedEditText.setHint(hint);
-        setSearchBoxMode(SEARCH_BOX_MODE_LARGE);
+
+    @Override
+    public void onRestoreInstanceState(Bundle savedInstanceState) {
+        if (mDrawerController != null) {
+            mDrawerController.restoreState(savedInstanceState);
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        if (mDrawerController != null) {
+            mDrawerController.saveState(outState);
+        }
+    }
+
+    @Override
+    public void onStart() {
+
+    }
+
+    @Override
+    public void onResume() {
+
+    }
+
+    @Override
+    public void onPause() {
+
+    }
+
+    @Override
+    public void onStop() {
+
     }
 
     /**
      * Sets the colors of all the parts of the search box (regardless of whether it is currently
      * showing).
      */
+    @Override
     public void setSearchBoxColors(int backgroundColor, int searchLogoColor, int textColor,
                                    int hintTextColor) {
         // set background color of mSearchBox to get rid of the animation artifact in b/23767062
@@ -354,11 +335,59 @@
         mCarRestrictedEditText.setHintTextColor(hintTextColor);
     }
 
+    /**
+     * Sets the view to be displayed at the end of the search box, or null to clear any existing
+     * views.
+     */
+    @Override
+    public void setSearchBoxEndView(View endView) {
+        if (endView == null) {
+            mSearchBoxEndView.removeAllViews();
+        } else if (mSearchBoxEndView.getChildCount() == 0) {
+            mSearchBoxEndView.addView(endView);
+        } else if (mSearchBoxEndView.getChildAt(0) != endView) {
+            mSearchBoxEndView.removeViewAt(0);
+            mSearchBoxEndView.addView(endView);
+        }
+    }
+
+    @Override
     public void showSearchBox(final View.OnClickListener listener) {
         setSearchBoxMode(SEARCH_BOX_MODE_SMALL);
         mSearchBoxClickListener = wrapSearchBoxClickListener(listener);
     }
 
+    @Override
+    public void stopInput() {
+        setSearchBoxMode(SEARCH_BOX_MODE_NONE);
+    }
+
+    @Override
+    public void setSearchBoxEditListener(SearchBoxEditListener listener) {
+        mSearchBoxEditListener = listener;
+    }
+
+
+    /**
+     * Set the progress of the animated {@link DrawerArrowDrawable}.
+     * @param progress 0f displays a menu button
+     *                 1f displays a back button
+     *                 anything in between will be an interpolation of the drawable between
+     *                 back and menu
+     */
+    public void setMenuProgress(float progress) {
+        mDrawerArrowDrawable.setProgress(progress);
+    }
+
+    private void setSearchBoxModeLarge(String hint) {
+        mCarRestrictedEditText.setHint(hint);
+        setSearchBoxMode(SEARCH_BOX_MODE_LARGE);
+    }
+
+    public void setTitleText(CharSequence title) {
+        mTitleView.setText(title);
+    }
+
     /**
      * Sets all the view visibilities and layout params for a search box mode.
      */
@@ -439,20 +468,6 @@
         }
     }
 
-    /**
-     * Sets the view to be displayed at the end of the search box, or null to clear any existing
-     * views.
-     */
-    public void setSearchBoxEndView(View endView) {
-        if (endView == null) {
-            mSearchBoxEndView.removeAllViews();
-        } else if (mSearchBoxEndView.getChildCount() == 0) {
-            mSearchBoxEndView.addView(endView);
-        } else if (mSearchBoxEndView.getChildAt(0) != endView) {
-            mSearchBoxEndView.removeViewAt(0);
-            mSearchBoxEndView.addView(endView);
-        }
-    }
 
     private final Runnable mSetEditTextGoneRunnable = new Runnable() {
         @Override
@@ -468,9 +483,6 @@
         }
     };
 
-    public void stopInput() {
-        setSearchBoxMode(SEARCH_BOX_MODE_NONE);
-    }
 
     private SearchBoxClickListener wrapSearchBoxClickListener(final View.OnClickListener listener) {
         return new SearchBoxClickListener() {
@@ -481,71 +493,6 @@
         };
     }
 
-    public void setSearchBoxEditListener(IBinder binder) {
-        ISearchBoxEditListener listener = ISearchBoxEditListener.Stub.asInterface(binder);
-        mSearchBoxEditListener = new WrappedSearchBoxEditListener(listener);
-    }
-
-    private static class WrappedSearchBoxEditListener
-            implements CarDrawerActivity.SearchBoxEditListener {
-        private final ISearchBoxEditListener mListener;
-
-        public WrappedSearchBoxEditListener(ISearchBoxEditListener listener) {
-            mListener = listener;
-        }
-
-        @Override
-        public void onSearch(String text) {
-            try {
-                mListener.onSearch(text);
-            } catch (RemoteException e) {
-                Log.d(TAG, "Error calling onSearch", e);
-            }
-        }
-
-        @Override
-        public void onEdit(String text) {
-            try {
-                mListener.onEdit(text);
-            } catch (RemoteException e) {
-                Log.d(TAG, "Error calling onEdit", e);
-            }
-        }
-    }
-
-    @Override
-    public void onRestoreInstanceState(Bundle savedInstanceState) {
-        if (mDrawerController != null) {
-            mDrawerController.restoreState(savedInstanceState);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        if (mDrawerController != null) {
-            mDrawerController.saveState(outState);
-        }
-    }
-
-    @Override
-    public void onStart() {
-
-    }
-
-    @Override
-    public void onResume() {
-
-    }
-
-    @Override
-    public void onPause() {
-
-    }
-
-    @Override
-    public void onStop() {
-
-    }
 
     private static void setViewColor(View view, int color) {
         if (view instanceof TextView) {
@@ -561,4 +508,39 @@
             }
         }
     }
+
+    private void adjustDrawer() {
+        Resources resources = mUiLibContext.getResources();
+        float width = resources.getDisplayMetrics().widthPixels;
+        CarDrawerLayout.LayoutParams layoutParams = new CarDrawerLayout.LayoutParams(
+                CarDrawerLayout.LayoutParams.MATCH_PARENT,
+                CarDrawerLayout.LayoutParams.MATCH_PARENT);
+        layoutParams.gravity = Gravity.LEFT;
+        // 1. If the screen width is larger than 800dp, the drawer width is kept as 704dp;
+        // 2. Else the drawer width is adjusted to keep the margin end of drawer as 96dp.
+        if (width > resources.getDimension(R.dimen.car_standard_width)) {
+            layoutParams.setMarginEnd(
+                    (int) (width - resources.getDimension(R.dimen.car_drawer_standard_width)));
+        } else {
+            layoutParams.setMarginEnd(
+                    (int) resources.getDimension(R.dimen.car_card_margin));
+        }
+        mContentView.findViewById(R.id.drawer).setLayoutParams(layoutParams);
+    }
+
+    private final PagedListView.OnScrollBarListener mOnScrollBarListener =
+            new PagedListView.OnScrollBarListener() {
+
+                @Override
+                public void onReachBottom() {
+                    if (mDrawerController.isTruncatedList()) {
+                        mTruncatedListCardView.setVisibility(View.VISIBLE);
+                    }
+                }
+
+                @Override
+                public void onLeaveBottom() {
+                    mTruncatedListCardView.setVisibility(View.GONE);
+                }
+            };
 }
diff --git a/car-ui-provider/src/android/support/car/ui/provider/DrawerApiAdapter.java b/car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
similarity index 92%
rename from car-ui-provider/src/android/support/car/ui/provider/DrawerApiAdapter.java
rename to car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
index b3f8ebb..2571187 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/DrawerApiAdapter.java
+++ b/car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -40,17 +40,21 @@
 import java.util.List;
 import java.util.Map;
 
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_EMPTY_PLACEHOLDER;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_FLAGS;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_ID;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_LEFTICON;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_REMOTEVIEWS;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_RIGHTICON;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_RIGHTTEXT;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_TEXT;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_TITLE;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_WIDGET;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_WIDGET_STATE;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.FLAG_BROWSABLE;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.FLAG_FIRSTITEM;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_EMPTY_PLACEHOLDER;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_FLAGS;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_ID;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_LEFTICON;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_REMOTEVIEWS;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_RIGHTICON;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_RIGHTTEXT;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_TEXT;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_TITLE;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_WIDGET;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_WIDGET_STATE;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.WIDGET_CHECKBOX;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.WIDGET_TEXT_VIEW;
 
 public class DrawerApiAdapter extends RecyclerView.Adapter<CarListItemViewHolder>
         implements PagedListView.ItemCap {
@@ -100,7 +104,7 @@
         }
 
         int flags = item.getInt(KEY_FLAGS);
-        if ((flags & CarMenu.FLAG_BROWSABLE) != 0 || item.containsKey(KEY_RIGHTICON)) {
+        if ((flags & FLAG_BROWSABLE) != 0 || item.containsKey(KEY_RIGHTICON)) {
             return R.layout.car_imageview;
         }
 
@@ -109,9 +113,9 @@
         }
 
         switch (item.getInt(KEY_WIDGET)) {
-            case CarMenu.WIDGET_CHECKBOX:
+            case WIDGET_CHECKBOX:
                 return R.layout.car_menu_checkbox;
-            case CarMenu.WIDGET_TEXT_VIEW:
+            case WIDGET_TEXT_VIEW:
                 return R.layout.car_textview;
             default:
                 return 0;
@@ -216,7 +220,7 @@
                     mNoLeftIcon = false;
                 }
                 if (bundle.containsKey(KEY_FLAGS) &&
-                        (bundle.getInt(KEY_FLAGS) & CarMenu.FLAG_FIRSTITEM) != 0) {
+                        (bundle.getInt(KEY_FLAGS) & FLAG_FIRSTITEM) != 0) {
                     mFirstItemIndex = index;
                 }
                 mIdToPosMap.put(bundle.getString(KEY_ID), index);
@@ -332,7 +336,7 @@
             return;
         }
         final int flags = item.getInt(KEY_FLAGS);
-        if ((flags & CarMenu.FLAG_BROWSABLE) != 0) {
+        if ((flags & FLAG_BROWSABLE) != 0) {
             // Set the resource id as the tag so we can reload it on day/night mode change.
             // If the tag is -1 or not set, then assume the app will send an updated bitmap
             holder.rightImage.setTag(R.drawable.ic_chevron_right);
diff --git a/car-ui-provider/src/android/support/car/ui/provider/DrawerController.java b/car-ui-provider/src/android/car/ui/provider/DrawerController.java
similarity index 81%
rename from car-ui-provider/src/android/support/car/ui/provider/DrawerController.java
rename to car-ui-provider/src/android/car/ui/provider/DrawerController.java
index 07a9dab..d554f08 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/DrawerController.java
+++ b/car-ui-provider/src/android/car/ui/provider/DrawerController.java
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
+import android.car.app.menu.CarMenuCallbacks;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.support.car.ui.PagedListView;
 import android.support.car.ui.R;
 import android.support.v7.widget.CardView;
@@ -30,10 +30,6 @@
 import android.view.animation.AnimationUtils;
 import android.widget.ProgressBar;
 
-import android.support.car.app.menu.CarMenu;
-import android.support.car.app.menu.ICarMenuCallbacks;
-import android.support.car.app.menu.ISubscriptionCallbacks;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -41,9 +37,10 @@
 import java.util.Queue;
 import java.util.Stack;
 
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_FLAGS;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_ID;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_TITLE;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.FLAG_BROWSABLE;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_FLAGS;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_ID;
+import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_TITLE;
 
 /**
  * Controls the drawer for SDK app
@@ -87,7 +84,7 @@
     private final CardView mTruncatedListCardView;
     private final Stack<Integer> mClickCountStack = new Stack<>();
 
-    private ICarMenuCallbacks mCarMenuCallbacks;
+    private CarMenuCallbacks mCarMenuCallbacks;
     private DrawerApiAdapter mAdapter;
     private int mScrimColor = CarDrawerLayout.DEFAULT_SCRIM_COLOR;
     private boolean mIsDrawerOpen;
@@ -128,11 +125,7 @@
         mUiEntry.setMenuProgress(1.0f);
         // This can be null on day/night mode changes
         if (mCarMenuCallbacks != null) {
-            try {
-                mCarMenuCallbacks.onCarMenuOpened();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Exception calling back on drawer opened: ", e);
-            }
+            mCarMenuCallbacks.onCarMenuOpened();
         }
     }
 
@@ -145,11 +138,7 @@
         mUiEntry.setTitle(mContentTitle);
         // This can be null on day/night mode changes
         if (mCarMenuCallbacks != null) {
-            try {
-                mCarMenuCallbacks.onCarMenuClosed();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Exception calling back on drawer closed: ", e);
-            }
+            mCarMenuCallbacks.onCarMenuClosed();
         }
     }
 
@@ -162,11 +151,7 @@
         mIsDrawerAnimating = true;
         // This can be null on day/night mode changes
         if (mCarMenuCallbacks != null) {
-            try {
-                mCarMenuCallbacks.onCarMenuOpening();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Exception calling back on drawer opening: ", e);
-            }
+            mCarMenuCallbacks.onCarMenuOpening();
         }
     }
 
@@ -180,11 +165,7 @@
         mIsDrawerAnimating = true;
         // This can be null on day/night mode changes
         if (mCarMenuCallbacks != null) {
-            try {
-                mCarMenuCallbacks.onCarMenuClosing();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Exception calling back on drawer closing: ", e);
-            }
+            mCarMenuCallbacks.onCarMenuClosing();
         }
     }
 
@@ -196,47 +177,39 @@
         }
         int flags = item.getInt(KEY_FLAGS);
         String id = item.getString(KEY_ID);
-        try {
-            // Page number is 0 index, + 1 for the actual click.
-            int clicksUsed = mListView.getPage(position) + 1;
-            mClickCountStack.push(clicksUsed);
-            mListView.setMaxPages(mListView.getMaxPages() - clicksUsed);
-            mCarMenuCallbacks.onItemClicked(id);
-            if ((flags & CarMenu.FLAG_BROWSABLE) != 0) {
-                if (mListView.getMaxPages() == 0) {
-                    mIsCapped = true;
-                }
-                CharSequence title = item.getString(KEY_TITLE);
-                if (TextUtils.isEmpty(title)) {
-                    title = mContentTitle;
-                }
-                mUiEntry.setTitleText(title);
-                mTitles.push(title);
-                if (!mSubscriptionIds.isEmpty()) {
-                    mPlvAnimationController.enqueueExitAnimation(mClearAdapterRunnable);
-                }
-                mProgressBar.setVisibility(View.VISIBLE);
-                if (!mSubscriptionIds.isEmpty()) {
-                    mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
-                }
-                mSubscriptionIds.push(id);
-                subscribe(id);
-            } else {
-                closeDrawer();
+
+        // Page number is 0 index, + 1 for the actual click.
+        int clicksUsed = mListView.getPage(position) + 1;
+        mClickCountStack.push(clicksUsed);
+        mListView.setMaxPages(mListView.getMaxPages() - clicksUsed);
+        mCarMenuCallbacks.onItemClicked(id);
+        if ((flags & FLAG_BROWSABLE) != 0) {
+            if (mListView.getMaxPages() == 0) {
+                mIsCapped = true;
             }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception: ", e);
+            CharSequence title = item.getString(KEY_TITLE);
+            if (TextUtils.isEmpty(title)) {
+                title = mContentTitle;
+            }
+            mUiEntry.setTitleText(title);
+            mTitles.push(title);
+            if (!mSubscriptionIds.isEmpty()) {
+                mPlvAnimationController.enqueueExitAnimation(mClearAdapterRunnable);
+            }
+            mProgressBar.setVisibility(View.VISIBLE);
+            if (!mSubscriptionIds.isEmpty()) {
+                mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
+            }
+            mSubscriptionIds.push(id);
+            subscribe(id);
+        } else {
+            closeDrawer();
         }
     }
 
     @Override
     public boolean onItemLongClicked(Bundle item) {
-        try {
-            return mCarMenuCallbacks.onItemLongClicked(item.getString(KEY_ID));
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception: ", e);
-        }
-        return false;
+        return mCarMenuCallbacks.onItemLongClicked(item.getString(KEY_ID));
     }
 
     @Override
@@ -261,7 +234,7 @@
         }
     }
 
-    public void setRootAndCallbacks(String rootId, ICarMenuCallbacks callbacks) {
+    public void setRootAndCallbacks(String rootId, CarMenuCallbacks callbacks) {
         mAdapter = new DrawerApiAdapter();
         mAdapter.setItemSelectedListener(this);
         mListView.setAdapter(mAdapter);
@@ -271,13 +244,8 @@
         if (mSubscriptionIds.isEmpty()) {
             setRootId(rootId);
         } else {
-            try {
-                subscribe(mSubscriptionIds.peek());
-                openDrawer();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error restoring drawer subscription state.", e);
-                return;
-            }
+            subscribe(mSubscriptionIds.peek());
+            openDrawer();
         }
     }
 
@@ -381,7 +349,7 @@
         Bundle bundle = new Bundle();
         bundle.putString(KEY_ID, id);
         bundle.putString(KEY_TITLE, title);
-        bundle.putInt(KEY_FLAGS, CarMenu.FLAG_BROWSABLE);
+        bundle.putInt(KEY_FLAGS, FLAG_BROWSABLE);
         onItemClicked(bundle, 0 /* position */);
     }
 
@@ -399,11 +367,7 @@
 
     private void clearMenu() {
         if (!mSubscriptionIds.isEmpty()) {
-            try {
-                mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error unsubscribing from " + mSubscriptionIds.peek() + ": ", e);
-            }
+            mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
             mSubscriptionIds.clear();
             mTitles.clear();
         }
@@ -422,7 +386,7 @@
         mContainer.addViewFader(mStatusViewViewFader);
     }
 
-    private void subscribe(String id) throws RemoteException {
+    private void subscribe(String id) {
         mProgressBar.setVisibility(View.VISIBLE);
         mCarMenuCallbacks.subscribe(id, mSubscriptionCallbacks);
     }
@@ -438,16 +402,9 @@
         if (mSubscriptionIds.size() > 1) {
             mPlvAnimationController.enqueueBackAnimation(mClearAdapterRunnable);
             mProgressBar.setVisibility(View.VISIBLE);
-            try {
-                mCarMenuCallbacks.unsubscribe(mSubscriptionIds.pop(),
-                        mSubscriptionCallbacks);
-                subscribe(mSubscriptionIds.peek());
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error subscribing or unsubscribing: ", e);
-                mProgressBar.setVisibility(View.GONE);
-                closeDrawer();
-                return;
-            }
+            mCarMenuCallbacks.unsubscribe(mSubscriptionIds.pop(),
+                    mSubscriptionCallbacks);
+            subscribe(mSubscriptionIds.peek());
             // Restore the title for this menu level.
             mTitles.pop();
             CharSequence title = mTitles.peek();
@@ -463,12 +420,8 @@
     private final View.OnClickListener mMenuClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View view) {
-            try {
-                if (mIsDrawerAnimating || mCarMenuCallbacks.onMenuClicked()) {
-                    return;
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error calling mCarMenuCallbacks.onMenuClicked()");
+            if (mIsDrawerAnimating || mCarMenuCallbacks.onMenuClicked()) {
+                return;
             }
             // Check if drawer has root set.
             if (mRootId == null) {
@@ -482,14 +435,9 @@
                 mIsCapped = false;
                 backOrClose();
             } else {
-                try {
-                    mSubscriptionIds.push(mRootId);
-                    mTitles.push(mContentTitle);
-                    subscribe(mRootId);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error subscribing to the root id " + mRootId, e);
-                    return;
-                }
+                mSubscriptionIds.push(mRootId);
+                mTitles.push(mContentTitle);
+                subscribe(mRootId);
                 openDrawer();
             }
         }
@@ -639,18 +587,12 @@
         }
     }
 
-    private class SubscriptionCallbacks extends ISubscriptionCallbacks.Stub {
+    private class SubscriptionCallbacks extends android.car.app.menu.SubscriptionCallbacks {
         private final Object mItemLock = new Object();
         private volatile List<Bundle> mItems;
 
         @Override
-        public int getVersion() {
-            return 0;
-        }
-
-        @Override
-        public void onChildrenLoaded(String parentId, final List<Bundle> items)
-                throws RemoteException {
+        public void onChildrenLoaded(String parentId, final List<Bundle> items) {
             if (mSubscriptionIds.isEmpty() || parentId.equals(mSubscriptionIds.peek())) {
                 // Add unavailable category explanation at the first item of menu.
                 if (mIsCapped) {
@@ -676,12 +618,12 @@
         }
 
         @Override
-        public void onError(String id) throws RemoteException {
+        public void onError(String id) {
             // TODO: do something useful here.
         }
 
         @Override
-        public void onChildChanged(String parentId, Bundle bundle) throws RemoteException {
+        public void onChildChanged(String parentId, Bundle bundle) {
             if (!mSubscriptionIds.isEmpty() && parentId.equals(mSubscriptionIds.peek())) {
                 // List is still animating, so adapter hasn't been updated. Update the list that
                 // needs to be set.
diff --git a/car-ui-provider/src/android/support/car/ui/provider/MaxWidthLayout.java b/car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/MaxWidthLayout.java
rename to car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
index f1547f4..4311c5a 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/MaxWidthLayout.java
+++ b/car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/PagedListView.java b/car-ui-provider/src/android/car/ui/provider/PagedListView.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/PagedListView.java
rename to car-ui-provider/src/android/car/ui/provider/PagedListView.java
index 3d0b1e7..9648dd6 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/PagedListView.java
+++ b/car-ui-provider/src/android/car/ui/provider/PagedListView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/PagedScrollBarView.java b/car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/PagedScrollBarView.java
rename to car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
index b4b6bbd..91a25ab 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/PagedScrollBarView.java
+++ b/car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.car.ui.provider;
+package android.car.ui.provider;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
index 5925e9b..331f93c 100644
--- a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
+++ b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
@@ -707,8 +707,10 @@
         }
 
         if (!isCustom && v.getValueType() != valueType) {
-            throw new IllegalArgumentException("Unexpected type: " + v.getValueType()
-                    + ", expecting: " + valueType);
+            throw new IllegalArgumentException(
+                    "Unexpected type for property 0x" + Integer.toHexString(property) +
+                    " got:0x" + Integer.toHexString(v.getValueType())
+                    + ", expecting:0x" + Integer.toHexString(valueType));
         }
         return v;
     }
diff --git a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
index faf56f6..52affef 100644
--- a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
+++ b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
@@ -15,7 +15,8 @@
  * limitations under the License.
  */
 
-//Autogenerated from vehicle.h. Do not modify manually.
+//Autogenerated from vehicle.h using libvehiclenetwork/tool/vehicle_code_gen.py.
+//Do not modify manually.
 
 package com.android.car.vehiclenetwork;
 
@@ -56,11 +57,11 @@
 public static final int VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT = 0x00000902;
 public static final int VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY = 0x00000903;
 public static final int VEHICLE_PROPERTY_AUDIO_HW_VARIANT = 0x00000904;
-public static final int VEHICLE_PROPERTY_AUDIO_CONTEXT = 0x00000905;
 public static final int VEHICLE_PROPERTY_AP_POWER_STATE = 0x00000A00;
 public static final int VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS = 0x00000A01;
 public static final int VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON = 0x00000A02;
 public static final int VEHICLE_PROPERTY_HW_KEY_INPUT = 0x00000A10;
+public static final int VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO = 0x00000A20;
 public static final int VEHICLE_PROPERTY_CUSTOM_START = 0x70000000;
 public static final int VEHICLE_PROPERTY_CUSTOM_END = 0x73ffffff;
 public static final int VEHICLE_PROPERTY_INTERNAL_START = 0x74000000;
@@ -97,16 +98,16 @@
 case VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
 case VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
 case VEHICLE_PROPERTY_RADIO_PRESET: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
-case VEHICLE_PROPERTY_AUDIO_FOCUS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3;
+case VEHICLE_PROPERTY_AUDIO_FOCUS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
 case VEHICLE_PROPERTY_AUDIO_VOLUME: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3;
 case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
 case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
 case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
 case VEHICLE_PROPERTY_AP_POWER_STATE: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
 case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
 case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
 case VEHICLE_PROPERTY_HW_KEY_INPUT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
 case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
 default: return VehicleValueType.VEHICLE_VALUE_TYPE_SHOUD_NOT_USE;
 }
@@ -148,11 +149,11 @@
 case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return "VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT";
 case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return "VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY";
 case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return "VEHICLE_PROPERTY_AUDIO_HW_VARIANT";
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return "VEHICLE_PROPERTY_AUDIO_CONTEXT";
 case VEHICLE_PROPERTY_AP_POWER_STATE: return "VEHICLE_PROPERTY_AP_POWER_STATE";
 case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return "VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS";
 case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return "VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON";
 case VEHICLE_PROPERTY_HW_KEY_INPUT: return "VEHICLE_PROPERTY_HW_KEY_INPUT";
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return "VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO";
 case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return "VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE";
 default: return "UNKNOWN_PROPERTY";
 }
@@ -194,11 +195,11 @@
 case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
 case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
 case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC };
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
 case VEHICLE_PROPERTY_AP_POWER_STATE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
 case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
 case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC };
 case VEHICLE_PROPERTY_HW_KEY_INPUT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
 case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
 default: return null;
 }
@@ -240,11 +241,11 @@
 case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
 case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
 case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
 case VEHICLE_PROPERTY_AP_POWER_STATE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
 case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ , VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
 case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
 case VEHICLE_PROPERTY_HW_KEY_INPUT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
 case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
 default: return null;
 }
@@ -362,11 +363,45 @@
 public static final int VEHICLE_AUDIO_FOCUS_INDEX_FOCUS = 0;
 public static final int VEHICLE_AUDIO_FOCUS_INDEX_STREAMS = 1;
 public static final int VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE = 2;
+public static final int VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS = 3;
 public static String enumToString(int v) {
 switch(v) {
 case VEHICLE_AUDIO_FOCUS_INDEX_FOCUS: return "VEHICLE_AUDIO_FOCUS_INDEX_FOCUS";
 case VEHICLE_AUDIO_FOCUS_INDEX_STREAMS: return "VEHICLE_AUDIO_FOCUS_INDEX_STREAMS";
 case VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE: return "VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE";
+case VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS: return "VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS";
+default: return "UNKNOWN";
+}
+}
+}
+
+public static class VehicleAudioContextFlag {
+public static final int VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG = 0x1;
+public static final int VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG = 0x2;
+public static final int VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG = 0x4;
+public static final int VEHICLE_AUDIO_CONTEXT_CALL_FLAG = 0x8;
+public static final int VEHICLE_AUDIO_CONTEXT_ALARM_FLAG = 0x10;
+public static final int VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG = 0x20;
+public static final int VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG = 0x40;
+public static final int VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG = 0x80;
+public static final int VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG = 0x100;
+public static final int VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG = 0x200;
+public static final int VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG = 0x400;
+public static final int VEHICLE_AUDIO_CONTEXT_RADIO_FLAG = 0x800;
+public static String enumToString(int v) {
+switch(v) {
+case VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG: return "VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG";
+case VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG";
+case VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG: return "VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG";
+case VEHICLE_AUDIO_CONTEXT_CALL_FLAG: return "VEHICLE_AUDIO_CONTEXT_CALL_FLAG";
+case VEHICLE_AUDIO_CONTEXT_ALARM_FLAG: return "VEHICLE_AUDIO_CONTEXT_ALARM_FLAG";
+case VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG";
+case VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG: return "VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG";
+case VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG: return "VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG";
+case VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG: return "VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG";
+case VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG: return "VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG";
+case VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG: return "VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG";
+case VEHICLE_AUDIO_CONTEXT_RADIO_FLAG: return "VEHICLE_AUDIO_CONTEXT_RADIO_FLAG";
 default: return "UNKNOWN";
 }
 }
@@ -432,36 +467,6 @@
 }
 }
 
-public static class VehicleAudioContextFlag {
-public static final int VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG = 0x1;
-public static final int VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG = 0x2;
-public static final int VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG = 0x4;
-public static final int VEHICLE_AUDIO_CONTEXT_CALL_FLAG = 0x8;
-public static final int VEHICLE_AUDIO_CONTEXT_ALARM_FLAG = 0x10;
-public static final int VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG = 0x20;
-public static final int VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG = 0x40;
-public static final int VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG = 0x80;
-public static final int VEHICLE_AUDIO_CONTEXT_CD_ROM = 0x100;
-public static final int VEHICLE_AUDIO_CONTEXT_AUX_AUDIO = 0x200;
-public static final int VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND = 0x400;
-public static String enumToString(int v) {
-switch(v) {
-case VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG: return "VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG";
-case VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG";
-case VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG: return "VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG";
-case VEHICLE_AUDIO_CONTEXT_CALL_FLAG: return "VEHICLE_AUDIO_CONTEXT_CALL_FLAG";
-case VEHICLE_AUDIO_CONTEXT_ALARM_FLAG: return "VEHICLE_AUDIO_CONTEXT_ALARM_FLAG";
-case VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG";
-case VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG: return "VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG";
-case VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG: return "VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG";
-case VEHICLE_AUDIO_CONTEXT_CD_ROM: return "VEHICLE_AUDIO_CONTEXT_CD_ROM";
-case VEHICLE_AUDIO_CONTEXT_AUX_AUDIO: return "VEHICLE_AUDIO_CONTEXT_AUX_AUDIO";
-case VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND: return "VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND";
-default: return "UNKNOWN";
-}
-}
-}
-
 public static class VehicleApPowerStateConfigFlag {
 public static final int VEHICLE_AP_POWER_STATE_CONFIG_ENABLE_DEEP_SLEEP_FLAG = 0x1;
 public static final int VEHICLE_AP_POWER_STATE_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2;
@@ -578,6 +583,20 @@
 }
 }
 
+public static class VehicleInstumentClusterType {
+public static final int VEHICLE_INSTRUMENT_CLUSTER_TYPE_NONE = 0;
+public static final int VEHICLE_INSTRUMENT_CLUSTER_TYPE_HAL_INTERFACE = 1;
+public static final int VEHICLE_INSTRUMENT_CLUSTER_TYPE_EXTERNAL_DISPLAY = 2;
+public static String enumToString(int v) {
+switch(v) {
+case VEHICLE_INSTRUMENT_CLUSTER_TYPE_NONE: return "VEHICLE_INSTRUMENT_CLUSTER_TYPE_NONE";
+case VEHICLE_INSTRUMENT_CLUSTER_TYPE_HAL_INTERFACE: return "VEHICLE_INSTRUMENT_CLUSTER_TYPE_HAL_INTERFACE";
+case VEHICLE_INSTRUMENT_CLUSTER_TYPE_EXTERNAL_DISPLAY: return "VEHICLE_INSTRUMENT_CLUSTER_TYPE_EXTERNAL_DISPLAY";
+default: return "UNKNOWN";
+}
+}
+}
+
 public static class VehicleValueType {
 public static final int VEHICLE_VALUE_TYPE_SHOUD_NOT_USE = 0x00;
 public static final int VEHICLE_VALUE_TYPE_STRING = 0x01;
@@ -771,7 +790,7 @@
 public static class VehicleGear {
 public static final int VEHICLE_GEAR_NEUTRAL = 0x0001;
 public static final int VEHICLE_GEAR_REVERSE = 0x0002;
-public static final int VEHICLE_GEAR_PARKING = 0x0004;
+public static final int VEHICLE_GEAR_PARK = 0x0004;
 public static final int VEHICLE_GEAR_DRIVE = 0x0008;
 public static final int VEHICLE_GEAR_L = 0x0010;
 public static final int VEHICLE_GEAR_1 = 0x0010;
@@ -787,7 +806,7 @@
 switch(v) {
 case VEHICLE_GEAR_NEUTRAL: return "VEHICLE_GEAR_NEUTRAL";
 case VEHICLE_GEAR_REVERSE: return "VEHICLE_GEAR_REVERSE";
-case VEHICLE_GEAR_PARKING: return "VEHICLE_GEAR_PARKING";
+case VEHICLE_GEAR_PARK: return "VEHICLE_GEAR_PARK";
 case VEHICLE_GEAR_DRIVE: return "VEHICLE_GEAR_DRIVE";
 case VEHICLE_GEAR_L: return "VEHICLE_GEAR_L";
 case VEHICLE_GEAR_2: return "VEHICLE_GEAR_2";
diff --git a/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h b/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h
index 3c34f5f..5e97ef6 100644
--- a/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h
+++ b/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h
@@ -43,7 +43,7 @@
     VEHICLE_NETWORK_AUDIO_HELPER_STREAM_3 = 0x8,
 };
 
-#define FOCUS_WAIT_DEFAULT_TIMEOUT_NS 500000000
+#define FOCUS_WAIT_DEFAULT_TIMEOUT_NS 1000000000
 
 /**
  * Create helper instance with default timeout. Timer is reset when
diff --git a/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp b/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp
index 14d7cae..75b6f6b 100644
--- a/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp
+++ b/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp
@@ -55,7 +55,7 @@
     mScratchValueStreamState.value_type = VEHICLE_VALUE_TYPE_INT32_VEC2;
     mScratchValueStreamState.timestamp = 0;
     mScratchValueFocus.prop = VEHICLE_PROPERTY_AUDIO_FOCUS;
-    mScratchValueFocus.value_type = VEHICLE_VALUE_TYPE_INT32_VEC3;
+    mScratchValueFocus.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4;
     mScratchValueFocus.timestamp = 0;
     updatePropertiesLocked();
     return NO_ERROR;
diff --git a/libvehiclenetwork/tool/vehiclehal_code_gen.py b/libvehiclenetwork/tool/vehiclehal_code_gen.py
index d96a87e..e35654f 100755
--- a/libvehiclenetwork/tool/vehiclehal_code_gen.py
+++ b/libvehiclenetwork/tool/vehiclehal_code_gen.py
@@ -37,7 +37,8 @@
  * limitations under the License.
  */
 
-//Autogenerated from vehicle.h. Do not modify manually.
+//Autogenerated from vehicle.h using libvehiclenetwork/tool/vehicle_code_gen.py.
+//Do not modify manually.
 
 package com.android.car.vehiclenetwork;
 
diff --git a/service/Android.mk b/service/Android.mk
index f0dd68d..8731ccb 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -16,6 +16,9 @@
 
 # Build the Car service.
 
+#disble build in PDK, missing aidl import breaks build
+ifneq ($(TARGET_BUILD_PDK),true)
+
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
@@ -34,6 +37,8 @@
 LOCAL_JAVA_LIBRARIES += android.car
 LOCAL_STATIC_JAVA_LIBRARIES += libvehiclenetwork-java car-systemtest
 
+LOCAL_JNI_SHARED_LIBRARIES := libjni_car_service
+
 include $(BUILD_PACKAGE)
 
 #####################################################################################
@@ -53,3 +58,4 @@
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
 
+endif #TARGET_BUILD_PDK
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 32647a3..659094a 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -27,7 +27,12 @@
         android:icon="@drawable/car_ic_mode"
         android:description="@string/car_permission_desc"
         android:label="@string/car_permission_label" />
-
+    <permission
+        android:name="android.car.permission.CAR_CAMERA"
+        android:permissionGroup="android.car.permission.CAR_INFORMATION"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_camera"
+        android:description="@string/car_permission_desc_camera" />
     <permission
         android:name="android.car.permission.CAR_FUEL"
         android:permissionGroup="android.car.permission.CAR_INFORMATION"
@@ -85,6 +90,7 @@
     <uses-permission android:name="android.permission.REBOOT" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
     <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
     <application android:label="Car service"
             android:allowBackup="false"
diff --git a/service/jni/Android.mk b/service/jni/Android.mk
new file mode 100644
index 0000000..ed29126
--- /dev/null
+++ b/service/jni/Android.mk
@@ -0,0 +1,39 @@
+# 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.
+#
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+    find . -name "*.cpp" -and -not -name ".*"))
+
+LOCAL_C_INCLUDES += \
+    system/core/include
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libnativehelper \
+    libutils \
+    libhardware
+
+LOCAL_CFLAGS := \
+    -Wno-unused-parameter \
+
+LOCAL_MODULE := libjni_car_service
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/service/jni/JniInit.cpp b/service/jni/JniInit.cpp
new file mode 100644
index 0000000..751a5e6
--- /dev/null
+++ b/service/jni/JniInit.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CAR.JNI"
+
+#include <utils/Log.h>
+#include <jni.h>
+
+namespace android {
+extern int register_com_android_car_CarCameraService(JNIEnv *env);
+extern int register_com_android_car_CarInputService(JNIEnv *env);
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+
+    int r = android::register_com_android_car_CarCameraService(env);
+    if (r != 0) {
+        ALOGE("register_com_android_car_CarCameraService failed %d", r);
+        return JNI_ERR;
+    }
+    r = android::register_com_android_car_CarInputService(env);
+    if (r != 0) {
+        ALOGE("register_com_android_car_CarInputService failed %d", r);
+        return JNI_ERR;
+    }
+    return JNI_VERSION_1_6;
+}
diff --git a/service/jni/com_android_car_CarCameraService.cpp b/service/jni/com_android_car_CarCameraService.cpp
new file mode 100644
index 0000000..7306702
--- /dev/null
+++ b/service/jni/com_android_car_CarCameraService.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+#include <cutils/log.h>
+#include <hardware/hardware.h>
+#include <hardware/vehicle_camera.h>
+#include <jni.h>
+#include <JNIHelp.h>
+#include <system/window.h>
+
+namespace android {
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeOpen
+ * Signature: ()J
+ */
+static jlong com_android_car_CarCameraService_nativeOpen
+  (JNIEnv *env, jobject obj)
+{
+    vehicle_camera_module_t *module = NULL;
+
+    hw_get_module(VEHICLE_CAMERA_HARDWARE_MODULE_ID, (const hw_module_t**)&module);
+
+    if (module == NULL) {
+        ALOGE("JNI Camera:  nativeOpen failed!");
+    }
+    return (jlong)module;
+}
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeClose
+ * Signature: (J)V
+ */
+static void com_android_car_CarCameraService_nativeClose
+  (JNIEnv *env, jobject, jlong dev)
+{
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+
+    if (device != NULL) {
+        device->common.close((struct hw_device_t*)device);
+    }
+}
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeGetSupportedCameras
+ * Signature: (J)[I
+ */
+static jintArray com_android_car_CarCameraService_nativeGetSupportedCameras
+  (JNIEnv *env, jobject, jlong mod)
+{
+    int i;
+    vehicle_camera_module_t *module = (vehicle_camera_module_t*)mod;
+    const uint32_t *camera_list;
+    uint32_t numCameras;
+    jintArray outArray = NULL;
+
+    camera_list = module->get_camera_device_list(&numCameras);
+
+    if ((numCameras > 0) && (camera_list != NULL)) {
+        outArray = env->NewIntArray(numCameras);
+    }
+
+    if (outArray == NULL) {
+        return NULL;
+    }
+    // Copy camera list to output array
+    env->SetIntArrayRegion(outArray, 0, numCameras, (jint*)camera_list);
+    return outArray;
+}
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeGetDevice
+ * Signature: (JI)J
+ */
+static jlong com_android_car_CarCameraService_nativeGetDevice
+  (JNIEnv *env, jobject, jlong mod, jint cameraType)
+{
+    const char *cameraTypeString[] = {
+        VEHICLE_CAMERA_RVC_DEVICE,
+    };
+    vehicle_camera_module_t *module = (vehicle_camera_module_t*)mod;
+    hw_device_t *device = NULL;
+
+    if (module != NULL) {
+        module->common.methods->open((hw_module_t*)module, cameraTypeString[cameraType], &device);
+    }
+
+    if (device == NULL) {
+        ALOGE("JNI Camera:  nativeGetDevice failed!");
+    }
+    return (jlong)device;
+}
+
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeGetCapabilities
+ * Signature: (J)I
+ */
+static jint com_android_car_CarCameraService_nativeGetCapabilities
+  (JNIEnv *env, jobject, jlong dev)
+{
+    vehicle_camera_cap_t cap;
+    jint capabilities = 0;
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+    if (device != NULL) {
+        // Fetch the capabilities
+        int errCode = device->get_capabilities(device, &cap);
+
+        if (errCode == 0) {
+            capabilities = cap.capabilites_flags;
+        } else {
+            ALOGE("JNI Camera:  nativeGetCapabilites errCode = %d", errCode);
+        }
+    } else {
+        ALOGE("JNI Camera:  nativeGetCapabilities!");
+    }
+
+    return capabilities;
+}
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeGetCameraCrop
+ * Signature: (J)Landroid/graphics/Rect;
+ */
+static jobject com_android_car_CarCameraService_nativeGetCameraCrop
+  (JNIEnv *env, jobject, jlong dev)
+{
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+    jobject retObj = NULL;
+
+    if (device != NULL) {
+        // Get the crop values
+        android_native_rect_t rect;
+        device->get_camera_crop(device, &rect);
+
+        // Get the class
+        jclass cls = env->FindClass("android/graphics/Rect");
+        jmethodID midInit = env->GetMethodID(cls, "<init>", "(IIII)V");
+
+        // Instantiate a new java CarRect objected with the current values
+        retObj = env->NewObject(cls, midInit, rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    return retObj;
+}
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeSetCameraCrop
+ * Signature: (JLandroid/graphics/Rect;)V
+ */
+static void com_android_car_CarCameraService_nativeSetCameraCrop
+  (JNIEnv *env, jobject, jlong dev, jobject jrect)
+{
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+    if (device != NULL) {
+        android_native_rect_t rect;
+
+        jclass   cls       = env->GetObjectClass(jrect);
+        jfieldID fidLeft   = env->GetFieldID(cls, "left",   "I");
+        jfieldID fidTop    = env->GetFieldID(cls, "top",    "I");
+        jfieldID fidRight  = env->GetFieldID(cls, "right",  "I");
+        jfieldID fidBottom = env->GetFieldID(cls, "bottom", "I");
+
+        rect.left   = (uint32_t)env->GetIntField(jrect, fidLeft);
+        rect.top    = (uint32_t)env->GetIntField(jrect, fidTop);
+        rect.right  = (uint32_t)env->GetIntField(jrect, fidRight);
+        rect.bottom = (uint32_t)env->GetIntField(jrect, fidBottom);
+
+        device->set_camera_crop(device, &rect);
+    }
+}
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeGetCameraPosition
+ * Signature: (J)Landroid/graphics/Rect;
+ */
+static jobject com_android_car_CarCameraService_nativeGetCameraPosition
+  (JNIEnv *env, jobject, jlong dev)
+{
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+    jobject retObj = NULL;
+
+    if (device != NULL) {
+        // Get the position values
+        android_native_rect_t rect;
+        device->get_camera_position(device, &rect);
+
+        // Get the class
+        jclass cls = env->FindClass("android/graphics/Rect");
+        jmethodID midInit = env->GetMethodID(cls, "<init>", "(IIII)V");
+
+        // Instantiate a new java CarRect objected with the current values
+        retObj = env->NewObject(cls, midInit, rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    return retObj;
+}
+
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeSetCameraPosition
+ * Signature: (JLandroid/graphics/Rect;)V
+ */
+static void com_android_car_CarCameraService_nativeSetCameraPosition
+  (JNIEnv *env, jobject, jlong dev, jobject jrect)
+{
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+    if (device != NULL) {
+        android_native_rect_t rect;
+
+        jclass   cls       = env->GetObjectClass(jrect);
+        jfieldID fidLeft   = env->GetFieldID(cls, "left",   "I");
+        jfieldID fidTop    = env->GetFieldID(cls, "top",    "I");
+        jfieldID fidRight  = env->GetFieldID(cls, "right",  "I");
+        jfieldID fidBottom = env->GetFieldID(cls, "bottom", "I");
+
+        rect.left   = (uint32_t)env->GetIntField(jrect, fidLeft);
+        rect.top    = (uint32_t)env->GetIntField(jrect, fidTop);
+        rect.right  = (uint32_t)env->GetIntField(jrect, fidRight);
+        rect.bottom = (uint32_t)env->GetIntField(jrect, fidBottom);
+
+        device->set_camera_position(device, &rect);
+    }
+}
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeGetCameraState
+ * Signature: (J)Landroid/car/hardware/camera/CarCameraState;
+ */
+static jobject com_android_car_CarCameraService_nativeGetCameraState
+  (JNIEnv *env, jobject, jlong dev)
+{
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+    jobject retObj = NULL;
+
+    if (device != NULL) {
+        vehicle_camera_state_t state;
+        device->get_camera_state(device, &state);
+
+        // Get the class
+        jclass cls = env->FindClass("android/car/hardware/camera/CarCameraState");
+        jmethodID midInit = env->GetMethodID(cls, "<init>", "(ZZ)V");
+
+        // Instantiate a new java CarRect objected with the current values
+        retObj = env->NewObject(cls, midInit, state.overlay_on, state.camera_on);
+    }
+
+    return retObj;
+}
+
+
+/*
+ * Class:     com_android_car_CarCameraService
+ * Method:    nativeSetCameraState
+ * Signature: (JLandroid/car/hardware/camera/CarCameraState;)V
+ */
+static void com_android_car_CarCameraService_nativeSetCameraState
+  (JNIEnv *env, jobject, jlong dev, jobject jstate)
+{
+    vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+    if (device != NULL) {
+        vehicle_camera_state_t state;
+
+        jclass cls = env->GetObjectClass(jstate);
+        jmethodID midCamera  = env->GetMethodID(cls, "getCameraIsOn",  "()Z");
+        jmethodID midOverlay = env->GetMethodID(cls, "getOverlayIsOn", "()Z");
+
+        state.overlay_on = (uint32_t)env->CallBooleanMethod(jstate, midOverlay);
+        state.camera_on  = (uint32_t)env->CallBooleanMethod(jstate, midCamera);
+
+        device->set_camera_state(device, &state);
+    }
+}
+
+static JNINativeMethod gMethods[] = {
+    { "nativeOpen", "()J", (void*)com_android_car_CarCameraService_nativeOpen },
+    { "nativeClose", "(J)V", (void*)com_android_car_CarCameraService_nativeClose },
+    { "nativeGetSupportedCameras", "(J)[I", (void*)com_android_car_CarCameraService_nativeGetSupportedCameras },
+    { "nativeGetDevice", "(JI)J", (void*)com_android_car_CarCameraService_nativeGetDevice },
+    { "nativeGetCapabilities", "(J)I", (void*)com_android_car_CarCameraService_nativeGetCapabilities },
+    { "nativeGetCameraCrop", "(J)Landroid/graphics/Rect;", (void*)com_android_car_CarCameraService_nativeGetCameraCrop },
+    { "nativeSetCameraCrop", "(JLandroid/graphics/Rect;)V", (void*)com_android_car_CarCameraService_nativeSetCameraCrop },
+    { "nativeGetCameraPosition", "(J)Landroid/graphics/Rect;", (void*)com_android_car_CarCameraService_nativeGetCameraPosition },
+    { "nativeSetCameraPosition", "(JLandroid/graphics/Rect;)V", (void*)com_android_car_CarCameraService_nativeSetCameraPosition },
+    { "nativeGetCameraState", "(J)Landroid/car/hardware/camera/CarCameraState;", (void*)com_android_car_CarCameraService_nativeGetCameraState },
+    { "nativeSetCameraState", "(JLandroid/car/hardware/camera/CarCameraState;)V", (void*)com_android_car_CarCameraService_nativeSetCameraState },
+};
+
+int register_com_android_car_CarCameraService(JNIEnv *env) {
+    return jniRegisterNativeMethods(env, "com/android/car/CarCameraService",
+            gMethods, NELEM(gMethods));
+}
+
+} // namespace android
+
+
diff --git a/service/jni/com_android_car_CarInputService.cpp b/service/jni/com_android_car_CarInputService.cpp
new file mode 100644
index 0000000..1b02c12
--- /dev/null
+++ b/service/jni/com_android_car_CarInputService.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "CAR.INPUT"
+
+#include <string.h>
+#include <sys/time.h>
+#include <linux/input.h>
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android/keycodes.h>
+#include <cutils/log.h>
+#include <utils/Errors.h>
+
+
+namespace android {
+
+static int androidKeyCodeToLinuxKeyCode(int androidKeyCode) {
+    switch (androidKeyCode) {
+    case AKEYCODE_VOLUME_UP:
+        return KEY_VOLUMEUP;
+    case AKEYCODE_VOLUME_DOWN:
+        return KEY_VOLUMEDOWN;
+    case AKEYCODE_CALL:
+        return KEY_SEND;
+    case AKEYCODE_ENDCALL:
+        return KEY_END;
+    /* TODO add more keys like these:
+    case AKEYCODE_MEDIA_PLAY_PAUSE:
+    case AKEYCODE_MEDIA_STOP:
+    case AKEYCODE_MEDIA_NEXT:
+    case AKEYCODE_MEDIA_PREVIOUS:*/
+    case AKEYCODE_VOICE_ASSIST:
+        return KEY_MICMUTE;
+    default:
+        ALOGW("Unmapped android key code %d dropped", androidKeyCode);
+        return 0;
+    }
+}
+
+/*
+ * Class:     com_android_car_CarInputService
+ * Method:    nativeInjectKeyEvent
+ * Signature: (IIZ)I
+ */
+static jint com_android_car_CarInputService_nativeInjectKeyEvent
+  (JNIEnv *env, jobject /*object*/, jint fd, jint keyCode, jboolean down) {
+    int linuxKeyCode = androidKeyCodeToLinuxKeyCode(keyCode);
+    if (linuxKeyCode == 0) {
+        return BAD_VALUE;
+    }
+    struct input_event ev[2];
+    memset(reinterpret_cast<void*>(&ev), 0, sizeof(ev));
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    // kernel driver is not using time now, but set it to be safe.
+    ev[0].time = now;
+    ev[0].type = EV_KEY;
+    ev[0].code = linuxKeyCode;
+    ev[0].value = (down ? 1 : 0);
+    // force delivery and flushing
+    ev[1].time = now;
+    ev[1].type = EV_SYN;
+    ev[1].code = SYN_REPORT;
+    ev[1].value = 0;
+    ALOGI("injectKeyEvent down %d keyCode %d, value %d", down, ev[0].code, ev[0].value);
+    int r = write(fd, reinterpret_cast<void*>(&ev), sizeof(ev));
+    if (r != sizeof(ev)) {
+        return -EIO;
+    }
+    return 0;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "nativeInjectKeyEvent", "(IIZ)I",
+            (void*)com_android_car_CarInputService_nativeInjectKeyEvent },
+};
+
+int register_com_android_car_CarInputService(JNIEnv *env) {
+    return jniRegisterNativeMethods(env, "com/android/car/CarInputService",
+            gMethods, NELEM(gMethods));
+}
+
+} // namespace android
diff --git a/service/res/layout/instrument_cluster.xml b/service/res/layout/instrument_cluster.xml
new file mode 100644
index 0000000..1b5387b
--- /dev/null
+++ b/service/res/layout/instrument_cluster.xml
@@ -0,0 +1,22 @@
+<?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:orientation="horizontal"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" android:id="@+id/instrument_cluster_layout"
+              android:theme="@android:style/Theme.Material"
+              android:background="@android:color/background_dark">
+</FrameLayout>
\ No newline at end of file
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 165688d..24ef730 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -43,6 +43,10 @@
         <item>"0:call,media,unknown#1:nav_guidance,voice_command,alarm,notification,system,safety"</item>
     </string-array>
 
+    <!-- This is kernel device node to allow input event injection for key inputs coming
+         from vehicle hal -->
+    <string name="inputInjectionDeviceNode">/dev/input/event2</string>
+
     <string name="instrumentClusterRendererPackage">android.car.cluster.demorenderer</string>
     <string name="instrumentClusterRendererFactoryClass">android.car.cluster.demorenderer.InstrumentClusterRendererFactory</string>
 </resources>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index 4606f21..d2ee58a 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -19,14 +19,19 @@
     <string name="car_permission_label">Car information</string>
     <!-- Permission text: can access your car's information [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc">Access your car\'s information.</string>
+    <!-- Permission text: apps can control car camera [CHAR LIMIT=NONE] -->
+    <string name="car_permission_label_camera">Car Camera</string>
+    <!-- Permission text: apps can control car amera [CHAR LIMIT=NONE] -->
+    <string name="car_permission_desc_camera">Access your car\'s camera(s).</string>
     <!-- Permission text: can access your car's fuel level [CHAR LIMIT=NONE] -->
     <string name="car_permission_label_fuel">Car fuel level</string>
     <!-- Permission text: can access your car's fuel level [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc_fuel">Access your car\'s fuel level information.</string>
-    <!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
+    <!-- Permission text: apps can control car hvac [CHAR LIMIT=NONE] -->
     <string name="car_permission_label_hvac">Car Hvac</string>
     <!-- Permission text: apps can control car hvac [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc_hvac">Access your car\'s hvac.</string>
+    <!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
     <string name="car_permission_label_mileage">Car mileage</string>
     <!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc_mileage">Access your car\'s mileage information.</string>
diff --git a/service/src/com/android/car/AppContextService.java b/service/src/com/android/car/AppContextService.java
index 71bd539..edc10bd 100644
--- a/service/src/com/android/car/AppContextService.java
+++ b/service/src/com/android/car/AppContextService.java
@@ -41,7 +41,6 @@
     /** K: context flag, V: client owning it */
     private final HashMap<Integer, ClientInfo> mContextOwners = new HashMap<>();
     private int mActiveContexts;
-    private boolean mCallActive;
 
     private final HandlerThread mHandlerThread;
     private final DispatchHandler mDispatchHandler;
@@ -112,6 +111,8 @@
                 if ((c & contexts) != 0 && (c & alreadyOwnedContexts) == 0) {
                     ClientInfo ownerInfo = mContextOwners.get(c);
                     if (ownerInfo != null && ownerInfo != info) {
+                        //TODO check if current owner is having fore-ground activity. If yes,
+                        //reject request. Always grant if requestor is fore-ground activity.
                         ownerInfo.setOwnedContexts(ownerInfo.getOwnedContexts() & ~c);
                         mDispatchHandler.requestAppContextOwnershipLossDispatch(
                                 ownerInfo.binderInterface, c);
@@ -132,7 +133,6 @@
                     Log.i(CarLog.TAG_APP_CONTEXT, "setting context " +
                             Integer.toHexString(addedContexts) + "," + info.toString());
                 }
-                mDispatchHandler.requestHalNotification(mActiveContexts, mCallActive);
                 for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
                     mAllClients.getInterfaces()) {
                     ClientInfo clientInfo = (ClientInfo) client;
@@ -176,7 +176,6 @@
                     Log.i(CarLog.TAG_APP_CONTEXT, "resetting context " +
                             Integer.toHexString(removedContexts) + "," + info.toString());
                 }
-                mDispatchHandler.requestHalNotification(mActiveContexts, mCallActive);
                 for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
                     mAllClients.getInterfaces()) {
                     ClientInfo clientInfo = (ClientInfo) client;
@@ -201,17 +200,6 @@
             mAllClients.clear();
             mContextOwners.clear();
             mActiveContexts = 0;
-            mCallActive = false;
-        }
-    }
-
-    public void handleCallStateChange(boolean callActive) {
-        synchronized (this) {
-            if (callActive == mCallActive) { // no change
-                return;
-            }
-            mCallActive = callActive;
-            mDispatchHandler.requestHalNotification(mActiveContexts, mCallActive);
         }
     }
 
@@ -229,8 +217,7 @@
     public void dump(PrintWriter writer) {
         writer.println("**AppContextService**");
         synchronized (this) {
-            writer.println("mActiveContexts:" + Integer.toHexString(mActiveContexts) +
-                    ",mCallState:" + mCallActive);
+            writer.println("mActiveContexts:" + Integer.toHexString(mActiveContexts));
             for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
                 mAllClients.getInterfaces()) {
                 ClientInfo clientInfo = (ClientInfo) client;
@@ -266,13 +253,6 @@
         }
     }
 
-    private void notifyAppContextChangeToHal(int contexts, boolean callState) {
-        VehicleHal.getInstance().updateAppContext(
-                (contexts & CarAppContextManager.APP_CONTEXT_NAVIGATION) != 0/*navigation*/,
-                (contexts & CarAppContextManager.APP_CONTEXT_VOICE_COMMAND) != 0/*voice*/,
-                callState /*callActive*/);
-    }
-
     private static class ClientHolder extends BinderInterfaceContainer<IAppContextListener> {
         private ClientHolder(AppContextService service) {
             super(service);
@@ -331,7 +311,6 @@
     private class DispatchHandler extends Handler {
         private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0;
         private static final int MSG_DISPATCH_CONTEXT_CHANGE = 1;
-        private static final int MSG_NOTIFY_HAL = 2;
 
         private DispatchHandler(Looper looper) {
             super(looper);
@@ -348,11 +327,6 @@
             sendMessage(msg);
         }
 
-        private void requestHalNotification(int contexts, boolean callState) {
-            Message msg = obtainMessage(MSG_NOTIFY_HAL, contexts, callState ? 1 : 0);
-            sendMessage(msg);
-        }
-
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -362,9 +336,6 @@
                 case MSG_DISPATCH_CONTEXT_CHANGE:
                     dispatchAppContextChange((IAppContextListener) msg.obj, msg.arg1);
                     break;
-                case MSG_NOTIFY_HAL:
-                    notifyAppContextChangeToHal(msg.arg1, msg.arg2 == 1);
-                    break;
             }
         }
     }
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index 5bbad78..5019fb4 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -65,6 +65,9 @@
     private LinkedList<AudioFocusInfo> mPendingFocusChanges = new LinkedList<>();
     @GuardedBy("mLock")
     private AudioFocusInfo mTopFocusInfo = null;
+    /** previous top which may be in ducking state */
+    @GuardedBy("mLock")
+    private AudioFocusInfo mSecondFocusInfo = null;
 
     private AudioRoutingPolicy mAudioRoutingPolicy;
     private final AudioManager mAudioManager;
@@ -78,8 +81,8 @@
     private boolean mRadioActive = false;
     @GuardedBy("mLock")
     private boolean mCallActive = false;
-
-    private final AppContextService mAppContextService;
+    @GuardedBy("mLock")
+    private int mCurrentAudioContexts = 0;
 
     private final AudioAttributes mAttributeBottom =
             CarAudioAttributesUtil.getAudioAttributesForCarUsage(
@@ -88,7 +91,7 @@
             CarAudioAttributesUtil.getAudioAttributesForCarUsage(
                     CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY);
 
-    public CarAudioService(Context context, AppContextService appContextService) {
+    public CarAudioService(Context context) {
         mAudioHal = VehicleHal.getInstance().getAudioHal();
         mContext = context;
         mFocusHandlerThread = new HandlerThread(CarLog.TAG_AUDIO);
@@ -97,7 +100,6 @@
         mFocusHandler = new CarAudioFocusChangeHandler(mFocusHandlerThread.getLooper());
         mVolumeHandler = new CarAudioVolumeHandler(Looper.getMainLooper());
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        mAppContextService = appContextService;
     }
 
     @Override
@@ -125,6 +127,7 @@
                     mBottomFocusState = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
                 }
                 mCurrentFocusState = currentState;
+                mCurrentAudioContexts = 0;
             }
         }
         int r = mAudioManager.registerAudioPolicy(mAudioPolicy);
@@ -159,6 +162,7 @@
         writer.println("*CarAudioService*");
         writer.println(" mCurrentFocusState:" + mCurrentFocusState +
                 " mLastFocusRequestToCar:" + mLastFocusRequestToCar);
+        writer.println(" mCurrentAudioContexts:0x" + Integer.toHexString(mCurrentAudioContexts));
         writer.println(" mCallActive:" + mCallActive + " mRadioActive:" + mRadioActive);
         mAudioRoutingPolicy.dump(writer);
     }
@@ -426,16 +430,17 @@
         int logicalStreamTypeForTop = CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib);
         int physicalStreamTypeForTop = mAudioRoutingPolicy.getPhysicalStreamForLogicalStream(
                 logicalStreamTypeForTop);
+        int audioContexts = 0;
         if (logicalStreamTypeForTop == CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL) {
             if (!mCallActive) {
                 mCallActive = true;
-                mAppContextService.handleCallStateChange(mCallActive);
+                audioContexts |= AudioHalService.AUDIO_CONTEXT_CALL_FLAG;
             }
         } else {
             if (mCallActive) {
                 mCallActive = false;
-                mAppContextService.handleCallStateChange(mCallActive);
             }
+            audioContexts = AudioHalService.logicalStreamToHalContextType(logicalStreamTypeForTop);
         }
         // other apps having focus
         int focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
@@ -445,6 +450,8 @@
             case AudioManager.AUDIOFOCUS_GAIN:
                 if (isFocusFromRadio(mTopFocusInfo)) {
                     mRadioActive = true;
+                    // audio context sending is only for audio from android.
+                    audioContexts = 0;
                 } else {
                     mRadioActive = false;
                 }
@@ -457,6 +464,7 @@
                 focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
                 break;
             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
+                audioContexts |= getAudioContext(mSecondFocusInfo);
                 focusToRequest =
                     AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK;
                 switch (mCurrentFocusState.focusState) {
@@ -466,7 +474,7 @@
                         break;
                     case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT:
                         streamsToRequest |= mCurrentFocusState.streams;
-                        //TODO is there a need to change this to GAIN_TRANSIENT?
+                        focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
                         break;
                     case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS:
                     case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT:
@@ -482,29 +490,57 @@
                 break;
         }
         if (mRadioActive) {
-            extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
             // TODO any need to keep media stream while radio is active?
             //     Most cars do not allow that, but if mixing is possible, it can take media stream.
             //     For now, assume no mixing capability.
             int radioPhysicalStream = mAudioRoutingPolicy.getPhysicalStreamForLogicalStream(
                     CarAudioManager.CAR_AUDIO_USAGE_MUSIC);
-            streamsToRequest &= ~(0x1 << radioPhysicalStream);
+            if (!isFocusFromRadio(mTopFocusInfo) &&
+                    (physicalStreamTypeForTop == radioPhysicalStream)) {
+                Log.i(CarLog.TAG_AUDIO, "Top stream is taking the same stream:" +
+                    physicalStreamTypeForTop + " as radio, stopping radio");
+                // stream conflict here. radio cannot be played
+                extFocus = 0;
+                mRadioActive = false;
+                audioContexts &= ~AudioHalService.AUDIO_CONTEXT_RADIO_FLAG;
+            } else {
+                extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
+                streamsToRequest &= ~(0x1 << radioPhysicalStream);
+            }
         } else if (streamsToRequest == 0) {
+            mCurrentAudioContexts = 0;
             mFocusHandler.handleFocusReleaseRequest();
             return false;
         }
-        return sendFocusRequestToCarIfNecessaryLocked(focusToRequest, streamsToRequest, extFocus);
+        return sendFocusRequestToCarIfNecessaryLocked(focusToRequest, streamsToRequest, extFocus,
+                audioContexts);
+    }
+
+    private static int getAudioContext(AudioFocusInfo info) {
+        if (info == null) {
+            return 0;
+        }
+        AudioAttributes attrib = info.getAttributes();
+        if (attrib == null) {
+            return AudioHalService.AUDIO_CONTEXT_UNKNOWN_FLAG;
+        }
+        return AudioHalService.logicalStreamToHalContextType(
+                CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib));
     }
 
     private boolean sendFocusRequestToCarIfNecessaryLocked(int focusToRequest,
-            int streamsToRequest, int extFocus) {
-        if (needsToSendFocusRequestLocked(focusToRequest, streamsToRequest, extFocus)) {
+            int streamsToRequest, int extFocus, int audioContexts) {
+        if (needsToSendFocusRequestLocked(focusToRequest, streamsToRequest, extFocus,
+                audioContexts)) {
             mLastFocusRequestToCar = FocusRequest.create(focusToRequest, streamsToRequest,
                     extFocus);
+            mCurrentAudioContexts = audioContexts;
             if (DBG) {
-                Log.d(TAG_FOCUS, "focus request to car:" + mLastFocusRequestToCar);
+                Log.d(TAG_FOCUS, "focus request to car:" + mLastFocusRequestToCar + " context:0x" +
+                        Integer.toHexString(audioContexts));
             }
-            mAudioHal.requestAudioFocusChange(focusToRequest, streamsToRequest, extFocus);
+            mAudioHal.requestAudioFocusChange(focusToRequest, streamsToRequest, extFocus,
+                    audioContexts);
             try {
                 mLock.wait(FOCUS_RESPONSE_WAIT_TIMEOUT_MS);
             } catch (InterruptedException e) {
@@ -516,10 +552,13 @@
     }
 
     private boolean needsToSendFocusRequestLocked(int focusToRequest, int streamsToRequest,
-            int extFocus) {
+            int extFocus, int audioContexts) {
         if (streamsToRequest != mCurrentFocusState.streams) {
             return true;
         }
+        if (audioContexts != mCurrentAudioContexts) {
+            return true;
+        }
         if ((extFocus & mCurrentFocusState.externalFocus) != extFocus) {
             return true;
         }
@@ -578,6 +617,11 @@
             if (DBG) {
                 Log.d(TAG_FOCUS, "top focus changed to:" + dumpAudioFocusInfo(newTopInfo));
             }
+            if (newTopInfo.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) {
+                mSecondFocusInfo = mTopFocusInfo;
+            } else {
+                mSecondFocusInfo = null;
+            }
             mTopFocusInfo = newTopInfo;
             focusRequested = reevaluateCarAudioFocusLocked();
             if (DBG) {
@@ -591,6 +635,7 @@
                         mLastFocusRequestToCar);
                 // no response. so reset to loss.
                 mFocusReceived = FocusState.STATE_LOSS;
+                mCurrentAudioContexts = 0;
             }
         }
         // handle it if there was response or force handle it for timeout.
@@ -610,7 +655,7 @@
                 mLastFocusRequestToCar = FocusRequest.STATE_RELEASE;
                 sent = true;
                 mAudioHal.requestAudioFocusChange(
-                        AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, 0);
+                        AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, 0, 0);
                 try {
                     mLock.wait(FOCUS_RESPONSE_WAIT_TIMEOUT_MS);
                 } catch (InterruptedException e) {
diff --git a/service/src/com/android/car/CarCameraService.java b/service/src/com/android/car/CarCameraService.java
new file mode 100644
index 0000000..7d2608b
--- /dev/null
+++ b/service/src/com/android/car/CarCameraService.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import android.content.Context;
+import android.car.hardware.camera.CarCameraState;
+import android.car.hardware.camera.ICarCamera;
+import android.graphics.Rect;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Set;
+
+public class CarCameraService extends ICarCamera.Stub implements CarServiceBase {
+    public static final boolean DBG = false;
+    public static final String  TAG = CarLog.TAG_CAMERA + ".CarCameraService";
+
+    private final Context mContext;
+    private long mModule;
+    private final HashMap<Integer, Long> mDeviceMap;
+
+    public CarCameraService(Context context) {
+        mContext = context;
+        mDeviceMap = new HashMap<Integer, Long>();
+    }
+
+    @Override
+    public synchronized void init() {
+        if (DBG) {
+            Log.d(TAG, "init called");
+        }
+        mModule = nativeOpen();
+
+        if (mModule != 0) {
+            int[] cameraType = nativeGetSupportedCameras(mModule);
+
+            if (cameraType != null) {
+                for (int i : cameraType) {
+                    long devicePtr = nativeGetDevice(mModule, i);
+                    if (devicePtr == 0) {
+                        Log.e(TAG, "Null device pointer returned for cameraType = " + i);
+                    } else {
+                        mDeviceMap.put(i, devicePtr);
+                    }
+                }
+            } else {
+                Log.e(TAG, "No car cameras are supported");
+            }
+        } else {
+            Log.w(TAG, "Cannot load camera module");
+        }
+    }
+
+    @Override
+    public synchronized void release() {
+        if (DBG) {
+            Log.d(TAG, "release called");
+        }
+        Collection<Long> devices = mDeviceMap.values();
+        for (Long device : devices) {
+            nativeClose(device);
+        }
+        mDeviceMap.clear();
+        mModule = 0;
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        // TODO
+    }
+
+    @Override
+    public int[] getCameraList() {
+        if (DBG) {
+            Log.d(TAG, "getCameraList called");
+        }
+        Set<Integer> keySet;
+        synchronized (this) {
+            keySet = mDeviceMap.keySet();
+        }
+
+        int keySetSize = keySet.size();
+
+        if (keySetSize > 0) {
+            int[] keyArray = new int[keySet.size()];
+            int i = 0;
+            for (Integer key : keySet) {
+                keyArray[i++] = key.intValue();
+            }
+            return keyArray;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public int getCapabilities(int cameraType) {
+        if (DBG) {
+            Log.d(TAG, "getCapabilities called, type = " + String.valueOf(cameraType));
+        }
+        synchronized (this) {
+            long device = getDeviceIdLocked(cameraType);
+            return nativeGetCapabilities(device);
+        }
+    }
+
+    @Override
+    public Rect getCameraCrop(int cameraType) {
+        Rect rect;
+        synchronized (this) {
+            long device = getDeviceIdLocked(cameraType);
+            rect = nativeGetCameraCrop(device);
+        }
+        if(DBG && (rect != null)) {
+            Log.d(TAG, "getCameraCrop called:  " + rect.toString());
+        }
+        return rect;
+    }
+
+    @Override
+    public void setCameraCrop(int cameraType, Rect rect) {
+        if (DBG) {
+            Log.d(TAG, "setCameraCrop called." + rect.toString());
+        }
+        synchronized (this) {
+            long device = getDeviceIdLocked(cameraType);
+            nativeSetCameraCrop(device, rect);
+        }
+    }
+
+    @Override
+    public Rect getCameraPosition(int cameraType) {
+        Rect rect;
+        synchronized (this) {
+            long device = getDeviceIdLocked(cameraType);
+            rect = nativeGetCameraPosition(device);
+        }
+        if(DBG && (rect != null)) {
+            Log.d(TAG, "getCameraPosition called:  " + rect.toString());
+        }
+        return rect;
+    }
+
+    @Override
+    public void setCameraPosition(int cameraType, Rect rect) {
+        if (DBG) {
+            Log.d(TAG, "setCameraPosition called." + rect.toString());
+        }
+        synchronized (this) {
+            long device = getDeviceIdLocked(cameraType);
+            nativeSetCameraPosition(device, rect);
+        }
+    }
+
+    @Override
+    public CarCameraState getCameraState(int cameraType) {
+        CarCameraState state;
+        synchronized (this) {
+            long device = getDeviceIdLocked(cameraType);
+            state = nativeGetCameraState(device);
+        }
+        if(DBG && (state != null)) {
+            Log.d(TAG, "getCameraState called: " + state.toString());
+        }
+        return state;
+    }
+
+    @Override
+    public void setCameraState(int cameraType, CarCameraState state) {
+        if (DBG) {
+            Log.d(TAG, "setCameraState called.  state: " + state.toString());
+        }
+        synchronized (this) {
+            long device = getDeviceIdLocked(cameraType);
+            nativeSetCameraState(device, state);
+        }
+    }
+
+    /**
+     * Validates that the cameraType is available and ready to be used.
+     * @param cameraType
+     * @return
+     */
+    private long getDeviceIdLocked(int cameraType) {
+        Long deviceId = mDeviceMap.get(cameraType);
+
+        if (deviceId == null) {
+            throw new IllegalArgumentException("cameraType " + cameraType + " doesn't exist in"
+                    + "device map");
+        }
+        return deviceId;
+    }
+
+    /*
+     * Native function definitions
+     */
+    private native long nativeOpen();
+    private native void nativeClose(long module);
+    private native int[] nativeGetSupportedCameras(long module);
+    private native long nativeGetDevice(long module, int cameraType);
+    private native int nativeGetCapabilities(long device);
+    private native Rect nativeGetCameraCrop(long device);
+    private native void nativeSetCameraCrop(long device, Rect rect);
+    private native Rect nativeGetCameraPosition(long device);
+    private native void nativeSetCameraPosition(long device, Rect rect);
+    private native CarCameraState nativeGetCameraState(long device);
+    private native void nativeSetCameraState(long device, CarCameraState state);
+}
diff --git a/service/src/com/android/car/CarInputService.java b/service/src/com/android/car/CarInputService.java
new file mode 100644
index 0000000..8b0bd3d
--- /dev/null
+++ b/service/src/com/android/car/CarInputService.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.speech.RecognizerIntent;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import com.android.car.hal.InputHalService;
+import com.android.car.hal.VehicleHal;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class CarInputService implements CarServiceBase, InputHalService.InputListener {
+
+    public interface KeyEventListener {
+        void onKeyEvent(KeyEvent event);
+    }
+
+    private static final long VOICE_LONG_PRESS_TIME_MS = 1000;
+
+    private final Context mContext;
+
+    private KeyEventListener mVoiceAssitantKeyListener;
+    private KeyEventListener mLongVoiceAssitantKeyListener;
+    private long mLastVoiceKeyDownTime = 0;
+
+    private KeyEventListener mInstumentClusterKeyListener;
+
+    private ParcelFileDescriptor mInjectionDeviceFd;
+
+    private int mKeyEventCount = 0;
+
+    public CarInputService(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Set listener for listening voice assistant key event. Setting to null stops listening.
+     * If listener is not set, default behavior will be done for short press.
+     * If listener is set, short key press will lead into calling the listener.
+     * @param listener
+     */
+    public void setVoiceAssitantKeyListener(KeyEventListener listener) {
+        synchronized (this) {
+            mVoiceAssitantKeyListener = listener;
+        }
+    }
+
+    /**
+     * Set listener for listening long voice assistant key event. Setting to null stops listening.
+     * If listener is not set, default behavior will be done for long press.
+     * If listener is set, short long press will lead into calling the listener.
+     * @param listener
+     */
+    public void setLongVoiceAssitantKeyListener(KeyEventListener listener) {
+        synchronized (this) {
+            mLongVoiceAssitantKeyListener = listener;
+        }
+    }
+
+    public void setInstrumentClusterKeyListener(KeyEventListener listener) {
+        synchronized (this) {
+            mInstumentClusterKeyListener = listener;
+        }
+    }
+
+    @Override
+    public void init() {
+        InputHalService hal = VehicleHal.getInstance().getInputHal();
+        if (!hal.isKeyInputSupported()) {
+            Log.w(CarLog.TAG_INPUT, "Hal does not support key input.");
+            return;
+        }
+        String injectionDevice = mContext.getResources().getString(
+                R.string.inputInjectionDeviceNode);
+        ParcelFileDescriptor file = null;
+        try {
+            file = ParcelFileDescriptor.open(new File(injectionDevice),
+                    ParcelFileDescriptor.MODE_READ_WRITE);
+        } catch (FileNotFoundException e) {
+            Log.w(CarLog.TAG_INPUT, "cannot open device for input injection:" + injectionDevice);
+            return;
+        }
+        synchronized (this) {
+            mInjectionDeviceFd = file;
+        }
+        hal.setInputListener(this);
+    }
+
+    @Override
+    public void release() {
+        synchronized (this) {
+            mVoiceAssitantKeyListener = null;
+            mLongVoiceAssitantKeyListener = null;
+            mInstumentClusterKeyListener = null;
+            if (mInjectionDeviceFd != null) {
+                try {
+                    mInjectionDeviceFd.close();
+                } catch (IOException e) {
+                }
+            }
+            mInjectionDeviceFd = null;
+            mKeyEventCount = 0;
+        }
+    }
+
+    @Override
+    public void onKeyEvent(KeyEvent event, int targetDisplay) {
+        synchronized (this) {
+            mKeyEventCount++;
+        }
+        int keyCode = event.getKeyCode();
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOICE_ASSIST:
+                handleVoiceAssistKey(event);
+                return;
+            default:
+                break;
+        }
+        if (targetDisplay == InputHalService.DISPLAY_INSTRUMENT_CLUSTER) {
+            handleInstrumentClusterKey(event);
+        } else {
+            handleMainDisplayKey(event);
+        }
+    }
+
+    private void handleVoiceAssistKey(KeyEvent event) {
+        int action = event.getAction();
+        if (action == KeyEvent.ACTION_DOWN) {
+            long now = SystemClock.elapsedRealtime();
+            synchronized (this) {
+                mLastVoiceKeyDownTime = now;
+            }
+        } else if (action == KeyEvent.ACTION_UP) {
+            // if no listener, do not handle long press
+            KeyEventListener listener = null;
+            KeyEventListener shortPressListener = null;
+            KeyEventListener longPressListener = null;
+            long downTime;
+            synchronized (this) {
+                shortPressListener = mVoiceAssitantKeyListener;
+                longPressListener = mLongVoiceAssitantKeyListener;
+                downTime = mLastVoiceKeyDownTime;
+            }
+            if (shortPressListener == null && longPressListener == null) {
+                launchDefaultVoiceAssitantHandler();
+            } else {
+                long duration = SystemClock.elapsedRealtime() - downTime;
+                listener = (duration > VOICE_LONG_PRESS_TIME_MS
+                        ? longPressListener : shortPressListener);
+                if (listener != null) {
+                    listener.onKeyEvent(event);
+                } else {
+                    launchDefaultVoiceAssitantHandler();
+                }
+            }
+        }
+    }
+
+    private void launchDefaultVoiceAssitantHandler() {
+        Log.i(CarLog.TAG_INPUT, "voice key, launch default intent");
+        Intent voiceIntent =
+                new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+        mContext.startActivityAsUser(voiceIntent, null, UserHandle.CURRENT_OR_SELF);
+    }
+
+    private void handleInstrumentClusterKey(KeyEvent event) {
+        KeyEventListener listener = null;
+        synchronized (this) {
+            listener = mInstumentClusterKeyListener;
+        }
+        if (listener == null) {
+            return;
+        }
+        listener.onKeyEvent(event);
+    }
+
+    private void handleMainDisplayKey(KeyEvent event) {
+        int fd;
+        synchronized (this) {
+            fd = mInjectionDeviceFd.getFd();
+        }
+        int action = event.getAction();
+        boolean isDown = (action == KeyEvent.ACTION_DOWN);
+        int keyCode = event.getKeyCode();
+        int r = nativeInjectKeyEvent(fd, keyCode, isDown);
+        if (r != 0) {
+            Log.e(CarLog.TAG_INPUT, "cannot inject key event, failed with:" + r);
+        }
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        writer.println("*Input Service*");
+        writer.println("mInjectionDeviceFd:" + mInjectionDeviceFd);
+        writer.println("mLastVoiceKeyDownTime:" + mLastVoiceKeyDownTime +
+                ",mKeyEventCount:" + mKeyEventCount);
+    }
+
+    private native int nativeInjectKeyEvent(int fd, int keyCode, boolean isDown);
+}
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index 551b9a7..58cf058 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -19,6 +19,7 @@
 public class CarLog {
     public static final String TAG_APP_CONTEXT = "CAR.APP_CONTEXT";
     public static final String TAG_AUDIO = "CAR.AUDIO";
+    public static final String TAG_CAMERA = "CAR.CAMERA";
     public static final String TAG_HAL = "CAR.HAL";
     public static final String TAG_HVAC = "CAR.HVAC";
     public static final String TAG_INFO = "CAR.INFO";
@@ -29,4 +30,6 @@
     public static final String TAG_SERVICE = "CAR.SERVICE";
     public static final String TAG_NAV = "CAR.NAV";
     public static final String TAG_TEST = "CAR.TEST";
+    public static final String TAG_INPUT = "CAR.INPUT";
+    public static final String TAG_CLUSTER = "CAR.CLUSTER";
 }
diff --git a/service/src/com/android/car/CarSensorService.java b/service/src/com/android/car/CarSensorService.java
index 992c033..39dc341 100644
--- a/service/src/com/android/car/CarSensorService.java
+++ b/service/src/com/android/car/CarSensorService.java
@@ -375,13 +375,8 @@
 
     private boolean sensorSupportRate(int sensorType) {
         switch (sensorType) {
-            case CarSensorManager.SENSOR_TYPE_COMPASS:
             case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
             case CarSensorManager.SENSOR_TYPE_RPM:
-            case CarSensorManager.SENSOR_TYPE_LOCATION:
-            case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
-            case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE:
-            case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
                 return true;
             case CarSensorManager.SENSOR_TYPE_ODOMETER:
             case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
@@ -415,9 +410,6 @@
         }
         String permission = null;
         switch (sensorType) {
-            case CarSensorManager.SENSOR_TYPE_LOCATION:
-                permission = Manifest.permission.ACCESS_FINE_LOCATION;
-                break;
             case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
                 permission = Car.PERMISSION_SPEED;
                 break;
@@ -958,11 +950,7 @@
                     if (record != null && record.lastEvent != null) {
                         writer.println("sensor: " + sensor
                                 + " active: " + record.enabled);
-                        // only print sensor data which is not related with location
-                        if (sensor != CarSensorManager.SENSOR_TYPE_LOCATION &&
-                                sensor != CarSensorManager.SENSOR_TYPE_GPS_SATELLITE) {
-                            writer.println(" " + record.lastEvent.toString());
-                        }
+                        writer.println(" " + record.lastEvent.toString());
                     }
                     SensorListeners listeners = mSensorListeners.get(sensor);
                     if (listeners != null) {
diff --git a/service/src/com/android/car/CarTestService.java b/service/src/com/android/car/CarTestService.java
index 2752e30..1a983c7 100644
--- a/service/src/com/android/car/CarTestService.java
+++ b/service/src/com/android/car/CarTestService.java
@@ -23,6 +23,7 @@
 import com.android.car.hal.VehicleHal;
 import com.android.car.vehiclenetwork.IVehicleNetworkHalMock;
 import com.android.car.vehiclenetwork.VehicleNetwork;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs;
 import com.android.car.vehiclenetwork.VehiclePropValueParcelable;
 
 import java.io.PrintWriter;
@@ -126,6 +127,13 @@
         });
     }
 
+    @Override
+    public boolean isPropertySupported(int property) {
+        VehiclePropConfigs configs = VehicleHal.getInstance().getVehicleNetwork().listProperties(
+                property);
+        return configs != null;
+    }
+
     public synchronized boolean isInMocking() {
         return mInMocking;
     }
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 2166eff..7745a82 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -18,22 +18,29 @@
 
 import android.car.Car;
 import android.car.ICar;
-import android.car.ICarConnectionListener;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.car.cluster.CarNavigationService;
+import com.android.car.cluster.InstrumentClusterService;
+import com.android.car.cluster.MediaStatusService;
 import com.android.car.hal.VehicleHal;
 import com.android.car.pm.CarPackageManagerService;
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.Collection;
 
 public class ICarImpl extends ICar.Stub {
 
+    public static final String INTERNAL_INPUT_SERVICE =  "internal_input";
+
+    // load jni for all services here
+    static {
+        System.loadLibrary("jni_car_service");
+    }
+
     @GuardedBy("ICarImpl.class")
     private static ICarImpl sInstance = null;
 
@@ -41,26 +48,26 @@
     private final VehicleHal mHal;
 
     private final CarPowerManagementService mCarPowerManagementService;
+    private final CarPackageManagerService mCarPackageManagerService;
+    private final CarInputService mCarInputService;
     private final CarSensorService mCarSensorService;
     private final CarInfoService mCarInfoService;
     private final CarAudioService mCarAudioService;
+    private final CarCameraService mCarCameraService;
     private final CarHvacService mCarHvacService;
     private final CarRadioService mCarRadioService;
     private final CarNightService mCarNightService;
     private final AppContextService mAppContextService;
-    private final CarPackageManagerService mCarPackageManagerService;
     private final GarageModeService mGarageModeService;
-    private final CarNavigationService mCarNavigationStatusService;
+    private final CarNavigationService mCarNavigationService;
+    private final MediaStatusService mMediaStatusService;
+    private final InstrumentClusterService mInstrumentClusterService;
 
     /** Test only service. Populate it only when necessary. */
     @GuardedBy("this")
     private CarTestService mCarTestService;
     private final CarServiceBase[] mAllServices;
 
-    /** Holds connection listener from client. Only necessary for mocking. */
-    private final BinderInterfaceContainer<ICarConnectionListener> mCarConnectionListeners =
-            new BinderInterfaceContainer<>(null);
-
     public synchronized static ICarImpl getInstance(Context serviceContext) {
         if (sInstance == null) {
             sInstance = new ICarImpl(serviceContext);
@@ -81,30 +88,39 @@
         mContext = serviceContext;
         mHal = VehicleHal.getInstance();
         mCarPowerManagementService = new CarPowerManagementService(serviceContext);
+        mCarInputService = new CarInputService(serviceContext);
         mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService);
         mCarInfoService = new CarInfoService(serviceContext);
         mAppContextService = new AppContextService(serviceContext);
         mCarSensorService = new CarSensorService(serviceContext);
-        mCarAudioService = new CarAudioService(serviceContext, mAppContextService);
+        mCarAudioService = new CarAudioService(serviceContext);
         mCarHvacService = new CarHvacService(serviceContext);
         mCarRadioService = new CarRadioService(serviceContext);
+        mCarCameraService = new CarCameraService(serviceContext);
         mCarNightService = new CarNightService(serviceContext);
         mCarPackageManagerService = new CarPackageManagerService(serviceContext);
-        mCarNavigationStatusService = new CarNavigationService(serviceContext, mAppContextService);
+        mInstrumentClusterService = new InstrumentClusterService(serviceContext);
+        mMediaStatusService = new MediaStatusService(serviceContext, mInstrumentClusterService);
+        mCarNavigationService = new CarNavigationService(
+                serviceContext, mAppContextService, mInstrumentClusterService);
 
         // Be careful with order. Service depending on other service should be inited later.
         mAllServices = new CarServiceBase[] {
                 mCarPowerManagementService,
-                mGarageModeService,
                 mCarPackageManagerService,
+                mCarInputService,
+                mGarageModeService,
                 mCarInfoService,
                 mAppContextService,
                 mCarSensorService,
                 mCarAudioService,
                 mCarHvacService,
                 mCarRadioService,
+                mCarCameraService,
                 mCarNightService,
-                mCarNavigationStatusService,
+                mInstrumentClusterService,
+                mCarNavigationService,
+                mMediaStatusService
                 };
     }
 
@@ -115,7 +131,6 @@
     }
 
     private void release() {
-        mCarConnectionListeners.clear();
         // release done in opposite order from init
         for (int i = mAllServices.length - 1; i >= 0; i--) {
             mAllServices[i].release();
@@ -141,25 +156,6 @@
         for (CarServiceBase service: mAllServices) {
             service.init();
         }
-        // send disconnect event and connect event to all clients.
-        Collection<BinderInterfaceContainer.BinderInterface<ICarConnectionListener>>
-                connectionListeners = mCarConnectionListeners.getInterfaces();
-        for (BinderInterfaceContainer.BinderInterface<ICarConnectionListener> client :
-                connectionListeners) {
-            try {
-                client.binderInterface.onDisconnected();
-            } catch (RemoteException e) {
-                //ignore
-            }
-        }
-        for (BinderInterfaceContainer.BinderInterface<ICarConnectionListener> client :
-                connectionListeners) {
-            try {
-                client.binderInterface.onConnected();
-            } catch (RemoteException e) {
-                //ignore
-            }
-        }
     }
 
     @Override
@@ -175,6 +171,9 @@
                 return mAppContextService;
             case Car.PACKAGE_SERVICE:
                 return mCarPackageManagerService;
+            case Car.CAMERA_SERVICE:
+                assertCameraPermission(mContext);
+                return mCarCameraService;
             case Car.HVAC_SERVICE:
                 assertHvacPermission(mContext);
                 return mCarHvacService;
@@ -182,7 +181,7 @@
                 assertRadioPermission(mContext);
                 return mCarRadioService;
             case Car.CAR_NAVIGATION_SERVICE:
-                return mCarNavigationStatusService;
+                return mCarNavigationService;
             case Car.TEST_SERVICE: {
                 assertVehicleHalMockPermission(mContext);
                 synchronized (this) {
@@ -199,23 +198,23 @@
     }
 
     @Override
-    public boolean isConnectedToCar() {
-        return true; // always connected in embedded
-    }
-
-    @Override
-    public void registerCarConnectionListener(ICarConnectionListener listener) {
-        mCarConnectionListeners.addBinder(listener);
-        try {
-            listener.onConnected();
-        } catch (RemoteException e) {
-            //ignore
+    public int getCarConnectionType() {
+        if (!isInMocking()) {
+            return Car.CONNECTION_TYPE_EMBEDDED;
+        } else {
+            return Car.CONNECTION_TYPE_EMBEDDED_MOCKING;
         }
     }
 
-    @Override
-    public void unregisterCarConnectionListener(ICarConnectionListener listener) {
-        mCarConnectionListeners.removeBinder(listener);
+    public CarServiceBase getCarInternalService(String serviceName) {
+        switch (serviceName) {
+            case INTERNAL_INPUT_SERVICE:
+                return mCarInputService;
+            default:
+                Log.w(CarLog.TAG_SERVICE, "getCarInternalService for unknown service:" +
+                        serviceName);
+                return null;
+        }
     }
 
     /**
@@ -236,6 +235,14 @@
         }
     }
 
+    public static void assertCameraPermission(Context context) {
+        if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_CAMERA)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "requires " + Car.PERMISSION_CAR_CAMERA);
+        }
+    }
+
     public static void assertHvacPermission(Context context) {
         if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_HVAC)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/service/src/com/android/car/CarNavigationService.java b/service/src/com/android/car/cluster/CarNavigationService.java
similarity index 66%
rename from service/src/com/android/car/CarNavigationService.java
rename to service/src/com/android/car/cluster/CarNavigationService.java
index b061a84..597d10c 100644
--- a/service/src/com/android/car/CarNavigationService.java
+++ b/service/src/com/android/car/cluster/CarNavigationService.java
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.car;
+package com.android.car.cluster;
 
 import android.car.CarAppContextManager;
-import android.car.cluster.NavigationRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.car.navigation.CarNavigationManager;
 import android.car.navigation.ICarNavigation;
@@ -25,9 +25,16 @@
 import android.graphics.Bitmap;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.car.AppContextService;
+import com.android.car.CarLog;
+import com.android.car.CarServiceBase;
+import com.android.car.cluster.InstrumentClusterService.RendererInitializationListener;
+import com.android.car.cluster.renderer.ThreadSafeNavigationRenderer;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -36,20 +43,22 @@
  * Service that will push navigation event to navigation renderer in instrument cluster.
  */
 public class CarNavigationService extends ICarNavigation.Stub
-        implements CarServiceBase {
+        implements CarServiceBase, RendererInitializationListener {
     private static final String TAG = CarLog.TAG_NAV;
 
     private final List<CarNavigationEventListener> mListeners = new ArrayList<>();
-
-    private CarNavigationInstrumentCluster mInstrumentClusterInfo = null;
     private final AppContextService mAppContextService;
     private final Context mContext;
-    private final boolean mRendererAvailable;
+    private final InstrumentClusterService mInstrumentClusterService;
 
-    public CarNavigationService(Context context, AppContextService appContextService) {
+    private CarNavigationInstrumentCluster mInstrumentClusterInfo = null;
+    private volatile NavigationRenderer mNavigationRenderer;
+
+    public CarNavigationService(Context context, AppContextService appContextService,
+            InstrumentClusterService instrumentClusterService) {
         mContext = context;
         mAppContextService = appContextService;
-        mRendererAvailable = InstrumentClusterRendererLoader.isRendererAvailable(mContext);
+        mInstrumentClusterService = instrumentClusterService;
     }
 
     @Override
@@ -57,6 +66,7 @@
         Log.d(TAG, "init");
         // TODO: we need to obtain this infromation from CarInstrumentClusterService.
         mInstrumentClusterInfo = CarNavigationInstrumentCluster.createCluster(1000);
+        mInstrumentClusterService.registerListener(this);
     }
 
     @Override
@@ -64,21 +74,22 @@
         synchronized(mListeners) {
             mListeners.clear();
         }
+        mInstrumentClusterService.unregisterListener(this);
     }
 
     @Override
     public void sendNavigationStatus(int status) {
         Log.d(TAG, "sendNavigationStatus, status: " + status);
-        if (!checkRendererAvailable()) {
+        if (!isRendererAvailable()) {
             return;
         }
         verifyNavigationContextOwner();
 
         if (status == CarNavigationManager.STATUS_ACTIVE) {
-            getNavRenderer().onStartNavigation();
+            mNavigationRenderer.onStartNavigation();
         } else if (status == CarNavigationManager.STATUS_INACTIVE
                 || status == CarNavigationManager.STATUS_UNAVAILABLE) {
-            getNavRenderer().onStopNavigation();
+            mNavigationRenderer.onStopNavigation();
         } else {
             throw new IllegalArgumentException("Unknown navigation status: " + status);
         }
@@ -89,49 +100,47 @@
             int event, String road, int turnAngle, int turnNumber, Bitmap image, int turnSide) {
         Log.d(TAG, "sendNavigationTurnEvent, event:" + event + ", turnAngle: " + turnAngle + ", "
                 + "turnNumber: " + turnNumber + ", " + "turnSide: " + turnSide);
-        if (!checkRendererAvailable()) {
+        if (!isRendererAvailable()) {
             return;
         }
         verifyNavigationContextOwner();
 
-        getNavRenderer().onNextTurnChanged(event, road, turnAngle, turnNumber, image, turnSide);
+        mNavigationRenderer.onNextTurnChanged(event, road, turnAngle, turnNumber, image, turnSide);
     }
 
     @Override
     public void sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds) {
         Log.d(TAG, "sendNavigationTurnDistanceEvent, distanceMeters:" + distanceMeters + ", "
                 + "timeSeconds: " + timeSeconds);
-        if (!checkRendererAvailable()) {
+        if (!isRendererAvailable()) {
             return;
         }
         verifyNavigationContextOwner();
 
-        getNavRenderer().onNextTurnDistanceChanged(distanceMeters, timeSeconds);
+        mNavigationRenderer.onNextTurnDistanceChanged(distanceMeters, timeSeconds);
     }
 
     @Override
     public boolean registerEventListener(ICarNavigationEventListener listener) {
+        CarNavigationEventListener eventListener;
         synchronized(mListeners) {
-            if (findClientLocked(listener) == null) {
-                CarNavigationEventListener eventListener =
-                        new CarNavigationEventListener(listener);
-                try {
-                    listener.asBinder().linkToDeath(eventListener, 0);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Adding listener failed.");
-                    return false;
-                }
-                mListeners.add(eventListener);
-
-                // The new listener needs to be told the instrument cluster parameters.
-                try {
-                    // TODO: onStart and onStop methods might be triggered from vehicle HAL as well.
-                    eventListener.listener.onInstrumentClusterStart(mInstrumentClusterInfo);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "listener.onStart failed.");
-                    return false;
-                }
+            if (findClientLocked(listener) != null) {
+                return true;
             }
+
+            eventListener = new CarNavigationEventListener(listener);
+            try {
+                listener.asBinder().linkToDeath(eventListener, 0);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Adding listener failed.", e);
+                return false;
+            }
+            mListeners.add(eventListener);
+        }
+
+        // The new listener needs to be told the instrument cluster parameters.
+        if (isRendererAvailable()) {
+            return eventListener.onInstrumentClusterStart(mInstrumentClusterInfo);
         }
         return true;
     }
@@ -188,6 +197,19 @@
         return null;
     }
 
+    @Override
+    public void onRendererInitSucceeded() {
+        Log.d(TAG, "onRendererInitSucceeded");
+        mNavigationRenderer = ThreadSafeNavigationRenderer.createFor(
+                Looper.getMainLooper(),
+                mInstrumentClusterService.getNavigationRenderer());
+
+        if (isRendererAvailable()) {
+            for (CarNavigationEventListener listener : mListeners) {
+                listener.onInstrumentClusterStart(mInstrumentClusterInfo);
+            }
+        }
+    }
 
     private class CarNavigationEventListener implements IBinder.DeathRecipient {
         final ICarNavigationEventListener listener;
@@ -201,6 +223,17 @@
             listener.asBinder().unlinkToDeath(this, 0);
             removeClient(this);
         }
+
+        /** Returns true if event sent successfully */
+        public boolean onInstrumentClusterStart(CarNavigationInstrumentCluster clusterInfo) {
+            try {
+                listener.onInstrumentClusterStart(clusterInfo);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to call onInstrumentClusterStart for listener: " + listener, e);
+                return false;
+            }
+            return true;
+        }
     }
 
     @Override
@@ -208,15 +241,11 @@
         // TODO Auto-generated method stub
     }
 
-    private NavigationRenderer getNavRenderer() {
-        // TODO: implement InstrumentClusterService that will abstract cluster renderer.
-        return InstrumentClusterRendererLoader.getRenderer(mContext).getNavigationRenderer();
-    }
-
-    private boolean checkRendererAvailable() {
-        if (!mRendererAvailable) {
+    private boolean isRendererAvailable() {
+        boolean available = mNavigationRenderer != null;
+        if (!available) {
             Log.w(TAG, "Instrument cluster renderer is not available.");
         }
-        return mRendererAvailable;
+        return available;
     }
 }
diff --git a/service/src/com/android/car/cluster/InstrumentClusterPresentation.java b/service/src/com/android/car/cluster/InstrumentClusterPresentation.java
new file mode 100644
index 0000000..53c73f8
--- /dev/null
+++ b/service/src/com/android/car/cluster/InstrumentClusterPresentation.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.view.Display;
+import android.view.WindowManager;
+
+/**
+ * Presentation class.
+ */
+public class InstrumentClusterPresentation extends Presentation {
+    public InstrumentClusterPresentation(Context outerContext, Display display) {
+        super(outerContext, display);
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+    }
+}
diff --git a/service/src/com/android/car/InstrumentClusterRendererLoader.java b/service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
similarity index 70%
rename from service/src/com/android/car/InstrumentClusterRendererLoader.java
rename to service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
index c892cb6..e6742a6 100644
--- a/service/src/com/android/car/InstrumentClusterRendererLoader.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.car;
+package com.android.car.cluster;
 
-import android.car.cluster.InstrumentClusterRenderer;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.hardware.display.DisplayManager;
 import android.util.Log;
-import android.view.Display;
+
+import com.android.car.CarLog;
+import com.android.car.R;
 
 import dalvik.system.PathClassLoader;
 
@@ -36,64 +37,43 @@
 
     private final static String sCreateRendererMethod = "createRenderer";
 
-    private static InstrumentClusterRenderer sRenderer;
-    private static Object sync = new Object();
-
     /**
-     * Load {@link InstrumentClusterRenderer} from separate APK and cache it. Subsequent calls to
-     * this function will return cached reference to the renderer.
-     */
-    public static InstrumentClusterRenderer getRenderer(Context context) {
-        synchronized (sync) {
-            if (sRenderer == null) {
-                Display display = getInstrumentClusterDisplay(context);
-                if (display != null) {
-                    createRenderer(context, display);
-                } else {
-                    Log.e(TAG, "Instrument cluster display not found.");
-                }
-            }
-        }
-        return sRenderer;
-    }
-
-    /**
-     * Returns true if instrument cluster renderer installed and secondary display is available.
+     * Returns true if instrument cluster renderer installed.
      */
     public static boolean isRendererAvailable(Context context) {
         PackageManager packageManager = context.getPackageManager();
         try {
             packageManager.getPackageInfo(getRendererPackageName(context), 0);
-            return getInstrumentClusterDisplay(context) != null;
+            return true;
         } catch (NameNotFoundException e) {
             return false;
         }
     }
 
-    /** To prevent instantiation of a singleton class. */
-    private InstrumentClusterRendererLoader() {}
-
-    private static void createRenderer(Context context, Display display) {
-        String packageName = getRendererPackageName(context);
-
+    /** Dynamically load renderer APK and creates {@link InstrumentClusterRenderer}. */
+    public static InstrumentClusterRenderer createRenderer(Context context) {
+        final String packageName = getRendererPackageName(context);
         try {
-            sRenderer = load(context, packageName, getRendererFactoryClassName(context));
+            return load(context, packageName, getRendererFactoryClassName(context));
         } catch (Exception e) {
             Log.e(TAG, "Failed to load renderer class: " + e.getMessage(), e);
             throw new RuntimeException(e);
         }
-
-        sRenderer.onCreate(createPackageContext(context, packageName), display);
-        sRenderer.onStart();
     }
 
+    public static Context createRendererPackageContext(Context context) {
+        return createPackageContext(context, getRendererPackageName(context));
+    }
+
+    /** To prevent instantiation of a singleton class. */
+    private InstrumentClusterRendererLoader() {}
+
     /**
      * Creates package context for given package name. It is necessary to get renderer's context
      * so appropriate resources will be loaded in the renderer code.
      */
     private static Context createPackageContext(Context currentContext, String packageName) {
         try {
-            // TODO: check APK certificate.
             return currentContext.createPackageContext(packageName,
                     Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
         } catch (NameNotFoundException e) {
@@ -102,22 +82,6 @@
         }
     }
 
-    private static Display getInstrumentClusterDisplay(Context context) {
-        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
-        Display[] displays = displayManager.getDisplays();
-
-        Log.d(TAG, "There are currently " + displays.length + " displays connected.");
-        for (Display display : displays) {
-            Log.d(TAG, "  " + display);
-        }
-
-        if (displays.length > 1) {
-            // TODO: assuming that secondary display is instrument cluster. Put this into settings?
-            return displays[1];
-        }
-        return null;
-    }
-
     /** Returns instrument cluster renderer or null if renderer package is not found */
     private static InstrumentClusterRenderer load(Context context, String packageName,
             String factoryClassName) throws Exception {
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
new file mode 100644
index 0000000..a3a8be6
--- /dev/null
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster;
+
+import static com.android.car.cluster.InstrumentClusterRendererLoader.createRenderer;
+import static com.android.car.cluster.InstrumentClusterRendererLoader.createRendererPackageContext;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
+import android.car.cluster.renderer.MediaRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.car.CarLog;
+import com.android.car.CarServiceBase;
+import com.android.car.CarServiceUtils;
+import com.android.car.R;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Service responsible for interaction with car's instrument cluster.
+ *
+ * @hide
+ */
+@SystemApi
+public class InstrumentClusterService implements CarServiceBase {
+
+    private static final String TAG = CarLog.TAG_CLUSTER + "."
+            + InstrumentClusterService.class.getSimpleName();
+
+    private static final int RENDERER_INIT_TIMEOUT_MS = 10 * 1000;
+    private final static int MSG_TIMEOUT = 1;
+
+    private final Context mContext;
+    private final Object mHalSync = new Object();
+    private final CopyOnWriteArrayList<RendererInitializationListener>
+            mRendererInitializationListeners = new CopyOnWriteArrayList<>();
+
+    private InstrumentClusterRenderer mRenderer;
+
+    private final Handler mHandler;
+
+    public InstrumentClusterService(Context context) {
+        mContext = context;
+        mHandler = new TimeoutHandler();
+    }
+
+    public interface RendererInitializationListener {
+        void onRendererInitSucceeded();
+    }
+
+    @Override
+    public void init() {
+        Log.d(TAG, "init");
+
+        if (getInstrumentClusterType() == InstrumentClusterType.GRAPHICS) {
+            Display display = getInstrumentClusterDisplay(mContext);
+            boolean rendererFound = InstrumentClusterRendererLoader.isRendererAvailable(mContext);
+
+            if (display != null && rendererFound) {
+                initRendererOnMainThread(display);
+            } else {
+                throw new RuntimeException("Failed to initialize InstrumentClusterRenderer"
+                        + ", renderer found: " + rendererFound
+                        + ", secondary display: " + (display != null));
+            }
+        }
+
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), RENDERER_INIT_TIMEOUT_MS);
+    }
+
+    private void initRendererOnMainThread(final Display display) {
+        CarServiceUtils.runOnMain(new Runnable() {
+            @Override
+            public void run() {
+                Log.d(TAG, "initRendererOnMainThread");
+                try {
+                    InstrumentClusterPresentation presentation =
+                            new InstrumentClusterPresentation(mContext, display);
+
+                    ViewGroup rootView = (ViewGroup) LayoutInflater.from(mContext).inflate(
+                            R.layout.instrument_cluster, null);
+
+                    presentation.setContentView(rootView);
+                    InstrumentClusterRenderer renderer = createRenderer(mContext);
+                    renderer.onCreate(createRendererPackageContext(mContext));
+                    View rendererView = renderer.onCreateView(null);
+                    renderer.initialize();
+                    rootView.addView(rendererView);
+                    presentation.show();
+                    initUiDone(renderer);
+                } catch (Exception e) {
+                    Log.e(TAG, e.getMessage(), e);
+                    throw e;
+                }
+            }
+        });
+    }
+
+    private void initUiDone(final InstrumentClusterRenderer renderer) {
+        Log.d(TAG, "initUiDone");
+        mHandler.removeMessages(MSG_TIMEOUT);
+
+        // Call listeners in service thread.
+        runOnServiceThread(new Runnable() {
+            @Override
+            public void run() {
+                mRenderer = renderer;
+
+                for (RendererInitializationListener listener : mRendererInitializationListeners) {
+                    listener.onRendererInitSucceeded();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void release() {
+        Log.d(TAG, "release");
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        // TODO
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            InstrumentClusterType.NONE,
+            InstrumentClusterType.METADATA,
+            InstrumentClusterType.GRAPHICS
+    })
+    public @interface InstrumentClusterType {
+        /**
+         * For use privately in this class.
+         * @hide
+         */
+        int UNDEFINED = -1;
+
+        /** Access to instrument cluster is not available */
+        int NONE = 0;
+
+        /** Access to instrument cluster through vehicle HAL using meta-data. */
+        int METADATA = 1;
+
+        /** Access instrument cluster as a secondary display. */
+        int GRAPHICS = 2;
+    }
+
+    @InstrumentClusterType private int mClusterType = InstrumentClusterType.UNDEFINED;
+
+    public boolean isInstrumentClusterAvailable() {
+        return mClusterType != InstrumentClusterType.NONE
+                && mClusterType != InstrumentClusterType.UNDEFINED;
+    }
+
+    public int getInstrumentClusterType() {
+        if (mClusterType == InstrumentClusterType.UNDEFINED) {
+            synchronized (mHalSync) {
+                // TODO: need to pull this information from the HAL
+                mClusterType = getInstrumentClusterDisplay(mContext) != null
+                        ? InstrumentClusterType.GRAPHICS : InstrumentClusterType.NONE;
+            }
+        }
+        return mClusterType;
+    }
+
+    @Nullable
+    public MediaRenderer getMediaRenderer() {
+        return mRenderer != null ? mRenderer.getMediaRenderer() : null;
+    }
+
+    @Nullable
+    public NavigationRenderer getNavigationRenderer() {
+        return mRenderer != null ? mRenderer.getNavigationRenderer() : null;
+    }
+
+    public void registerListener(RendererInitializationListener listener) {
+        mRendererInitializationListeners.add(listener);
+    }
+
+    public void unregisterListener(RendererInitializationListener listener) {
+        mRendererInitializationListeners.remove(listener);
+    }
+
+    private static Display getInstrumentClusterDisplay(Context context) {
+        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+        Display[] displays = displayManager.getDisplays();
+
+        Log.d(TAG, "There are currently " + displays.length + " displays connected.");
+        for (Display display : displays) {
+            Log.d(TAG, "  " + display);
+        }
+
+        if (displays.length > 1) {
+            // TODO: assuming that secondary display is instrument cluster. Put this into settings?
+            return displays[1];
+        }
+        return null;
+    }
+
+    private void runOnServiceThread(final Runnable runnable) {
+        if (Looper.myLooper() == mHandler.getLooper()) {
+            runnable.run();
+        } else {
+            mHandler.post(runnable);
+        }
+    }
+
+    private static class TimeoutHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_TIMEOUT) {
+                throw new RuntimeException("Renderer initialization timeout.");
+            } else {
+                super.handleMessage(msg);
+            }
+        }
+    }
+}
diff --git a/service/src/com/android/car/cluster/MediaStatusService.java b/service/src/com/android/car/cluster/MediaStatusService.java
new file mode 100644
index 0000000..e4d0306
--- /dev/null
+++ b/service/src/com/android/car/cluster/MediaStatusService.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster;
+
+import android.car.cluster.renderer.MediaRenderer;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
+import android.media.session.PlaybackState;
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.car.CarLog;
+import com.android.car.CarServiceBase;
+import com.android.car.cluster.InstrumentClusterService.RendererInitializationListener;
+import com.android.car.cluster.renderer.ThreadSafeMediaRenderer;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * Reports current media status to instrument cluster renderer.
+ */
+public class MediaStatusService implements CarServiceBase, RendererInitializationListener {
+
+    private final static String TAG = CarLog.TAG_CLUSTER
+            + "." + MediaStatusService.class.getSimpleName();
+
+    private final Context mContext;
+    private final MediaListener mMediaListener;
+    private volatile MediaRenderer mMediaRenderer;
+    private InstrumentClusterService mInstrumentClusterService;
+    private MediaController mPrimaryMediaController;
+    private OnActiveSessionsChangedListener mActiveSessionsChangedListener;
+    private MediaSessionManager mMediaSessionManager;
+
+    public MediaStatusService(Context context, InstrumentClusterService instrumentClusterService) {
+        mContext = context;
+        mInstrumentClusterService = instrumentClusterService;
+        instrumentClusterService.registerListener(this);
+        mMediaListener = new MediaListener(MediaStatusService.this);
+    }
+
+    @Override
+    public void init() {
+        Log.d(TAG, "init");
+
+        if (!mInstrumentClusterService.isInstrumentClusterAvailable()) {
+            Log.d(TAG, "Instrument cluster is not available.");
+            return;
+        }
+
+        mActiveSessionsChangedListener = new OnActiveSessionsChangedListener() {
+            @Override
+            public void onActiveSessionsChanged(List<MediaController> controllers) {
+                MediaStatusService.this.onActiveSessionsChanged(controllers);
+            }
+        };
+        mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+        mMediaSessionManager.addOnActiveSessionsChangedListener(
+                mActiveSessionsChangedListener, null);
+
+        onActiveSessionsChanged(mMediaSessionManager.getActiveSessions(null));
+    }
+
+    private void onActiveSessionsChanged(List<MediaController> controllers) {
+        Log.d(TAG, "onActiveSessionsChanged, controllers found:  " + controllers.size());
+        MediaController newPrimaryController = null;
+        if (controllers.size() > 0) {
+            newPrimaryController = controllers.get(0);
+            if (mPrimaryMediaController == newPrimaryController) {
+                // Primary media controller has not been changed.
+                return;
+            }
+        }
+
+        releasePrimaryMediaController();
+
+        if (newPrimaryController != null) {
+            mPrimaryMediaController = newPrimaryController;
+            mPrimaryMediaController.registerCallback(mMediaListener);
+        }
+        updateRendererMediaStatusIfAvailable();
+
+        for (MediaController m : controllers) {
+            Log.d(TAG, m + ": " + m.getPackageName());
+        }
+    }
+
+    @Override
+    public void release() {
+        releasePrimaryMediaController();
+        if (mActiveSessionsChangedListener != null) {
+            mMediaSessionManager.removeOnActiveSessionsChangedListener(
+                    mActiveSessionsChangedListener);
+            mActiveSessionsChangedListener = null;
+        }
+        mMediaSessionManager = null;
+    }
+
+    private void releasePrimaryMediaController() {
+        if (mPrimaryMediaController != null) {
+            mPrimaryMediaController.unregisterCallback(mMediaListener);
+            mPrimaryMediaController = null;
+        }
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        // TODO
+    }
+
+    @Override
+    public void onRendererInitSucceeded() {
+        Log.d(TAG, "onRendererInitSucceeded");
+        mMediaRenderer = ThreadSafeMediaRenderer.createFor(
+                Looper.getMainLooper(),
+                mInstrumentClusterService.getMediaRenderer());
+
+        updateRendererMediaStatusIfAvailable();
+    }
+
+    private void updateRendererMediaStatusIfAvailable() {
+        if (isRendererAvailable()) {
+            mMediaRenderer.onMetadataChanged(
+                    mPrimaryMediaController == null ? null : mPrimaryMediaController.getMetadata());
+            mMediaRenderer.onPlaybackStateChanged(
+                    mPrimaryMediaController == null
+                    ? null : mPrimaryMediaController.getPlaybackState());
+        }
+    }
+
+    private boolean isRendererAvailable() {
+        boolean available = mMediaRenderer != null;
+        if (!available) {
+            Log.w(TAG, "Media renderer is not available.");
+        }
+        return available;
+    }
+
+    private void onPlaybackStateChanged(PlaybackState state) {
+        if (isRendererAvailable()) {
+            mMediaRenderer.onPlaybackStateChanged(state);
+        }
+    }
+
+    private void onMetadataChanged(MediaMetadata metadata) {
+        if (isRendererAvailable()) {
+            mMediaRenderer.onMetadataChanged(metadata);
+        }
+    }
+
+    private static class MediaListener extends MediaController.Callback {
+        private final WeakReference<MediaStatusService> mServiceRef;
+
+        MediaListener(MediaStatusService service) {
+            mServiceRef = new WeakReference<>(service);
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            MediaStatusService service = mServiceRef.get();
+            if (service != null) {
+                service.onPlaybackStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            MediaStatusService service = mServiceRef.get();
+            if (service != null) {
+                service.onMetadataChanged(metadata);
+            }
+        }
+    }
+}
diff --git a/service/src/com/android/car/cluster/renderer/RendererHandler.java b/service/src/com/android/car/cluster/renderer/RendererHandler.java
new file mode 100644
index 0000000..b999dde
--- /dev/null
+++ b/service/src/com/android/car/cluster/renderer/RendererHandler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster.renderer;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Abstract {@link Handler} class that holds reference to a renderer object.
+ */
+public abstract class RendererHandler<T> extends Handler {
+
+    private final WeakReference<T> mRendererRef;
+
+    RendererHandler(Looper looper, T renderer) {
+        super(looper);
+        mRendererRef = new WeakReference<>(renderer);
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        T renderer = mRendererRef.get();
+        if (renderer != null) {
+            handleMessage(msg, renderer);
+        }
+    }
+
+    public abstract void handleMessage(Message msg, T renderer);
+}
diff --git a/service/src/com/android/car/cluster/renderer/ThreadSafeMediaRenderer.java b/service/src/com/android/car/cluster/renderer/ThreadSafeMediaRenderer.java
new file mode 100644
index 0000000..fcf3989
--- /dev/null
+++ b/service/src/com/android/car/cluster/renderer/ThreadSafeMediaRenderer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster.renderer;
+
+import android.car.cluster.renderer.MediaRenderer;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * A wrapper over {@link MediaRenderer} that runs all its methods in the context of provided looper.
+ * It is guaranteed that all calls will be invoked in order they were called.
+ */
+public class ThreadSafeMediaRenderer extends MediaRenderer {
+    private final Handler mHandler;
+
+    private final static int MSG_PLAYBACK_STATE_CHANGED = 1;
+    private final static int MSG_METADATA_CHANGED = 2;
+
+    public static MediaRenderer createFor(Looper looper, MediaRenderer renderer) {
+        return renderer != null ? new ThreadSafeMediaRenderer(renderer, looper) : null;
+    }
+
+    private ThreadSafeMediaRenderer(MediaRenderer mediaRenderer, Looper looper) {
+        mHandler = new MediaRendererHandler(looper, mediaRenderer);
+    }
+
+    @Override
+    public void onPlaybackStateChanged(PlaybackState playbackState) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_PLAYBACK_STATE_CHANGED, playbackState));
+    }
+
+    @Override
+    public void onMetadataChanged(MediaMetadata metadata) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_METADATA_CHANGED, metadata));
+    }
+
+    private static class MediaRendererHandler extends RendererHandler<MediaRenderer> {
+
+        MediaRendererHandler(Looper looper, MediaRenderer renderer) {
+            super(looper, renderer);
+        }
+
+        @Override
+        public void handleMessage(Message msg, MediaRenderer renderer) {
+            switch (msg.what) {
+                case MSG_PLAYBACK_STATE_CHANGED:
+                    renderer.onPlaybackStateChanged((PlaybackState) msg.obj);
+                    break;
+                case MSG_METADATA_CHANGED:
+                    renderer.onMetadataChanged((MediaMetadata) msg.obj);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Msg: " + msg.what);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/service/src/com/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java b/service/src/com/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
new file mode 100644
index 0000000..33dde7a
--- /dev/null
+++ b/service/src/com/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster.renderer;
+
+import android.annotation.Nullable;
+import android.car.cluster.renderer.NavigationRenderer;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * A wrapper over {@link NavigationRenderer} that runs all its methods in the context of provided
+ * looper. It is guaranteed that all calls will be invoked in order they were called.
+ */
+public class ThreadSafeNavigationRenderer extends NavigationRenderer {
+
+    private final Handler mHandler;
+
+    private final static int MSG_NAV_START = 1;
+    private final static int MSG_NAV_STOP = 2;
+    private final static int MSG_NAV_NEXT_TURN = 3;
+    private final static int MSG_NAV_NEXT_TURN_DISTANCE = 4;
+
+    /** Creates thread-safe {@link NavigationRenderer}. Returns null if renderer == null */
+    @Nullable
+    public static NavigationRenderer createFor(Looper looper, NavigationRenderer renderer) {
+        return renderer == null ? null : new ThreadSafeNavigationRenderer(looper, renderer);
+    }
+
+    private ThreadSafeNavigationRenderer(Looper looper, NavigationRenderer renderer) {
+        mHandler = new NavigationRendererHandler(looper, renderer);
+    }
+
+    @Override
+    public void onStartNavigation() {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_START));
+    }
+
+    @Override
+    public void onStopNavigation() {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_STOP));
+    }
+
+    @Override
+    public void onNextTurnChanged(int event, String road, int turnAngle, int turnNumber,
+            Bitmap image, int turnSide) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_NEXT_TURN,
+                new NextTurn(event, road, turnAngle, turnNumber, image, turnSide)));
+    }
+
+    @Override
+    public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds) {
+        mHandler.sendMessage(mHandler.obtainMessage(
+                    MSG_NAV_NEXT_TURN_DISTANCE, distanceMeters, timeSeconds));
+    }
+
+    private static class NavigationRendererHandler extends RendererHandler<NavigationRenderer> {
+
+        NavigationRendererHandler(Looper looper, NavigationRenderer renderer) {
+            super(looper, renderer);
+        }
+
+        @Override
+        public void handleMessage(Message msg, NavigationRenderer renderer) {
+
+            switch (msg.what) {
+                case MSG_NAV_START:
+                    renderer.onStartNavigation();
+                    break;
+                case MSG_NAV_STOP:
+                    renderer.onStopNavigation();
+                    break;
+                case MSG_NAV_NEXT_TURN:
+                    NextTurn nt = (NextTurn) msg.obj;
+                    renderer.onNextTurnChanged(nt.event, nt.road, nt.turnAngle, nt.turnNumber,
+                            nt.bitmap, nt.turnSide);
+                    break;
+                case MSG_NAV_NEXT_TURN_DISTANCE:
+                    renderer.onNextTurnDistanceChanged(msg.arg1, msg.arg2);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Msg: " + msg.what);
+            }
+        }
+    }
+
+    private static class NextTurn {
+        private final int event;
+        private final String road;
+        private final int turnAngle;
+        private final int turnNumber;
+        private final Bitmap bitmap;
+        private final int turnSide;
+
+        NextTurn(int event, String road, int turnAngle, int turnNumber, Bitmap bitmap,
+                int turnSide) {
+            this.event = event;
+            this.road = road;
+            this.turnAngle = turnAngle;
+            this.turnNumber = turnNumber;
+            this.bitmap = bitmap;
+            this.turnSide = turnSide;
+        }
+    }
+}
diff --git a/service/src/com/android/car/hal/AudioHalService.java b/service/src/com/android/car/hal/AudioHalService.java
index 68c4d11..5208787 100644
--- a/service/src/com/android/car/hal/AudioHalService.java
+++ b/service/src/com/android/car/hal/AudioHalService.java
@@ -19,6 +19,7 @@
 import android.util.Log;
 
 import com.android.car.AudioRoutingPolicy;
+import com.android.car.CarAudioAttributesUtil;
 import com.android.car.CarLog;
 import com.android.car.vehiclenetwork.VehicleNetwork;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts;
@@ -99,6 +100,31 @@
     public static final int FOCUS_STATE_ARRAY_INDEX_STREAMS = 1;
     public static final int FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS = 2;
 
+    public static final int AUDIO_CONTEXT_MUSIC_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG;
+    public static final int AUDIO_CONTEXT_NAVIGATION_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG;
+    public static final int AUDIO_CONTEXT_VOICE_COMMAND_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG;
+    public static final int AUDIO_CONTEXT_CALL_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG;
+    public static final int AUDIO_CONTEXT_ALARM_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG;
+    public static final int AUDIO_CONTEXT_NOTIFICATION_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG;
+    public static final int AUDIO_CONTEXT_UNKNOWN_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
+    public static final int AUDIO_CONTEXT_SAFETY_ALERT_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG;
+    public static final int AUDIO_CONTEXT_RADIO_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
+    public static final int AUDIO_CONTEXT_CD_ROM_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG;
+    public static final int AUDIO_CONTEXT_AUX_AUDIO_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG;
+    public static final int AUDIO_CONTEXT_SYSTEM_SOUND_FLAG =
+            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
+
     public interface AudioHalListener {
         /**
          * Audio focus change from car.
@@ -168,19 +194,24 @@
         for (int i = 0; i < policy.getPhysicalStreamsCount(); i++) {
             policyToSet[VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_STREAM] =
                     i;
-            int streams = 0;
+            int contexts = 0;
             for (int logicalStream : policy.getLogicalStreamsForPhysicalStream(i)) {
-                streams |= logicalStreamToHalStreamType(logicalStream);
+                contexts |= logicalStreamToHalContextType(logicalStream);
             }
             policyToSet[VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS]
-                    = streams;
+                    = contexts;
             vn.setIntVectorProperty(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY,
                     policyToSet);
         }
     }
 
-    private static int logicalStreamToHalStreamType(int logicalStream) {
+    /**
+     * Convert car audio manager stream type (usage) into audio context type.
+     */
+    public static int logicalStreamToHalContextType(int logicalStream) {
         switch (logicalStream) {
+            case CarAudioManager.CAR_AUDIO_USAGE_RADIO:
+                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
             case CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL:
                 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG;
             case CarAudioManager.CAR_AUDIO_USAGE_MUSIC:
@@ -196,21 +227,25 @@
             case CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT:
                 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG;
             case CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND:
-                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND;
+                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
             case CarAudioManager.CAR_AUDIO_USAGE_DEFAULT:
                 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
+            case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM:
+            case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY:
+                // internal tag not associated with any stream
+                return 0;
             default:
                 Log.w(CarLog.TAG_AUDIO, "Unknown logical stream:" + logicalStream);
                 return 0;
         }
     }
 
-    public void requestAudioFocusChange(int request, int streams) {
-        requestAudioFocusChange(request, streams, VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+    public void requestAudioFocusChange(int request, int streams, int audioContexts) {
+        requestAudioFocusChange(request, streams, VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG, audioContexts);
     }
 
-    public void requestAudioFocusChange(int request, int streams, int extFocus) {
-        int[] payload = { request, streams, extFocus };
+    public void requestAudioFocusChange(int request, int streams, int extFocus, int audioContexts) {
+        int[] payload = { request, streams, extFocus, audioContexts };
         mVehicleHal.getVehicleNetwork().setIntVectorProperty(
                 VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, payload);
     }
diff --git a/service/src/com/android/car/hal/InputHalService.java b/service/src/com/android/car/hal/InputHalService.java
new file mode 100644
index 0000000..70c7899
--- /dev/null
+++ b/service/src/com/android/car/hal/InputHalService.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.hal;
+
+import android.util.Log;
+import android.view.KeyEvent;
+
+import com.android.car.CarLog;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleDisplay;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleHwKeyInputAction;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+public class InputHalService extends HalServiceBase {
+
+    public static final int DISPLAY_MAIN = VehicleDisplay.VEHICLE_DISPLAY_MAIN;
+    public static final int DISPLAY_INSTRUMENT_CLUSTER =
+            VehicleDisplay.VEHICLE_DISPLAY_INSTRUMENT_CLUSTER;
+
+    public interface InputListener {
+        void onKeyEvent(KeyEvent event, int targetDisplay);
+    }
+
+    private static final boolean DBG = false;
+
+    private boolean mKeyInputSupported = false;
+    private InputListener mListener;
+
+    public void setInputListener(InputListener listener) {
+        synchronized (this) {
+            if (!mKeyInputSupported) {
+                Log.w(CarLog.TAG_INPUT, "input listener set while key input not supported");
+                return;
+            }
+            mListener = listener;
+        }
+        VehicleHal.getInstance().subscribeProperty(this,
+                VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT, 0);
+    }
+
+    public synchronized boolean isKeyInputSupported() {
+        return mKeyInputSupported;
+    }
+
+    @Override
+    public void init() {
+    }
+
+    @Override
+    public void release() {
+        synchronized (this) {
+            mListener = null;
+            mKeyInputSupported = false;
+        }
+    }
+
+    @Override
+    public List<VehiclePropConfig> takeSupportedProperties(List<VehiclePropConfig> allProperties) {
+        List<VehiclePropConfig> supported = new LinkedList<VehiclePropConfig>();
+        for (VehiclePropConfig p: allProperties) {
+            if (p.getProp() == VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT) {
+                supported.add(p);
+                synchronized (this) {
+                    mKeyInputSupported = true;
+                }
+            }
+        }
+        return supported;
+    }
+
+    @Override
+    public void handleHalEvents(List<VehiclePropValue> values) {
+        InputListener listener = null;
+        synchronized (this) {
+            listener = mListener;
+        }
+        if (listener == null) {
+            Log.w(CarLog.TAG_INPUT, "Input event while listener is null");
+            return;
+        }
+        for (VehiclePropValue v : values) {
+            if (v.getProp() != VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT) {
+                Log.e(CarLog.TAG_INPUT, "Wrong event dispatched, prop:0x" +
+                        Integer.toHexString(v.getProp()));
+                continue;
+            }
+            int action = (v.getInt32Values(0) ==
+                    VehicleHwKeyInputAction.VEHICLE_HW_KEY_INPUT_ACTION_DOWN) ?
+                            KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
+            int code = v.getInt32Values(1);
+            int display = v.getInt32Values(2);
+            if (DBG) {
+                Log.i(CarLog.TAG_INPUT, "hal event code:" + code + ",action:" + action +
+                        ",display:" + display);
+            }
+            KeyEvent event = new KeyEvent(action, code);
+            listener.onKeyEvent(event, display);
+        }
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        writer.println("*Input HAL*");
+        writer.println("mKeyInputSupported:" + mKeyInputSupported);
+    }
+
+}
diff --git a/service/src/com/android/car/hal/PowerHalService.java b/service/src/com/android/car/hal/PowerHalService.java
index 899fbe3..8cf412a 100644
--- a/service/src/com/android/car/hal/PowerHalService.java
+++ b/service/src/com/android/car/hal/PowerHalService.java
@@ -332,6 +332,8 @@
 
     @Override
     public void dump(PrintWriter writer) {
-        // TODO Auto-generated method stub
+        writer.println("*Power HAL*");
+        writer.println("isPowerStateSupported:" + isPowerStateSupported() +
+                ",isDeepSleepAllowed:" + isDeepSleepAllowed());
     }
 }
diff --git a/service/src/com/android/car/hal/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
index 05e7571..6ea7b15 100644
--- a/service/src/com/android/car/hal/SensorHalService.java
+++ b/service/src/com/android/car/hal/SensorHalService.java
@@ -113,7 +113,8 @@
         switch (property) {
             // boolean
             case VehicleNetworkConsts.VEHICLE_PROPERTY_NIGHT_MODE:
-            case VehicleNetworkConsts.VEHICLE_PROPERTY_PARKING_BRAKE_ON: {
+            case VehicleNetworkConsts.VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+            case VehicleNetworkConsts.VEHICLE_PROPERTY_FUEL_LEVEL_LOW: {
                 if (DBG_EVENTS) {
                     Log.i(CarLog.TAG_SENSOR, "boolean event, property:" +
                             Integer.toHexString(property) + " value:" + v.getInt32Values(0));
@@ -241,6 +242,8 @@
                 return CarSensorManager.SENSOR_TYPE_PARKING_BRAKE;
             case VehicleNetworkConsts.VEHICLE_PROPERTY_DRIVING_STATUS:
                 return CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
+            case VehicleNetworkConsts.VEHICLE_PROPERTY_FUEL_LEVEL_LOW:
+                return CarSensorManager.SENSOR_TYPE_FUEL_LEVEL;
             default:
                 return SENSOR_TYPE_INVALD;
         }
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index d1d0789..ef0e6c7 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -88,6 +88,7 @@
     private final RadioHalService mRadioHal;
     private final PowerHalService mPowerHal;
     private final HvacHalService mHvacHal;
+    private final InputHalService mInputHal;
 
     /** stores handler for each HAL property. Property events are sent to handler. */
     private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<HalServiceBase>();
@@ -107,13 +108,16 @@
         mAudioHal = new AudioHalService(this);
         mRadioHal = new RadioHalService(this);
         mHvacHal = new HvacHalService(this);
+        mInputHal = new InputHalService();
         mAllServices = new HalServiceBase[] {
                 mPowerHal,
                 mAudioHal,
                 mHvacHal,
                 mInfoHal,
                 mSensorHal,
-                mRadioHal };
+                mRadioHal,
+                mInputHal
+                };
         mVehicleNetwork = VehicleNetwork.createVehicleNetwork(this, mHandlerThread.getLooper());
     }
 
@@ -129,6 +133,7 @@
         mAudioHal = audioHal;
         mRadioHal = radioHal;
         mHvacHal = hvacHal;
+        mInputHal = null;
         mAllServices = null;
         mVehicleNetwork = vehicleNetwork;
     }
@@ -215,24 +220,8 @@
         return mHvacHal;
     }
 
-    public void updateAppContext(boolean navigationActive, boolean voiceCommandActive,
-            boolean callActive) {
-        synchronized (this) {
-            VehiclePropConfig config = mUnclaimedProperties.get(
-                    VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT);
-            if (config == null) {
-                return; // not supported
-            }
-        }
-        int currentContext =
-                (navigationActive ?
-                    VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG : 0) |
-                (voiceCommandActive ?
-                        VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG : 0) |
-                (callActive ?
-                        VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG : 0);
-        mVehicleNetwork.setIntProperty(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT,
-                currentContext);
+    public InputHalService getInputHal() {
+        return mInputHal;
     }
 
     private void assertServiceOwnerLocked(HalServiceBase service, int property) {
diff --git a/tests/car_activity_test_app/Android.mk b/tests/EmbeddedKitchenSinkApp/Android.mk
similarity index 76%
rename from tests/car_activity_test_app/Android.mk
rename to tests/EmbeddedKitchenSinkApp/Android.mk
index 0b9add5..e160e03 100644
--- a/tests/car_activity_test_app/Android.mk
+++ b/tests/EmbeddedKitchenSinkApp/Android.mk
@@ -22,15 +22,18 @@
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := CarActivityTestApp
+LOCAL_PACKAGE_NAME := EmbeddedKitchenSinkApp
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_AAPT_FLAGS := --auto-add-overlay
 
-# When built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-include packages/services/Car/car-support-lib/car-support.mk
+LOCAL_STATIC_JAVA_LIBRARIES += car-systemtest android.support.car
+
+LOCAL_JAVA_LIBRARIES += android.car
 
 include $(BUILD_PACKAGE)
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
new file mode 100644
index 0000000..aed497f
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="com.google.android.car.kitchensink" >
+    <uses-sdk
+        android:minSdkVersion="22"
+        android:targetSdkVersion='23'/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.car.permission.CAR_SPEED" />
+    <uses-permission android:name="android.car.permission.CAR_HVAC" />
+    <uses-permission android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL" />
+    <uses-permission android:name="android.car.permission.CAR_CAMERA" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <application android:label="@string/app_title"
+        android:icon="@drawable/ic_launcher">
+
+        <!-- This is for embedded mode. -->
+        <activity android:name=".KitchenSinkProxyActivity"
+            android:theme="@android:style/Theme.NoTitleBar"
+            android:label="@string/app_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <meta-data
+            android:name="android.car.application"
+            android:resource="@xml/automotive_app_desc" />
+
+        <service android:name=".job.DishService"
+            android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
+    </application>
+</manifest>
diff --git a/tests/EmbeddedKitchenSinkApp/res/drawable-hdpi/ic_launcher.png b/tests/EmbeddedKitchenSinkApp/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..af3d210
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-nodpi/sample_apps_bg.png b/tests/EmbeddedKitchenSinkApp/res/drawable-nodpi/app_bg.png
similarity index 100%
rename from tests/car_activity_test_app/res/drawable-nodpi/sample_apps_bg.png
rename to tests/EmbeddedKitchenSinkApp/res/drawable-nodpi/app_bg.png
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml b/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml
new file mode 100644
index 0000000..445a14f
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+    <!--  dummy one for top area -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:orientation="vertical"
+        android:layout_weight="1" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_weight="1" >
+        <ToggleButton
+            android:id="@+id/button_mock_audio"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textOn="@string/mock_audio_on"
+            android:textOff="@string/mock_audio_off" />
+        <ToggleButton
+            android:id="@+id/button_reject_audio_focus"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textOn="@string/reject_audio_focus_on"
+            android:textOff="@string/reject_audio_focus_off" />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_weight="1" >
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+            <TextView
+                android:id="@+id/audio_focus_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/audio_focus" />
+            <Button
+                android:id="@+id/button_audio_focus_request"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/request" />
+            <TextView
+                android:id="@+id/text_audio_focus_state"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/empty" />
+        </LinearLayout>
+        <RadioGroup
+            android:id="@+id/button_focus_request_selection"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+            <RadioButton
+                android:id="@+id/focus_gain"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/focus_gain" />
+            <RadioButton
+                android:id="@+id/focus_gain_transient_duck"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/focus_gain_transient_duck" />
+            <RadioButton
+                android:id="@+id/focus_release"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/focus_release" />
+        </RadioGroup>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_weight="1" >
+        <TextView
+            android:id="@+id/nav_play_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/nav_play" />
+        <Button
+            android:id="@+id/button_nav_play_once"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/play_pcm_once" />
+        <TextView
+            android:id="@+id/vr_play_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/vr_play" />
+        <Button
+            android:id="@+id/button_vr_play_once"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/play_pcm_once" />
+        <TextView
+            android:id="@+id/system_play_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/system_play" />
+        <Button
+            android:id="@+id/button_system_play_once"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/play_pcm_once" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_weight="1" >
+        <TextView
+            android:id="@+id/media_play_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/media_play" />
+        <Button
+            android:id="@+id/button_media_play_start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/play" />
+        <Button
+            android:id="@+id/button_media_play_once"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/play_pcm_once" />
+        <Button
+            android:id="@+id/button_media_play_stop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/stop" />
+        <Button
+            android:id="@+id/button_speaker_phone_on"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/speaker_phone_on" />
+        <Button
+            android:id="@+id/button_speaker_phone_off"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/speaker_phone_off" />
+        <Button
+            android:id="@+id/button_microphone_on"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/microphone_on" />
+        <Button
+            android:id="@+id/button_microphone_off"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/microphone_off" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/system_play_message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/empty"
+        android:layout_weight="1" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_weight="1" >
+        <Button
+            android:id="@+id/button_nav_start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/nav_start" />
+        <Button
+            android:id="@+id/button_nav_end"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/nav_end" />
+        <Button
+            android:id="@+id/button_vr_start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/vr_start" />
+        <Button
+            android:id="@+id/button_vr_end"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/vr_end" />
+        <Button
+            android:id="@+id/button_radio_start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/radio_start" />
+        <Button
+            android:id="@+id/button_radio_end"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/radio_end" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/camera_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/camera_test.xml
new file mode 100644
index 0000000..ed167a5
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/camera_test.xml
@@ -0,0 +1,148 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:gravity="center"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:layout_marginTop="100dp"
+    android:orientation="vertical">
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/btnGetCap"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_getCapabilities"
+            android:textSize="@dimen/rvcTextSize"/>
+        <TextView
+            android:gravity="center"
+            android:id="@+id/tvCap"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:textSize="@dimen/rvcTextSize"/>
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/btnGetRvcCrop"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_getRvcCrop"
+            android:textSize="@dimen/rvcTextSize"/>
+        <TextView
+            android:gravity="center"
+            android:id="@+id/tvRvcCrop"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:textSize="@dimen/rvcTextSize"/>
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/btnGetRvcPos"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_getRvcPos"
+            android:textSize="@dimen/rvcTextSize"/>
+        <TextView
+            android:gravity="center"
+            android:id="@+id/tvRvcPos"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:textSize="@dimen/rvcTextSize"/>
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/btnGetCameraState"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_getCameraState"
+            android:textSize="@dimen/rvcTextSize"/>
+        <TextView
+            android:gravity="center"
+            android:id="@+id/tvCameraState"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:textSize="@dimen/rvcTextSize"/>
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/btnSetRvcCrop"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_setRvcCrop"
+            android:textSize="@dimen/rvcTextSize"/>
+        <Button
+            android:id="@+id/btnSetRvcCrop2"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_setRvcCrop"
+            android:textSize="@dimen/rvcTextSize"/>
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/btnSetRvcPos"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_setRvcPos"
+            android:textSize="@dimen/rvcTextSize"/>
+        <Button
+            android:id="@+id/btnSetRvcPos2"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_setRvcPos"
+            android:textSize="@dimen/rvcTextSize"/>
+        <Button
+            android:id="@+id/btnSetRvcPos3"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_setRvcPos"
+            android:textSize="@dimen/rvcTextSize"/>
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+        <ToggleButton
+            android:id="@+id/btnRvcState"
+            android:layout_height="@dimen/rvcBtnHeight"
+            android:layout_width ="@dimen/rvcBtnWidth"
+            android:text="@string/rvc_state"
+            android:textSize="@dimen/rvcTextSize"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml
new file mode 100644
index 0000000..6cfe237
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:gravity="center"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:orientation="vertical"
+            android:gravity="center"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:layout_width = "wrap_content">
+
+            <Button
+                android:clickable="false"
+                android:id="@+id/btnDTempUp"
+                android:layout_height="100dp"
+                android:layout_width ="250dp"
+                android:text="@string/hvac_tempUp"
+                android:textSize="32sp"/>
+
+            <TextView
+                android:gravity="center"
+                android:id="@+id/tvDTemp"
+                android:layout_height="50dp"
+                android:layout_width ="250dp"
+                android:textSize="32sp"/>
+
+            <Button
+                android:clickable="false"
+                android:id="@+id/btnDTempDn"
+                android:layout_height="100dp"
+                android:layout_width ="250dp"
+                android:text="@string/hvac_tempDn"
+                android:textSize="32sp"/>
+        </LinearLayout>
+
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:gravity="center"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:layout_width = "wrap_content"
+            android:orientation="vertical">
+
+            <Button
+                android:clickable="false"
+                android:id="@+id/btnFanSpeedUp"
+                android:layout_height="100dp"
+                android:layout_width ="250dp"
+                android:text="@string/hvac_fanSpeedUp"
+                android:textSize="32sp"/>
+
+            <TextView
+                android:gravity="center"
+                android:id="@+id/tvFanSpeed"
+                android:layout_height="50dp"
+                android:layout_width ="250dp"
+                android:textSize="32sp"/>
+
+            <Button
+                android:clickable="false"
+                android:id="@+id/btnFanSpeedDn"
+                android:layout_height="100dp"
+                android:layout_width ="250dp"
+                android:text="@string/hvac_fanSpeedDn"
+                android:textSize="32sp"/>
+        </LinearLayout>
+
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:gravity="center"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:layout_width = "wrap_content"
+            android:orientation="vertical">
+
+            <Button
+                android:clickable="false"
+                android:id="@+id/btnPTempUp"
+                android:layout_height="100dp"
+                android:layout_width ="250dp"
+                android:text="@string/hvac_tempUp"
+                android:textSize="32sp"/>
+
+            <TextView
+                android:gravity="center"
+                android:id="@+id/tvPTemp"
+                android:layout_height="50dp"
+                android:layout_width ="250dp"
+                android:textSize="32sp"/>
+
+            <Button
+                android:clickable="false"
+                android:id="@+id/btnPTempDn"
+                android:layout_height="100dp"
+                android:layout_width ="250dp"
+                android:text="@string/hvac_tempDn"
+                android:textSize="32sp"/>
+        </LinearLayout>
+    </LinearLayout>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_marginTop="25dp"
+        android:orientation="horizontal">
+
+        <ToggleButton
+            android:clickable="false"
+            android:id="@+id/tbDefrostFront"
+            android:layout_height="100dp"
+            android:layout_weight="1"
+            android:layout_width ="150dp"
+            android:textOff="@string/hvac_defrostFrontOff"
+            android:textOn="@string/hvac_defrostFrontOn"
+            android:textSize="32sp"/>
+
+        <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+            android:clickable="false"
+            android:id="@+id/rgFanPosition"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_width="wrap_content"
+            android:orientation="vertical">
+            <RadioButton
+                android:clickable="false"
+                android:id="@+id/rbPositionFace"
+                android:text="@string/hvac_positionFace"
+                android:textSize="32sp"/>
+            <RadioButton
+                android:clickable="false"
+                android:id="@+id/rbPositionFloor"
+                android:text="@string/hvac_positionFloor"
+                android:textSize="32sp"/>
+            <RadioButton
+                android:clickable="false"
+                android:id="@+id/rbPositionFaceAndFloor"
+                android:text="@string/hvac_positionFaceAndFloor"
+                android:textSize="32sp"/>
+        </RadioGroup>
+
+        <ToggleButton
+            android:clickable="false"
+            android:id="@+id/tbAc"
+            android:layout_height="100dp"
+            android:layout_weight="1"
+            android:layout_width="150dp"
+            android:textOff="@string/hvac_acOff"
+            android:textOn="@string/hvac_acOn"
+            android:textSize="32sp"/>
+
+        <ToggleButton
+            android:clickable="false"
+            android:id="@+id/tbDefrostRear"
+            android:layout_height="100dp"
+            android:layout_weight="1"
+            android:layout_width ="150dp"
+            android:textOff="@string/hvac_defrostRearOff"
+            android:textOn="@string/hvac_defrostRearOn"
+            android:textSize="32sp"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml
new file mode 100644
index 0000000..f347ce9
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+    <!--  dummy one for top area -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:orientation="vertical"
+        android:layout_weight="1" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_weight="1" >
+        <Button
+            android:id="@+id/button_volume_up"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/volume_up" />
+        <Button
+            android:id="@+id/button_volume_down"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/volume_down" />
+        <Button
+            android:id="@+id/button_voice"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/voice" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
new file mode 100644
index 0000000..aebb27d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:layout_marginTop="40dp"
+              android:layout_marginLeft="40dp">
+
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+        <LinearLayout
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+            <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+                    android:text="@string/cluster_start" android:id="@+id/cluster_start_button"
+                    android:layout_column="0" android:textSize="32sp"/>
+            <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+                    android:text="@string/cluster_turn_left" android:id="@+id/cluster_turn_left_button"
+                    android:layout_column="0" android:textSize="32sp"/>
+            <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+                    android:text="@string/cluster_stop" android:id="@+id/cluster_stop_button"
+                    android:layout_column="0" android:textSize="32sp"/>
+        </LinearLayout>
+        <EditText
+                android:layout_width="match_parent"
+                android:layout_height="fill_parent"
+                android:inputType="textMultiLine"
+                android:ems="10"
+                android:id="@+id/cluster_log"
+                android:scrollIndicators="right" android:gravity="top"
+                android:editable="false"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/job_scheduler.xml b/tests/EmbeddedKitchenSinkApp/res/layout/job_scheduler.xml
new file mode 100644
index 0000000..f291f04
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/job_scheduler.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:layout_marginTop="90dp"
+    android:layout_marginLeft="90dp">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+            <Button
+                android:id="@+id/refresh_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/refresh_button" />
+            <Button
+                android:id="@+id/cancel_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/cancel_button" />
+        </LinearLayout>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/job_info"
+            android:textColor="@android:color/holo_red_dark" />
+        <TextView
+            android:id="@+id/current_jobs"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <Button
+            android:id="@+id/schedule_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/schedule_button"/>
+        <CheckBox
+            android:id="@+id/require_idle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/require_idle"/>
+        <CheckBox
+            android:id="@+id/require_charging"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/require_charging"/>
+        <CheckBox
+            android:id="@+id/require_persisted"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/require_persisted"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/require_network"/>
+        <RadioGroup
+            android:id="@+id/network_group"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+
+            <RadioButton android:id="@+id/network_any"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/network_any"/>
+
+            <RadioButton android:id="@+id/network_none"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/network_none"/>
+
+            <RadioButton android:id="@+id/network_unmetered"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/network_unmetered"/>
+        </RadioGroup>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/dish_num"/>
+
+            <android.support.car.input.CarRestrictedEditText
+                android:id="@+id/dish_num"
+                android:layout_width="100dp"
+                android:layout_height="40dp"
+                android:inputType="number"
+                android:text="@string/default_dish_num" />
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_end_view.xml b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_end_view.xml
new file mode 100644
index 0000000..d60f63d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_end_view.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ProgressBar
+        android:layout_width="50dp"
+        android:layout_height="50dp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_test.xml
new file mode 100644
index 0000000..c617094
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_test.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="96dp"
+    android:layout_marginLeft="96dp">
+
+    <TextView
+        android:id="@+id/search_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/on_search"
+        android:textSize="20dp" />
+
+    <TextView
+        android:id="@+id/edit_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/on_edit"
+        android:textSize="20dp" />
+
+    <Button
+        android:id="@+id/ime_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/open_ime_button"/>
+
+    <Button
+        android:id="@+id/stop_ime_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/close_ime_button"/>
+
+    <Button
+        android:id="@+id/ime_button2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/open_kb_button"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_sink_activity.xml b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_sink_activity.xml
new file mode 100644
index 0000000..2801daa
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_sink_activity.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="100dp"
+    android:orientation="vertical" >
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
new file mode 100644
index 0000000..25e6be6
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/one2six.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/one2six.mp3
new file mode 100644
index 0000000..1e2dc01
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/one2six.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/ring_classic_01.ogg b/tests/EmbeddedKitchenSinkApp/res/raw/ring_classic_01.ogg
new file mode 100644
index 0000000..b5e6077
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/ring_classic_01.ogg
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/turnright.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/turnright.mp3
new file mode 100644
index 0000000..6ef127e
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/turnright.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
new file mode 100644
index 0000000..e35ee3d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <dimen name="rvcBtnHeight">40dp</dimen>
+    <dimen name="rvcBtnWidth">150dp</dimen>
+    <dimen name="rvcTextSize">10dp</dimen>
+    <dimen name="rvcTvHeight">80dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
new file mode 100644
index 0000000..9eeeaa9
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string name="app_title">Kitchen Sink</string>
+    <!-- hvac -->
+    <string name="hvac_acOff">AC OFF</string>
+    <string name="hvac_acOn">AC ON</string>
+    <string name="hvac_defrostFrontOff">Front Defrost OFF</string>
+    <string name="hvac_defrostFrontOn">Front Defrost ON</string>
+    <string name="hvac_defrostRearOff">Rear Defrost OFF</string>
+    <string name="hvac_defrostRearOn">Rear Defrost ON</string>
+    <string name="hvac_positionFace">Face</string>
+    <string name="hvac_positionFloor">Floor</string>
+    <string name="hvac_positionFaceAndFloor">Face and Floor</string>
+    <string name="hvac_directionFaceAndDefrost">Face and Defrost</string>
+    <string name="hvac_fanSpeedDn">Fan Speed Down</string>
+    <string name="hvac_fanSpeedUp">Fan Speed Up</string>
+    <string name="hvac_tempDn">Temp Down</string>
+    <string name="hvac_tempUp">Temp Up</string>
+    <!-- camera -->
+    <string name="rvc_getCapabilities">Get Capabilities</string>
+    <string name="rvc_getRvcCrop">Get RVC Crop</string>
+    <string name="rvc_setRvcCrop">Set RVC Crop</string>
+    <string name="rvc_getRvcPos">Get RVC Pos</string>
+    <string name="rvc_setRvcPos">Set RVC Pos</string>
+    <string name="rvc_getCameraState">Get State</string>
+    <string name="rvc_setCameraState">Set State</string>
+    <string name="rvc_state">RVC State</string>
+    <!--  audio -->
+    <string name="empty"></string>
+    <string name="play">Play</string>
+    <string name="play_pcm_once">Play Once</string>
+    <string name="stop">Stop</string>
+    <string name="audio_focus">Audio Focus</string>
+    <string name="focus_gain">Gain</string>
+    <string name="focus_gain_transient_duck">Gain,Transient,Duck</string>
+    <string name="focus_release">Release</string>
+    <string name="request">request</string>
+    <string name="release">release</string>
+    <string name="media_play">Media Play</string>
+    <string name="nav_play">Nav Play</string>
+    <string name="vr_play">VR Play</string>
+    <string name="system_play">System Play</string>
+    <string name="config_index">Config Index</string>
+    <string name="choose_config">Choose config</string>
+    <string name="audio_stress_title">Audio Stress Test</string>
+    <string name="nav_start">Nav Start</string>
+    <string name="nav_end">Nav End</string>
+    <string name="vr_start">VR Start</string>
+    <string name="vr_end">VR End</string>
+    <string name="radio_start">Radio Start</string>
+    <string name="radio_end">Radio End</string>
+    <string name="speaker_phone_on">Speaker Phone On</string>
+    <string name="speaker_phone_off">Speaker Phone Off</string>
+    <string name="microphone_on">Mic On</string>
+    <string name="microphone_off">Mic Off</string>
+    <string name="mock_audio_on">Audio Mocking On</string>
+    <string name="mock_audio_off">Audio Mocking Off</string>
+    <string name="reject_audio_focus_on">Reject Audio Focus</string>
+    <string name="reject_audio_focus_off">Allow Audio Focus</string>
+
+    <!-- job scheduler -->
+    <string name="schedule_button">Schedule a  New Job</string>
+    <string name="refresh_button">Refresh pending jobs</string>
+    <string name="cancel_button">Cancel All Jobs</string>
+    <string name="job_info">Current Pending Jobs</string>
+    <string name="require_charging">Require Charging</string>
+    <string name="require_persisted">Persisted</string>
+    <string name="require_periodic">Periodic</string>
+    <string name="require_network">Network Type</string>
+    <string name="require_idle">Device Idle</string>
+    <string name="dish_num">Number of Plates</string>
+    <string name="default_dish_num">50</string>
+    <string name="network_any">ANY</string>
+    <string name="network_none">NONE</string>
+    <string name="network_unmetered">UNMETERED</string>
+
+    <!-- keyboard test fragment -->
+    <string name="keyboard_test_title">Keyboard Test</string>
+    <string name="on_search">OnSearch:</string>
+    <string name="on_edit">onEdit:</string>
+    <string name="open_ime_button">Open IME</string>
+    <string name="close_ime_button">Close IME</string>
+    <string name="open_kb_button">Hide/Show Input</string>
+
+    <!-- instrument cluster -->
+    <string name="cluster_start">Start Nav</string>
+    <string name="cluster_turn_left">Turn left</string>
+    <string name="cluster_stop">Stop Nav</string>
+    <string name="cluster_nav_app_context_loss">Navigation app context lost!</string>
+
+    <!--  input test -->
+    <string name="volume_up">Volume +</string>
+    <string name="volume_down">Volume -</string>
+    <string name="voice">Voice</string>
+</resources>
diff --git a/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml b/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..9bb6ae7
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<automotiveApp>
+    <uses name="service" />
+    <uses name="projection" />
+    <uses name="activity" class="com.google.android.car.kitchensink.KitchenSinkProxyActivity" />
+</automotiveApp>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
new file mode 100644
index 0000000..29aad2c
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink;
+
+import android.car.Car;
+import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
+import android.car.test.VehicleHalEmulator;
+import android.os.SystemClock;
+
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusState;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStream;
+import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
+import com.android.car.vehiclenetwork.VehiclePropValueUtil;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePermissionModel;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
+
+public class CarEmulator {
+
+    private final Car mCar;
+    private final VehicleHalEmulator mHalEmulator;
+
+    private final AudioFocusPropertyHandler mAudioFocusPropertyHandler =
+            new AudioFocusPropertyHandler();
+    private final AudioStreamStatePropertyHandler mAudioStreamStatePropertyHandler =
+            new AudioStreamStatePropertyHandler();
+
+    public CarEmulator(Car car) {
+        mCar = car;
+        mHalEmulator = new VehicleHalEmulator(car);
+        mHalEmulator.addProperty(
+                VehiclePropConfigUtil.getBuilder(
+                        VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS,
+                        VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
+                        VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3,
+                        VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+                        0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
+                        mAudioFocusPropertyHandler);
+        mHalEmulator.addProperty(
+                VehiclePropConfigUtil.getBuilder(
+                        VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE,
+                        VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
+                        VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2,
+                        VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+                        0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
+                        mAudioStreamStatePropertyHandler);
+    }
+
+    public void start() {
+        mHalEmulator.start();
+    }
+
+    public void stop() {
+        mHalEmulator.stop();
+    }
+
+    public void setAudioFocusControl(boolean reject) {
+        mAudioFocusPropertyHandler.setAudioFocusControl(reject);
+    }
+
+    private class AudioFocusPropertyHandler implements VehicleHalPropertyHandler {
+        private boolean mRejectFocus;
+        private int mCurrentFocusState = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+        private int mCurrentFocusStreams = 0;
+        private int mCurrentFocusExtState = 0;
+
+        public void setAudioFocusControl(boolean reject) {
+            VehiclePropValue injectValue = null;
+            synchronized (this) {
+                if (reject) {
+                    if (!mRejectFocus) {
+                        mCurrentFocusState =
+                                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE;
+                        mCurrentFocusStreams = 0;
+                        mCurrentFocusExtState = 0;
+                        mRejectFocus = true;
+                        int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+                                mCurrentFocusExtState };
+                        injectValue = VehiclePropValueUtil.createIntVectorValue(
+                                VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+                                SystemClock.elapsedRealtimeNanos());
+                    }
+                } else {
+                    if (mRejectFocus) {
+                        mCurrentFocusState = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+                        mCurrentFocusStreams = 0;
+                        mCurrentFocusExtState = 0;
+                        int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+                                mCurrentFocusExtState };
+                        injectValue = VehiclePropValueUtil.createIntVectorValue(
+                                VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+                                SystemClock.elapsedRealtimeNanos());
+                    }
+                    mRejectFocus = false;
+                }
+            }
+            if (injectValue != null) {
+                mHalEmulator.injectEvent(injectValue);
+            }
+        }
+
+        @Override
+        public void onPropertySet(VehiclePropValue value) {
+            VehiclePropValue injectValue = null;
+            synchronized (this) {
+                if (mRejectFocus) {
+                    int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+                            mCurrentFocusExtState };
+                    injectValue = VehiclePropValueUtil.createIntVectorValue(
+                            VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+                            SystemClock.elapsedRealtimeNanos());
+                } else {
+                    int request = value.getInt32Values(
+                            VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_FOCUS);
+                    int requestedStreams = value.getInt32Values(
+                            VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
+                    int requestedExtFocus = value.getInt32Values(
+                            VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
+                    int response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+                    switch (request) {
+                        case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN:
+                            response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN;
+                            break;
+                        case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT:
+                        case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK:
+                            response =
+                                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT;
+                            break;
+                        case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE:
+                            response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+                            break;
+                    }
+                    mCurrentFocusState = response;
+                    mCurrentFocusStreams = requestedStreams;
+                    mCurrentFocusExtState = requestedExtFocus;
+                    int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+                            mCurrentFocusExtState };
+                    injectValue = VehiclePropValueUtil.createIntVectorValue(
+                            VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+                            SystemClock.elapsedRealtimeNanos());
+                }
+            }
+            if (injectValue != null) {
+                mHalEmulator.injectEvent(injectValue);
+            }
+        }
+
+        @Override
+        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
+            int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+                    mCurrentFocusExtState };
+            return VehiclePropValueUtil.createIntVectorValue(
+                    VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+                    SystemClock.elapsedRealtimeNanos());
+        }
+
+        @Override
+        public void onPropertySubscribe(int property, float sampleRate, int zones) {
+        }
+
+        @Override
+        public void onPropertyUnsubscribe(int property) {
+        }
+    }
+
+    private class AudioStreamStatePropertyHandler implements VehicleHalPropertyHandler {
+        @Override
+        public void onPropertySet(VehiclePropValue value) {
+        }
+
+        @Override
+        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
+            //ignore
+            return null;
+        }
+
+        @Override
+        public void onPropertySubscribe(int property, float sampleRate, int zones) {
+        }
+
+        @Override
+        public void onPropertyUnsubscribe(int property) {
+        }
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
new file mode 100644
index 0000000..60aafe8
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink;
+
+import com.google.android.car.kitchensink.audio.AudioTestFragment;
+import com.google.android.car.kitchensink.camera.CameraTestFragment;
+import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment;
+import com.google.android.car.kitchensink.hvac.HvacTestFragment;
+import com.google.android.car.kitchensink.input.InputTestFragment;
+import com.google.android.car.kitchensink.job.JobSchedulerFragment;
+import com.google.android.car.kitchensink.keyboard.KeyboardFragment;
+
+import android.car.hardware.camera.CarCameraManager;
+import android.car.hardware.hvac.CarHvacManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.car.Car;
+import android.support.car.CarAppContextManager;
+import android.support.car.CarNotConnectedException;
+import android.support.car.ServiceConnectionListener;
+import android.support.car.app.menu.CarDrawerActivity;
+import android.support.car.app.menu.CarMenu;
+import android.support.car.app.menu.CarMenuCallbacks;
+import android.support.car.app.menu.RootMenu;
+import android.support.car.hardware.CarSensorEvent;
+import android.support.car.hardware.CarSensorManager;
+import android.support.car.navigation.CarNavigationManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class KitchenSinkActivity extends CarDrawerActivity {
+    private static final String TAG = "KitchenSinkActivity";
+
+    private static final String MENU_AUDIO = "audio";
+    private static final String MENU_CAMERA = "camera";
+    private static final String MENU_HVAC = "hvac";
+    private static final String MENU_QUIT = "quit";
+    private static final String MENU_JOB = "job_scheduler";
+    private static final String MENU_KEYBOARD = "keyboard";
+    private static final String MENU_CLUSTER = "inst cluster";
+    private static final String MENU_INPUT_TEST = "input test";
+
+    private Car mCarApi;
+    private CarCameraManager mCameraManager;
+    private CarHvacManager mHvacManager;
+    private CarSensorManager mCarSensorManager;
+    private CarNavigationManager mCarNavigationManager;
+    private CarAppContextManager mCarAppContextManager;
+
+
+    private AudioTestFragment mAudioTestFragment;
+    private CameraTestFragment mCameraTestFragment;
+    private HvacTestFragment mHvacTestFragment;
+    private JobSchedulerFragment mJobFragment;
+    private KeyboardFragment mKeyboardFragment;
+    private InstrumentClusterFragment mInstrumentClusterFragment;
+    private InputTestFragment mInputTestFragment;
+
+    private final CarSensorManager.CarSensorEventListener mListener =
+            new CarSensorManager.CarSensorEventListener() {
+        @Override
+        public void onSensorChanged(CarSensorEvent event) {
+            switch (event.sensorType) {
+                case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
+                    Log.d(TAG, "driving status:" + event.intValues[0]);
+                    break;
+            }
+        }
+    };
+
+    public KitchenSinkActivity(Proxy proxy, Context context, Car car) {
+        super(proxy, context, car);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        resetTitle();
+        setScrimColor(Color.LTGRAY);
+        setLightMode();
+        setCarMenuCallbacks(new MyCarMenuCallbacks());
+        setContentView(R.layout.kitchen_sink_activity);
+
+        // Connection to Car Service does not work for non-automotive yet.
+        if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            mCarApi = Car.createCar(getContext(), mServiceConnectionListener);
+            mCarApi.connect();
+        }
+        Log.i(TAG, "onCreate");
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Log.i(TAG, "onStart");
+    }
+
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        Log.i(TAG, "onRestart");
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.i(TAG, "onResume");
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        Log.i(TAG, "onPause");
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Log.i(TAG, "onStop");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mCarSensorManager != null) {
+            mCarSensorManager.unregisterListener(mListener);
+        }
+        if (mCarApi != null) {
+            mCarApi.disconnect();
+        }
+        Log.i(TAG, "onDestroy");
+    }
+
+    private void resetTitle() {
+        setTitle(getContext().getString(R.string.app_title));
+    }
+
+    private final ServiceConnectionListener mServiceConnectionListener =
+            new ServiceConnectionListener() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, "Connected to Car Service");
+            try {
+                mCameraManager = (CarCameraManager) mCarApi.getCarManager(android.car.Car
+                        .CAMERA_SERVICE);
+                mHvacManager = (CarHvacManager) mCarApi.getCarManager(android.car.Car.HVAC_SERVICE);
+                mCarNavigationManager = (CarNavigationManager) mCarApi.getCarManager(
+                        android.car.Car.CAR_NAVIGATION_SERVICE);
+                mCarSensorManager = (CarSensorManager) mCarApi.getCarManager(Car.SENSOR_SERVICE);
+                mCarSensorManager.registerListener(mListener,
+                        CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+                        CarSensorManager.SENSOR_RATE_NORMAL);
+                mCarAppContextManager =
+                        (CarAppContextManager) mCarApi.getCarManager(Car.APP_CONTEXT_SERVICE);
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "Car is not connected!");
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.d(TAG, "Disconnect from Car Service");
+        }
+
+        @Override
+        public void onServiceSuspended(int cause) {
+            Log.d(TAG, "Car Service connection suspended");
+        }
+
+        @Override
+        public void onServiceConnectionFailed(int cause) {
+            Log.d(TAG, "Car Service connection failed");
+        }
+    };
+
+    private final class MyCarMenuCallbacks extends CarMenuCallbacks {
+        /** Id for the root menu */
+        private static final String ROOT = "ROOT";
+
+        @Override
+        public RootMenu onGetRoot(Bundle hints) {
+            return new RootMenu(ROOT);
+        }
+
+        @Override
+        public void onLoadChildren(String parentId, CarMenu result) {
+            List<CarMenu.Item> items = new ArrayList<>();
+            if (parentId.equals(ROOT)) {
+                String[] allMenus = {
+                        MENU_AUDIO, MENU_CAMERA, MENU_HVAC, MENU_JOB, MENU_KEYBOARD, MENU_CLUSTER,
+                        MENU_INPUT_TEST, MENU_QUIT
+                };
+                for (String menu : allMenus) {
+                    items.add(new CarMenu.Builder(menu).setText(menu).build());
+                }
+            }
+            result.sendResult(items);
+        }
+
+        @Override
+        public void onItemClicked(String id) {
+            Log.d(TAG, "onItemClicked id=" + id);
+            if (id.equals(MENU_AUDIO)) {
+                if (mAudioTestFragment == null) {
+                    mAudioTestFragment = new AudioTestFragment();
+                }
+                setContentFragment(mAudioTestFragment);
+            } else if (id.equals(MENU_CAMERA)) {
+                if (mCameraManager != null) {
+                    if (mCameraTestFragment == null) {
+                        mCameraTestFragment = new CameraTestFragment();
+                        mCameraTestFragment.setCameraManager(mCameraManager);
+                    }
+                    // Don't allow camera fragment to start if we don't have a manager.
+                    setContentFragment(mCameraTestFragment);
+                }
+            } else if (id.equals(MENU_HVAC)) {
+                if (mHvacManager != null) {
+                    if (mHvacTestFragment == null) {
+                        mHvacTestFragment = new HvacTestFragment();
+                        mHvacTestFragment.setHvacManager(mHvacManager);
+                    }
+                    // Don't allow HVAC fragment to start if we don't have a manager.
+                    setContentFragment(mHvacTestFragment);
+                }
+            } else if (id.equals(MENU_JOB)) {
+                if (mJobFragment == null) {
+                    mJobFragment = new JobSchedulerFragment();
+                }
+                setContentFragment(mJobFragment);
+            } else if (id.equals(MENU_KEYBOARD)) {
+                if (mKeyboardFragment == null) {
+                    mKeyboardFragment = new KeyboardFragment();
+                }
+                setContentFragment(mKeyboardFragment);
+            } else if (id.equals(MENU_CLUSTER)) {
+                if (mInstrumentClusterFragment == null) {
+                    mInstrumentClusterFragment = new InstrumentClusterFragment();
+                    mInstrumentClusterFragment.setCarNavigationManager(mCarNavigationManager);
+                    mInstrumentClusterFragment.setCarAppContextManager(mCarAppContextManager);
+                }
+                setContentFragment(mInstrumentClusterFragment);
+            } else if (id.equals(MENU_INPUT_TEST)) {
+                if (mInputTestFragment == null) {
+                    mInputTestFragment = new InputTestFragment();
+                }
+                setContentFragment(mInputTestFragment);
+            } else if (id.equals(MENU_QUIT)) {
+                finish();
+            }
+        }
+
+        @Override
+        public void onCarMenuClosed() {
+            resetTitle();
+        }
+    }
+}
diff --git a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarProxyActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkProxyActivity.java
similarity index 77%
rename from tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarProxyActivity.java
rename to tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkProxyActivity.java
index ce5ed1a..40bebd9 100644
--- a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarProxyActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkProxyActivity.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.support.car.test.caractivitytest;
+package com.google.android.car.kitchensink;
 
 import android.support.car.app.CarProxyActivity;
 
-public class HelloCarProxyActivity extends CarProxyActivity {
+public class KitchenSinkProxyActivity extends CarProxyActivity {
 
-    public HelloCarProxyActivity() {
-        super(HelloCarActivity.class);
+    public KitchenSinkProxyActivity() {
+        super(KitchenSinkActivity.class, true);
     }
 }
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
new file mode 100644
index 0000000..618367d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.audio;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.util.Log;
+
+import com.google.android.car.kitchensink.R;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Class for playing music.
+ *
+ * This is not thread safe and all public calls should be made from main thread.
+ *
+ * MP3 used is from http://freemusicarchive.org/music/John_Harrison_with_the_Wichita_State_University_Chamber_Players/The_Four_Seasons_Vivaldi/05_-_Vivaldi_Summer_mvt_2_Adagio_-_John_Harrison_violin
+ * from John Harrison with the Wichita State University Chamber Players
+ * Copyright under Create Commons license.
+ */
+public class AudioPlayer {
+
+    public interface PlayStateListener {
+        void onCompletion();
+    }
+
+    private static final String TAG = AudioPlayer.class.getSimpleName();
+
+    private final AudioManager.OnAudioFocusChangeListener mFocusListener =
+            new AudioManager.OnAudioFocusChangeListener() {
+
+        @Override
+        public void onAudioFocusChange(int focusChange) {
+            Log.i(TAG, "audio focus change " + focusChange);
+            if (mPlayer == null) {
+                return;
+            }
+            if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+                mPlayer.setVolume(1.0f, 1.0f);
+                if (mRepeat && isPlaying()) {
+                    doResume();
+                }
+            } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+                if (isPlaying()) {
+                    mPlayer.setVolume(0.5f, 0.5f);
+                }
+            } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT && mRepeat) {
+                if (isPlaying()) {
+                    doPause();
+                }
+            } else {
+                if (isPlaying()) {
+                    doStop();
+                }
+            }
+        }
+    };
+
+    private AudioManager mAudioManager;
+    private MediaPlayer mPlayer;
+
+    private final Context mContext;
+    private final int mResourceId;
+    private final AudioAttributes mAttrib;
+
+    private final AtomicBoolean mPlaying = new AtomicBoolean(false);
+
+    private volatile boolean mHandleFocus;
+    private volatile boolean mRepeat;
+
+    private PlayStateListener mListener;
+
+    public AudioPlayer(Context context, int resourceId, AudioAttributes attrib) {
+        mContext = context;
+        mResourceId = resourceId;
+        mAttrib = attrib;
+    }
+
+    public void start(boolean handleFocus, boolean repeat, int focusRequest) {
+        mHandleFocus = handleFocus;
+        mRepeat = repeat;
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        int ret = AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+        if (mHandleFocus) {
+            ret = mAudioManager.requestAudioFocus(mFocusListener, mAttrib,
+                    focusRequest, 0);
+        }
+        if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+            doStart();
+        } else {
+            Log.i(TAG, "no focus");
+        }
+    }
+
+    public void start(boolean handleFocus, boolean repeat, int focusRequest,
+            PlayStateListener listener) {
+        mListener = listener;
+        start(handleFocus, repeat, focusRequest);
+    }
+
+    private void doStart() {
+        if (mPlaying.getAndSet(true)) {
+            Log.i(TAG, "already playing");
+            return;
+        }
+        Log.i(TAG, "doStart audio");
+        mPlayer = new MediaPlayer();
+        mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+
+            @Override
+            public boolean onError(MediaPlayer mp, int what, int extra) {
+                Log.e(TAG, "audio error what " + what + " extra " + extra);
+                mPlaying.set(false);
+                if (!mRepeat && mHandleFocus) {
+                    mPlayer.stop();
+                    mPlayer.release();
+                    mPlayer = null;
+                    mAudioManager.abandonAudioFocus(mFocusListener);
+                }
+                return false;
+            }
+
+        });
+        mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                mPlaying.set(false);
+                if (!mRepeat && mHandleFocus) {
+                    mPlayer.stop();
+                    mPlayer.release();
+                    mPlayer = null;
+                    mAudioManager.abandonAudioFocus(mFocusListener);
+                    if (mListener != null) {
+                        mListener.onCompletion();
+                    }
+                }
+            }
+        });
+        mPlayer.setAudioAttributes(mAttrib);
+        mPlayer.setLooping(mRepeat);
+        mPlayer.setVolume(1.0f, 1.0f);
+        try {
+            AssetFileDescriptor afd =
+                    mContext.getResources().openRawResourceFd(mResourceId);
+            if (afd == null) {
+                throw new RuntimeException("no res");
+            }
+            mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
+                    afd.getLength());
+            afd.close();
+            mPlayer.prepare();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        mPlayer.start();
+    }
+
+    public void stop() {
+        doStop();
+        if (mHandleFocus) {
+            mAudioManager.abandonAudioFocus(mFocusListener);
+        }
+    }
+
+    public void release() {
+        if (isPlaying()) {
+            stop();
+        }
+    }
+
+    private void doStop() {
+        if (!mPlaying.getAndSet(false)) {
+            Log.i(TAG, "already stopped");
+            return;
+        }
+        Log.i(TAG, "doStop audio");
+        mPlayer.stop();
+        mPlayer.release();
+        mPlayer = null;
+    }
+
+    private void doPause() {
+        mPlayer.pause();
+    }
+
+    private void doResume() {
+        mPlayer.start();
+    }
+
+    public boolean isPlaying() {
+        return mPlaying.get();
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
new file mode 100644
index 0000000..5ac40ea
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.audio;
+
+import android.car.Car;
+import android.car.CarAppContextManager;
+import android.car.CarAppContextManager.AppContextChangeListener;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.google.android.car.kitchensink.CarEmulator;
+import com.google.android.car.kitchensink.R;
+import com.google.android.car.kitchensink.audio.AudioPlayer.PlayStateListener;
+
+public class AudioTestFragment extends Fragment {
+    private static final String TAG = "CAR.AUDIO.KS";
+    private static final boolean DBG = true;
+
+    private AudioManager mAudioManager;
+    private FocusHandler mAudioFocusHandler;
+    private Button mNavPlayOnce;
+    private Button mVrPlayOnce;
+    private Button mSystemPlayOnce;
+    private Button mMediaPlay;
+    private Button mMediaPlayOnce;
+    private Button mMediaStop;
+    private Button mNavStart;
+    private Button mNavEnd;
+    private Button mVrStart;
+    private Button mVrEnd;
+    private Button mRadioStart;
+    private Button mRadioEnd;
+    private Button mSpeakerPhoneOn;
+    private Button mSpeakerPhoneOff;
+    private Button mMicrophoneOn;
+    private Button mMicrophoneOff;
+    private ToggleButton mEnableMocking;
+    private ToggleButton mRejectFocus;
+
+    private AudioPlayer mMusicPlayer;
+    private AudioPlayer mMusicPlayerShort;
+    private AudioPlayer mNavGuidancePlayer;
+    private AudioPlayer mVrPlayer;
+    private AudioPlayer mSystemPlayer;
+    private AudioPlayer[] mAllPlayers;
+
+    private Handler mHandler;
+    private Context mContext;
+
+    private Car mCar;
+    private CarAppContextManager mAppContextManager;
+    private CarAudioManager mCarAudioManager;
+    private AudioAttributes mMusicAudioAttrib;
+    private AudioAttributes mNavAudioAttrib;
+    private AudioAttributes mVrAudioAttrib;
+    private AudioAttributes mRadioAudioAttrib;
+    private AudioAttributes mSystemSoundAudioAttrib;
+    private CarEmulator mCarEmulator;
+
+    private final AudioManager.OnAudioFocusChangeListener mNavFocusListener =
+            new AudioManager.OnAudioFocusChangeListener() {
+                @Override
+                public void onAudioFocusChange(int focusChange) {
+                    Log.i(TAG, "Nav focus change:" + focusChange);
+                }
+    };
+    private final AudioManager.OnAudioFocusChangeListener mVrFocusListener =
+            new AudioManager.OnAudioFocusChangeListener() {
+                @Override
+                public void onAudioFocusChange(int focusChange) {
+                    Log.i(TAG, "VR focus change:" + focusChange);
+                }
+    };
+    private final AudioManager.OnAudioFocusChangeListener mRadioFocusListener =
+            new AudioManager.OnAudioFocusChangeListener() {
+                @Override
+                public void onAudioFocusChange(int focusChange) {
+                    Log.i(TAG, "Radio focus change:" + focusChange);
+                }
+    };
+
+    private void init() {
+        mContext = getContext();
+        mHandler = new Handler(Looper.getMainLooper());
+        mCar = Car.createCar(mContext, new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                mAppContextManager =
+                        (CarAppContextManager) mCar.getCarManager(Car.APP_CONTEXT_SERVICE);
+                mAppContextManager.registerContextListener(new AppContextChangeListener() {
+
+                    @Override
+                    public void onAppContextOwnershipLoss(int context) {
+                    }
+
+                    @Override
+                    public void onAppContextChange(int activeContexts) {
+                    }
+                }, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+                mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+                mMusicAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+                        CarAudioManager.CAR_AUDIO_USAGE_MUSIC);
+                mNavAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+                        CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE);
+                mVrAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+                        CarAudioManager.CAR_AUDIO_USAGE_VOICE_COMMAND);
+                mRadioAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+                        CarAudioManager.CAR_AUDIO_USAGE_RADIO);
+                mSystemSoundAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+                        CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND);
+                mMusicPlayer = new AudioPlayer(mContext, R.raw.john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio,
+                        mMusicAudioAttrib);
+                mMusicPlayerShort = new AudioPlayer(mContext, R.raw.ring_classic_01,
+                        mMusicAudioAttrib);
+                mNavGuidancePlayer = new AudioPlayer(mContext, R.raw.turnright,
+                        mNavAudioAttrib);
+                // no Usage for voice command yet.
+                mVrPlayer = new AudioPlayer(mContext, R.raw.one2six,
+                        mVrAudioAttrib);
+                mSystemPlayer = new AudioPlayer(mContext, R.raw.ring_classic_01,
+                        mSystemSoundAudioAttrib);
+                mAllPlayers = new AudioPlayer[] {
+                        mMusicPlayer,
+                        mMusicPlayerShort,
+                        mNavGuidancePlayer,
+                        mVrPlayer,
+                        mSystemPlayer
+                };
+            }
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        }, Looper.getMainLooper());
+        mCar.connect();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+        Log.i(TAG, "onCreateView");
+        init();
+        View view = inflater.inflate(R.layout.audio, container, false);
+        mAudioManager = (AudioManager) mContext.getSystemService(
+                Context.AUDIO_SERVICE);
+        mAudioFocusHandler = new FocusHandler(
+                (RadioGroup) view.findViewById(R.id.button_focus_request_selection),
+                (Button) view.findViewById(R.id.button_audio_focus_request),
+                (TextView) view.findViewById(R.id.text_audio_focus_state));
+        mMediaPlay = (Button) view.findViewById(R.id.button_media_play_start);
+        mMediaPlay.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mMusicPlayer.start(false, true, AudioManager.AUDIOFOCUS_GAIN);
+            }
+        });
+        mMediaPlayOnce = (Button) view.findViewById(R.id.button_media_play_once);
+        mMediaPlayOnce.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mMusicPlayerShort.start(true, false, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+                // play only for 1 sec and stop
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        mMusicPlayerShort.stop();
+                    }
+                }, 1000);
+            }
+        });
+        mMediaStop = (Button) view.findViewById(R.id.button_media_play_stop);
+        mMediaStop.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mMusicPlayer.stop();
+            }
+        });
+        mNavPlayOnce = (Button) view.findViewById(R.id.button_nav_play_once);
+        mNavPlayOnce.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mAppContextManager == null) {
+                    return;
+                }
+                if (DBG) {
+                    Log.i(TAG, "Nav start");
+                }
+                if (!mNavGuidancePlayer.isPlaying()) {
+                    mAppContextManager.setActiveContexts(
+                            CarAppContextManager.APP_CONTEXT_NAVIGATION);
+                    mNavGuidancePlayer.start(true, false,
+                            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+                            new PlayStateListener() {
+                        @Override
+                        public void onCompletion() {
+                            mAppContextManager.resetActiveContexts(
+                                    CarAppContextManager.APP_CONTEXT_NAVIGATION);
+                        }
+                    });
+                }
+            }
+        });
+        mVrPlayOnce = (Button) view.findViewById(R.id.button_vr_play_once);
+        mVrPlayOnce.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mAppContextManager == null) {
+                    return;
+                }
+                if (DBG) {
+                    Log.i(TAG, "VR start");
+                }
+                mAppContextManager.setActiveContexts(
+                        CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+                if (!mVrPlayer.isPlaying()) {
+                    mVrPlayer.start(true, false, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
+                            new PlayStateListener() {
+                        @Override
+                        public void onCompletion() {
+                            mAppContextManager.resetActiveContexts(
+                                    CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+                        }
+                    });
+                }
+            }
+        });
+        mSystemPlayOnce = (Button) view.findViewById(R.id.button_system_play_once);
+        mSystemPlayOnce.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (DBG) {
+                    Log.i(TAG, "System start");
+                }
+                if (!mSystemPlayer.isPlaying()) {
+                    // system sound played without focus
+                    mSystemPlayer.start(false, false, 0);
+                }
+            }
+        });
+        mNavStart = (Button) view.findViewById(R.id.button_nav_start);
+        mNavStart.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                handleNavStart();
+            }
+        });
+        mNavEnd = (Button) view.findViewById(R.id.button_nav_end);
+        mNavEnd.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                handleNavEnd();
+            }
+        });
+        mVrStart = (Button) view.findViewById(R.id.button_vr_start);
+        mVrStart.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                handleVrStart();
+            }
+        });
+        mVrEnd = (Button) view.findViewById(R.id.button_vr_end);
+        mVrEnd.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                handleVrEnd();
+            }
+        });
+        mRadioStart = (Button) view.findViewById(R.id.button_radio_start);
+        mRadioStart.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                handleRadioStart();
+            }
+        });
+        mRadioEnd = (Button) view.findViewById(R.id.button_radio_end);
+        mRadioEnd.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                handleRadioEnd();
+            }
+        });
+        mSpeakerPhoneOn = (Button) view.findViewById(R.id.button_speaker_phone_on);
+        mSpeakerPhoneOn.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAudioManager.setSpeakerphoneOn(true);
+            }
+        });
+        mSpeakerPhoneOff = (Button) view.findViewById(R.id.button_speaker_phone_off);
+        mSpeakerPhoneOff.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAudioManager.setSpeakerphoneOn(false);
+            }
+        });
+        mMicrophoneOn = (Button) view.findViewById(R.id.button_microphone_on);
+        mMicrophoneOn.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAudioManager.setMicrophoneMute(false); // Turn the microphone on.
+            }
+        });
+        mMicrophoneOff = (Button) view.findViewById(R.id.button_microphone_off);
+        mMicrophoneOff.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAudioManager.setMicrophoneMute(true); // Mute the microphone.
+            }
+        });
+
+
+        mRejectFocus = (ToggleButton) view.findViewById(R.id.button_reject_audio_focus);
+        mRejectFocus.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (mCarEmulator == null) {
+                    return;
+                }
+                if (!mEnableMocking.isChecked()) {
+                    return;
+                }
+                if (isChecked) {
+                    mCarEmulator.setAudioFocusControl(true);
+                } else {
+                    mCarEmulator.setAudioFocusControl(false);
+                }
+            }
+        });
+        mRejectFocus.setActivated(false);
+        mEnableMocking = (ToggleButton) view.findViewById(R.id.button_mock_audio);
+        mEnableMocking.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (mCarEmulator == null) {
+                    mCarEmulator = new CarEmulator(mCar);
+                }
+                if (isChecked) {
+                    mRejectFocus.setActivated(true);
+                    mCarEmulator.start();
+                } else {
+                    mRejectFocus.setActivated(false);
+                    mCarEmulator.stop();
+                    mCarEmulator = null;
+                }
+            }
+        });
+        return view;
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        Log.i(TAG, "onDestroyView");
+        if (mCarEmulator != null) {
+            mCarEmulator.setAudioFocusControl(false);
+            mCarEmulator.stop();
+        }
+        for (AudioPlayer p : mAllPlayers) {
+            p.stop();
+        }
+        if (mAudioFocusHandler != null) {
+            mAudioFocusHandler.release();
+            mAudioFocusHandler = null;
+        }
+        if (mAppContextManager != null) {
+            mAppContextManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                    CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        }
+    }
+
+    private void handleNavStart() {
+        if (mAppContextManager == null) {
+            return;
+        }
+        if (mCarAudioManager == null) {
+            return;
+        }
+        if (DBG) {
+            Log.i(TAG, "Nav start");
+        }
+        mAppContextManager.setActiveContexts(
+                CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        mCarAudioManager.requestAudioFocus(mNavFocusListener, mNavAudioAttrib,
+                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+    }
+
+    private void handleNavEnd() {
+        if (mAppContextManager == null) {
+            return;
+        }
+        if (mCarAudioManager == null) {
+            return;
+        }
+        if (DBG) {
+            Log.i(TAG, "Nav end");
+        }
+        mAppContextManager.resetActiveContexts(
+                CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        mCarAudioManager.abandonAudioFocus(mNavFocusListener, mNavAudioAttrib);
+    }
+
+    private void handleVrStart() {
+        if (mAppContextManager == null) {
+            return;
+        }
+        if (mCarAudioManager == null) {
+            return;
+        }
+        if (DBG) {
+            Log.i(TAG, "VR start");
+        }
+        mAppContextManager.setActiveContexts(
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        mCarAudioManager.requestAudioFocus(mVrFocusListener, mVrAudioAttrib,
+                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 0);
+    }
+
+    private void handleVrEnd() {
+        if (mAppContextManager == null) {
+            return;
+        }
+        if (mCarAudioManager == null) {
+            return;
+        }
+        if (DBG) {
+            Log.i(TAG, "VR end");
+        }
+        mAppContextManager.resetActiveContexts(
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        mCarAudioManager.abandonAudioFocus(mVrFocusListener, mVrAudioAttrib);
+    }
+
+    private void handleRadioStart() {
+        if (mCarAudioManager == null) {
+            return;
+        }
+        if (DBG) {
+            Log.i(TAG, "Radio start");
+        }
+        mCarAudioManager.requestAudioFocus(mRadioFocusListener, mRadioAudioAttrib,
+                AudioManager.AUDIOFOCUS_GAIN, 0);
+    }
+
+    private void handleRadioEnd() {
+        if (mCarAudioManager == null) {
+            return;
+        }
+        if (DBG) {
+            Log.i(TAG, "Radio end");
+        }
+        mCarAudioManager.abandonAudioFocus(mRadioFocusListener, mRadioAudioAttrib);
+    }
+
+    private class FocusHandler {
+        private static final String AUDIO_FOCUS_STATE_GAIN = "gain";
+        private static final String AUDIO_FOCUS_STATE_RELEASED_UNKNOWN = "released / unknown";
+
+        private final RadioGroup mRequestSelection;
+        private final TextView mText;
+        private final AudioFocusListener mFocusListener;
+
+        public FocusHandler(RadioGroup radioGroup, Button requestButton, TextView text) {
+            mText = text;
+            mRequestSelection = radioGroup;
+            mRequestSelection.check(R.id.focus_gain);
+            setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
+            mFocusListener = new AudioFocusListener();
+            requestButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    int selectedButtonId = mRequestSelection.getCheckedRadioButtonId();
+                    int focusRequest = AudioManager.AUDIOFOCUS_GAIN;
+                    if (selectedButtonId == R.id.focus_gain_transient_duck) {
+                        focusRequest = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
+                    } else if (selectedButtonId == R.id.focus_release) {
+                        mAudioManager.abandonAudioFocus(mFocusListener);
+                        setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
+                        return;
+                    }
+                    int ret = mAudioManager.requestAudioFocus(mFocusListener,
+                            AudioManager.STREAM_MUSIC, focusRequest);
+                    Log.i(TAG, "requestAudioFocus returned " + ret);
+                    if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+                        setFocusText(AUDIO_FOCUS_STATE_GAIN);
+                    }
+                }
+            });
+        }
+
+        public void release() {
+            abandonAudioFocus();
+        }
+
+        private void abandonAudioFocus() {
+            if (DBG) {
+                Log.i(TAG, "abandonAudioFocus");
+            }
+            mAudioManager.abandonAudioFocus(mFocusListener);
+            setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
+        }
+
+        private void setFocusText(String msg) {
+            mText.setText("focus state:" + msg);
+        }
+
+        private class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
+            @Override
+            public void onAudioFocusChange(int focusChange) {
+                Log.i(TAG, "onAudioFocusChange " + focusChange);
+                if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+                    setFocusText(AUDIO_FOCUS_STATE_GAIN);
+                } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+                    setFocusText("loss");
+                } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+                    setFocusText("loss,transient");
+                } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+                    setFocusText("loss,transient,duck");
+                }
+            }
+        }
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/camera/CameraTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/camera/CameraTestFragment.java
new file mode 100644
index 0000000..8d68cf4
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/camera/CameraTestFragment.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.camera;
+
+import android.os.Bundle;
+import android.car.CarNotConnectedException;
+import android.car.hardware.camera.CarCamera;
+import android.car.hardware.camera.CarCameraState;
+import android.car.hardware.camera.CarCameraManager;
+import android.graphics.Rect;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.google.android.car.kitchensink.R;
+
+import java.lang.Override;
+
+public class CameraTestFragment extends Fragment {
+    private final boolean DBG = true;
+    private final String TAG = "CameraTestFragment";
+    private TextView mTvCap;
+    private TextView mTvRvcCrop;
+    private TextView mTvRvcPos;
+    private TextView mTvCameraState;
+    private CarCameraManager mCarCameraManager;
+    private CarCamera mRvcCamera;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+        View v = inflater.inflate(R.layout.camera_test, container, false);
+
+        int[] cameraList = mCarCameraManager.getCameraList();
+        for (int camera : cameraList) {
+            if (camera == CarCameraManager.CAR_CAMERA_TYPE_RVC) {
+                mRvcCamera = mCarCameraManager.openCamera(1);
+                break;
+            }
+        }
+
+        mTvCap = (TextView)v.findViewById(R.id.tvCap);
+        mTvRvcCrop = (TextView)v.findViewById(R.id.tvRvcCrop);
+        mTvRvcPos = (TextView)v.findViewById(R.id.tvRvcPos);
+        mTvCameraState = (TextView)v.findViewById(R.id.tvCameraState);
+
+        Button btn = (Button) v.findViewById(R.id.btnGetCap);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                int cap = mRvcCamera.getCapabilities();
+                mTvCap.setText(String.valueOf(cap));
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnGetRvcCrop);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Rect rect = mRvcCamera.getCameraCrop();
+                if(rect != null) {
+                    mTvRvcCrop.setText(rect.toString());
+                } else {
+                    mTvRvcCrop.setText("null");
+                }
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnGetRvcPos);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Rect rect = mRvcCamera.getCameraPosition();
+                if(rect != null) {
+                    mTvRvcPos.setText(String.valueOf(rect));
+                } else {
+                    mTvRvcPos.setText("null");
+                }
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnGetCameraState);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                CarCameraState state = mRvcCamera.getCameraState();
+                if(state != null) {
+                    mTvCameraState.setText(state.toString());
+                } else {
+                    mTvCameraState.setText("null");
+                }
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnSetRvcCrop);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Rect rect = new Rect(160, 240, 560, 480);
+                mRvcCamera.setCameraCrop(rect);
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnSetRvcCrop2);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Rect rect = new Rect(0, 0, 720, 480);
+                mRvcCamera.setCameraCrop(rect);
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnSetRvcPos);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Rect rect = new Rect(300, 0, 800, 480);
+                mRvcCamera.setCameraPosition(rect);
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnSetRvcPos2);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Rect rect = new Rect(500, 0, 800, 480);
+                mRvcCamera.setCameraPosition(rect);
+            }
+        });
+
+        btn = (Button) v.findViewById(R.id.btnSetRvcPos3);
+        btn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Rect rect = new Rect(300, 0, 500, 300);
+                mRvcCamera.setCameraPosition(rect);
+            }
+        });
+
+        final ToggleButton toggleBtn = (ToggleButton) v.findViewById(R.id.btnRvcState);
+        toggleBtn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                CarCameraState state = new CarCameraState(false, toggleBtn.isChecked());
+                mRvcCamera.setCameraState(state);
+            }
+        });
+
+        if(DBG) {
+            Log.d(TAG, "Starting CameraTestFragment");
+        }
+        return v;
+    }
+
+    public void setCameraManager(CarCameraManager cameraManager) {
+        Log.d(TAG, "setCameraManager()");
+        mCarCameraManager = cameraManager;
+    }
+}
+
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
new file mode 100644
index 0000000..f3bc327
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.cluster;
+
+import com.google.android.car.kitchensink.R;
+
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.car.CarAppContextManager;
+import android.support.car.CarAppContextManager.AppContextChangeListener;
+import android.support.car.CarNotConnectedException;
+import android.support.car.navigation.CarNavigationManager;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+
+/**
+ * Contains functions to test instrument cluster API.
+ */
+public class InstrumentClusterFragment extends Fragment {
+    private static final String TAG = InstrumentClusterFragment.class.getSimpleName();
+
+    private CarNavigationManager mCarNavigationManager;
+    private CarAppContextManager mCarAppContextManager;
+
+    public void setCarNavigationManager(CarNavigationManager carNavigationManager) {
+        mCarNavigationManager = carNavigationManager;
+    }
+
+    public void setCarAppContextManager(CarAppContextManager carAppContextManager) {
+        mCarAppContextManager = carAppContextManager;
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.instrument_cluster, container);
+
+        view.findViewById(R.id.cluster_start_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                initCluster();
+            }
+        });
+        view.findViewById(R.id.cluster_turn_left_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                turnLeft();
+            }
+        });
+
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+
+    private void turnLeft() {
+        try {
+            mCarNavigationManager.sendNavigationTurnEvent(CarNavigationManager.TURN_TURN,
+                    "Huff Ave", 90, -1, null, CarNavigationManager.TURN_SIDE_LEFT);
+            mCarNavigationManager.sendNavigationTurnDistanceEvent(500, 10);
+        } catch (CarNotConnectedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void initCluster() {
+        mCarAppContextManager.registerContextListener(new AppContextChangeListener() {
+            @Override
+            public void onAppContextChange(int activeContexts) {
+                Log.d(TAG, "onAppContextChange, activeContexts: " + activeContexts);
+            }
+
+            @Override
+            public void onAppContextOwnershipLoss(int context) {
+                Log.w(TAG, "onAppContextOwnershipLoss, context: " + context);
+
+                new AlertDialog.Builder(getContext())
+                        .setTitle(getContext().getApplicationInfo().name)
+                        .setMessage(R.string.cluster_nav_app_context_loss)
+                        .show();
+            }
+        }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+
+        mCarAppContextManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        boolean ownsContext =
+                mCarAppContextManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        Log.d(TAG, "Owns APP_CONTEXT_NAVIGATION: " + ownsContext);
+        if (!ownsContext) {
+            throw new RuntimeException("Context was not acquired.");
+        }
+
+        try {
+            mCarNavigationManager.sendNavigationStatus(CarNavigationManager.STATUS_ACTIVE);
+        } catch (CarNotConnectedException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
new file mode 100644
index 0000000..c22cd6b
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.hvac;
+
+import android.car.CarNotConnectedException;
+import android.car.hardware.hvac.CarHvacEvent;
+import android.car.hardware.hvac.CarHvacManager.CarHvacBaseProperty;
+import android.car.hardware.hvac.CarHvacManager.CarHvacBooleanValue;
+import android.car.hardware.hvac.CarHvacManager.CarHvacFloatProperty;
+import android.car.hardware.hvac.CarHvacManager.CarHvacFloatValue;
+import android.car.hardware.hvac.CarHvacManager.CarHvacIntProperty;
+import android.car.hardware.hvac.CarHvacManager.CarHvacIntValue;
+import android.car.hardware.hvac.CarHvacManager;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleHvacFanDirection;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleWindow;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleZone;
+
+import com.google.android.car.kitchensink.R;
+
+import java.lang.Override;
+import java.util.List;
+
+public class HvacTestFragment extends Fragment {
+    private final boolean DBG = true;
+    private final String TAG = "HvacTestFragment";
+    private RadioButton mRbFanPositionFace;
+    private RadioButton mRbFanPositionFloor;
+    private RadioButton mRbFanPositionFaceAndFloor;
+    private ToggleButton mTbAc;
+    private ToggleButton mTbDefrostFront;
+    private ToggleButton mTbDefrostRear;
+    private TextView mTvFanSpeed;
+    private TextView mTvDTemp;
+    private TextView mTvPTemp;
+    private int mCurFanSpeed = 1;
+    private float mCurDTemp = 23;
+    private float mCurPTemp = 23;
+    private CarHvacManager mCarHvacManager;
+
+    private final CarHvacManager.CarHvacEventListener mHvacListener =
+            new CarHvacManager.CarHvacEventListener () {
+                @Override
+                public void onChangeEvent(final CarHvacManager.CarHvacBaseProperty value) {
+                    int zone = value.getZone();
+                    switch(value.getPropertyId()) {
+                        case CarHvacManager.HVAC_ZONED_AC_ON:
+                            mTbAc.setChecked(((CarHvacBooleanValue)value).getValue());
+                            break;
+                        case CarHvacManager.HVAC_ZONED_FAN_POSITION:
+                            switch(((CarHvacIntValue)value).getValue()) {
+                                case VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FACE:
+                                    mRbFanPositionFace.setChecked(true);
+                                    break;
+                                case VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FLOOR:
+                                    mRbFanPositionFloor.setChecked(true);
+                                    break;
+                                case VehicleHvacFanDirection.
+                                        VEHICLE_HVAC_FAN_DIRECTION_FACE_AND_FLOOR:
+                                    mRbFanPositionFaceAndFloor.setChecked(true);
+                                    break;
+                                default:
+                                    Log.e(TAG, "Unknown fan position: " +
+                                            ((CarHvacIntValue)value).getValue());
+                                    break;
+                            }
+                            break;
+                        case CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT:
+                            mCurFanSpeed = ((CarHvacIntValue)value).getValue();
+                            mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+                            break;
+                        case CarHvacManager.HVAC_ZONED_TEMP_SETPOINT:
+                            switch(zone) {
+                                case VehicleZone.VEHICLE_ZONE_ROW_1_LEFT:
+                                    mCurDTemp = ((CarHvacFloatValue)value).getValue();
+                                    mTvDTemp.setText(String.valueOf(mCurDTemp));
+                                    break;
+                                case VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT:
+                                    mCurPTemp = ((CarHvacFloatValue)value).getValue();
+                                    mTvPTemp.setText(String.valueOf(mCurPTemp));
+                                    break;
+                                default:
+                                    Log.w(TAG, "Unknown zone = " + zone);
+                                    break;
+                            }
+                            break;
+                        case CarHvacManager.HVAC_WINDOW_DEFROSTER_ON:
+                            if((zone & VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) ==
+                                    VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) {
+                                mTbDefrostFront.setChecked(((CarHvacBooleanValue)value).getValue());
+                            }
+                            if((zone & VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) ==
+                                    VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) {
+                                mTbDefrostRear.setChecked(((CarHvacBooleanValue)value).getValue());
+                            }
+                            break;
+                        default:
+                            Log.d(TAG, "onChangeEvent(): unknown property id = " + value
+                                    .getPropertyId());
+                    }
+                }
+
+                @Override
+                public void onErrorEvent(final int propertyId, final int zone) {
+                    Log.d(TAG, "Error:  propertyId=" + propertyId + "  zone=" + zone);
+                }
+            };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        try {
+            mCarHvacManager.registerListener(mHvacListener);
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!");
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mCarHvacManager.unregisterListener();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+        View v = inflater.inflate(R.layout.hvac_test, container, false);
+
+        List<CarHvacBaseProperty> props = mCarHvacManager.getPropertyList();
+
+        for(CarHvacBaseProperty prop : props) {
+            int propId = prop.getPropertyId();
+            int type = prop.getType();
+
+            if(DBG) {
+                switch(type) {
+                    case CarHvacManager.PROPERTY_TYPE_BOOLEAN:
+                        Log.d(TAG, "propertyId = " + propId + " type = " + type +
+                                " zone = " + prop.getZone());
+                        break;
+                    case CarHvacManager.PROPERTY_TYPE_FLOAT:
+                        CarHvacFloatProperty f = (CarHvacFloatProperty)prop;
+                        Log.d(TAG, "propertyId = " + propId + " type = " + type +
+                                " zone = " + f.getZone() + " min = " + f.getMinValue() +
+                                " max = " + f.getMaxValue());
+                        break;
+                    case CarHvacManager.PROPERTY_TYPE_INT:
+                        CarHvacIntProperty i = (CarHvacIntProperty)prop;
+                        Log.d(TAG, "propertyId = " + propId + " type = " + type +
+                                " zone = " + i.getZone() + " min = " + i.getMinValue() +
+                                " max = " + i.getMaxValue());
+                        break;
+                }
+            }
+
+            switch(propId) {
+                case CarHvacManager.HVAC_ZONED_AC_ON:
+                    configureAcOn(v);
+                    break;
+                case CarHvacManager.HVAC_ZONED_FAN_POSITION:
+                    configureFanPosition(v);
+                    break;
+                case CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT:
+                    configureFanSpeed(v);
+                    break;
+                case CarHvacManager.HVAC_ZONED_TEMP_SETPOINT:
+                    configureTempSetpoint(v);
+                    break;
+                case CarHvacManager.HVAC_WINDOW_DEFROSTER_ON:
+                    configureDefrosterOn(v, prop.getZone());
+                    break;
+                default:
+                    Log.w(TAG, "propertyId " + propId + " is not handled");
+                    break;
+            }
+        }
+
+        mTvFanSpeed = (TextView) v.findViewById(R.id.tvFanSpeed);
+        mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+        mTvDTemp = (TextView) v.findViewById(R.id.tvDTemp);
+        mTvDTemp.setText(String.valueOf(mCurDTemp));
+        mTvPTemp = (TextView) v.findViewById(R.id.tvPTemp);
+        mTvPTemp.setText(String.valueOf(mCurPTemp));
+
+        if(DBG) {
+            Log.d(TAG, "Starting HvacTestFragment");
+        }
+
+        return v;
+    }
+
+    public void setHvacManager(CarHvacManager hvacManager) {
+        Log.d(TAG, "setHvacManager()");
+        mCarHvacManager = hvacManager;
+    }
+
+    private void configureAcOn(View v) {
+        mTbAc = (ToggleButton)v.findViewById(R.id.tbAc);
+        mTbAc.setEnabled(true);
+        mTbAc.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                // TODO handle zone properly
+                mCarHvacManager.setBooleanProperty(CarHvacManager.HVAC_ZONED_AC_ON, 0x8,
+                        mTbAc.isChecked());
+            }
+        });
+    }
+
+    private void configureFanPosition(View v) {
+        RadioGroup rg = (RadioGroup)v.findViewById(R.id.rgFanPosition);
+        rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(RadioGroup group, int checkedId) {
+                switch(checkedId) {
+                    case R.id.rbPositionFace:
+                        mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_POSITION,
+                                VehicleZone.VEHICLE_ZONE_ROW_1_ALL,
+                                VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FACE);
+                        break;
+                    case R.id.rbPositionFloor:
+                        mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_POSITION,
+                                VehicleZone.VEHICLE_ZONE_ROW_1_ALL,
+                                VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FLOOR);
+                        break;
+                    case R.id.rbPositionFaceAndFloor:
+                        mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_POSITION,
+                                VehicleZone.VEHICLE_ZONE_ROW_1_ALL, VehicleHvacFanDirection.
+                                        VEHICLE_HVAC_FAN_DIRECTION_FACE_AND_FLOOR);
+                        break;
+                }
+            }
+        });
+
+        mRbFanPositionFace = (RadioButton)v.findViewById(R.id.rbPositionFace);
+        mRbFanPositionFace.setClickable(true);
+        mRbFanPositionFloor = (RadioButton)v.findViewById(R.id.rbPositionFloor);
+        mRbFanPositionFaceAndFloor = (RadioButton)v.findViewById(R.id.rbPositionFaceAndFloor);
+        mRbFanPositionFaceAndFloor.setClickable(true);
+        mRbFanPositionFloor.setClickable(true);
+    }
+
+    private void configureFanSpeed(View v) {
+        mCurFanSpeed = mCarHvacManager.getIntProperty(
+                CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT,
+                VehicleZone.VEHICLE_ZONE_ALL);
+
+        Button btnFanSpeedUp = (Button) v.findViewById(R.id.btnFanSpeedUp);
+        btnFanSpeedUp.setEnabled(true);
+        btnFanSpeedUp.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if(mCurFanSpeed < 7) {
+                    mCurFanSpeed++;
+                    mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+                    mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT,
+                            VehicleZone.VEHICLE_ZONE_ALL, mCurFanSpeed);
+                }
+            }
+        });
+
+        Button btnFanSpeedDn = (Button) v.findViewById(R.id.btnFanSpeedDn);
+        btnFanSpeedDn.setEnabled(true);
+        btnFanSpeedDn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if (mCurFanSpeed > 1) {
+                    mCurFanSpeed--;
+                    mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+                    mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT,
+                            VehicleZone.VEHICLE_ZONE_ALL, mCurFanSpeed);
+                }
+            }
+        });
+    }
+
+    private void configureTempSetpoint(View v) {
+        mCurDTemp = mCarHvacManager.getFloatProperty(
+                CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+                VehicleZone.VEHICLE_ZONE_ROW_1_LEFT);
+        mCurPTemp = mCarHvacManager.getFloatProperty(
+                CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+                VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT);
+
+        Button btnDTempUp = (Button) v.findViewById(R.id.btnDTempUp);
+        btnDTempUp.setEnabled(true);
+        btnDTempUp.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if(mCurDTemp < 29.5) {
+                    mCurDTemp += 0.5;
+                    mTvDTemp.setText(String.valueOf(mCurDTemp));
+                    mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+                            VehicleZone.VEHICLE_ZONE_ROW_1_LEFT, mCurDTemp);
+                }
+            }
+        });
+
+        Button btnDTempDn = (Button) v.findViewById(R.id.btnDTempDn);
+        btnDTempDn.setEnabled(true);
+        btnDTempDn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if(mCurDTemp > 15.5) {
+                    mCurDTemp -= 0.5;
+                    mTvDTemp.setText(String.valueOf(mCurDTemp));
+                    mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+                            VehicleZone.VEHICLE_ZONE_ROW_1_LEFT, mCurDTemp);
+                }
+            }
+        });
+
+        Button btnPTempUp = (Button) v.findViewById(R.id.btnPTempUp);
+        btnPTempUp.setEnabled(true);
+        btnPTempUp.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if (mCurPTemp < 29.5) {
+                    mCurPTemp += 0.5;
+                    mTvPTemp.setText(String.valueOf(mCurPTemp));
+                    mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+                            VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT, mCurPTemp);
+                }
+            }
+        });
+
+        Button btnPTempDn = (Button) v.findViewById(R.id.btnPTempDn);
+        btnPTempDn.setEnabled(true);
+        btnPTempDn.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if (mCurPTemp > 15.5) {
+                    mCurPTemp -= 0.5;
+                    mTvPTemp.setText(String.valueOf(mCurPTemp));
+                    mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+                            VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT, mCurPTemp);
+                }
+            }
+        });
+    }
+
+    private void configureDefrosterOn(View v, int zone) {
+        if((zone & VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) ==
+                VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) {
+            mTbDefrostFront = (ToggleButton)v.findViewById(R.id.tbDefrostFront);
+            mTbDefrostFront.setEnabled(true);
+            mTbDefrostFront.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    mCarHvacManager.setBooleanProperty(CarHvacManager.HVAC_WINDOW_DEFROSTER_ON,
+                            VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD,
+                            mTbDefrostFront.isChecked());
+                }
+            });
+        }
+        if((zone & VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) ==
+                VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) {
+            mTbDefrostRear = (ToggleButton)v.findViewById(R.id.tbDefrostRear);
+            mTbDefrostRear.setEnabled(true);
+            mTbDefrostRear.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    mCarHvacManager.setBooleanProperty(CarHvacManager.HVAC_WINDOW_DEFROSTER_ON,
+                            VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD,
+                            mTbDefrostRear.isChecked());
+                }
+            });
+        }
+    }
+}
+
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java
new file mode 100644
index 0000000..c406616
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.input;
+
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehiclePropValueUtil;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleHwKeyInputAction;
+import com.google.android.car.kitchensink.R;
+
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.test.CarTestManager;
+import android.car.test.CarTestManagerBinderWrapper;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnTouchListener;
+import android.widget.Button;
+
+/**
+ * Test input event handling to system.
+ * vehicle hal should have VEHICLE_PROPERTY_HW_KEY_INPUT support for this to work.
+ */
+public class InputTestFragment extends Fragment {
+
+    private static final String TAG = "CAR.INPUT.KS";
+
+    private Car mCar;
+    private CarTestManager mTestManager;
+    private Button mVolumeUp;
+    private Button mVolumeDown;
+    private Button mVoice;
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.input_test, container, false);
+
+        // Single touch + key event does not work as touch is happening in other window
+        // at the same time. But long press will work.
+        mVolumeUp = (Button) view.findViewById(R.id.button_volume_up);
+        mVolumeUp.setOnTouchListener(new OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                handleTouchEvent(event, KeyEvent.KEYCODE_VOLUME_UP);
+                return true;
+            }
+        });
+        mVolumeDown = (Button) view.findViewById(R.id.button_volume_down);
+        mVolumeDown.setOnTouchListener(new OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                handleTouchEvent(event, KeyEvent.KEYCODE_VOLUME_DOWN);
+                return true;
+            }
+        });
+        mVoice = (Button) view.findViewById(R.id.button_voice);
+        mVoice.setOnTouchListener(new OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                handleTouchEvent(event, KeyEvent.KEYCODE_VOICE_ASSIST);
+                return true;
+            }
+        });
+
+        mCar = Car.createCar(getContext(), new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                mTestManager = new CarTestManager(
+                        (CarTestManagerBinderWrapper) mCar.getCarManager(Car.TEST_SERVICE));
+                if (!mTestManager.isPropertySupported(
+                        VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT)) {
+                    Log.w(TAG, "VEHICLE_PROPERTY_HW_KEY_INPUT not supported");
+                    mVolumeUp.setEnabled(false);
+                    mVolumeDown.setEnabled(false);
+                    mVoice.setEnabled(false);
+                }
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        });
+        mCar.connect();
+        return view;
+    }
+
+    private void handleTouchEvent(MotionEvent event, int keyCode) {
+        int action = event.getActionMasked();
+        Log.i(TAG, "handleTouchEvent, action:" + action + ",keyCode:" + keyCode);
+        boolean shouldInject = false;
+        boolean isDown = false;
+        if (action == MotionEvent.ACTION_DOWN) {
+            shouldInject = true;
+            isDown = true;
+        } else if (action == MotionEvent.ACTION_UP) {
+            shouldInject = true;
+        }
+        if (shouldInject) {
+            int[] values = { isDown ? VehicleHwKeyInputAction.VEHICLE_HW_KEY_INPUT_ACTION_DOWN :
+                VehicleHwKeyInputAction.VEHICLE_HW_KEY_INPUT_ACTION_UP, keyCode, 0, 0 };
+            long now = SystemClock.elapsedRealtimeNanos();
+            mTestManager.injectEvent(VehiclePropValueUtil.createIntVectorValue(
+                    VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT, values, now));
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mCar.disconnect();
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/DishService.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/DishService.java
new file mode 100644
index 0000000..c2758a8
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/DishService.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.job;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.lang.ref.WeakReference;
+
+public class DishService extends JobService {
+    private static final String TAG = "JobScheduler_DishService";
+    private static final int DELAY_MS = 1000; // wash a plate every second!
+
+    private static final int MSG_FINISHED = 0;
+    private static final int MSG_RUN_JOB = 1;
+    private static final int MSG_CANCEL_JOB = 2;
+
+    public static final String EXTRA_DISH_COUNT = "dish_count";
+
+    private final Handler mHandler = new Handler() {
+        private SparseArray<JobParameters> mTaskMap = new SparseArray<JobParameters>();
+        @Override
+        public void handleMessage(Message msg) {
+            JobParameters job = (JobParameters) msg.obj;
+            switch (msg.what) {
+                case MSG_FINISHED:
+                    Log.d(TAG, "Job done! " + job.getJobId());
+                    mTaskMap.remove(job.getJobId());
+                    jobFinished(job, false);
+                    break;
+                case MSG_RUN_JOB:
+                    DishWasherTask task = new DishWasherTask(this, job, msg.arg1);
+                    task.execute();
+                    mTaskMap.put(job.getJobId(), job);
+                    break;
+                case MSG_CANCEL_JOB:
+                    JobParameters job1 = mTaskMap.get(job.getJobId());
+                    if (job1 != null) {
+                        removeMessages(MSG_RUN_JOB, job1);
+                        Log.d(TAG, "cancelled job " + job1);
+                        mTaskMap.remove(job.getJobId());
+                    }
+                    break;
+                default:
+                    Log.w(TAG, "Unknown message " + msg.what);
+            }
+        }
+    };
+
+    @Override
+    public boolean onStopJob(JobParameters jobParameters) {
+        Log.d(TAG, "onStopJob " + jobParameters);
+        Message msg = mHandler.obtainMessage(MSG_CANCEL_JOB, 0, 0, jobParameters);
+        mHandler.sendMessage(msg);
+        return false;
+    }
+
+    @Override
+    public boolean onStartJob(final JobParameters jobParameters) {
+        Log.d(TAG, "onStartJob " + jobParameters);
+        Message msg = mHandler.obtainMessage(MSG_RUN_JOB, 0, 0, jobParameters);
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private static final class DishWasherTask extends AsyncTask<Void, Void, Boolean> {
+        private final WeakReference<Handler> mHandler;
+        private final JobParameters mJobParameter;
+        private final int mMyDishNum;
+
+
+        public DishWasherTask(Handler handler, JobParameters jobParameters, int dishNum) {
+            mHandler = new WeakReference<Handler>(handler);
+            mJobParameter = jobParameters;
+            mMyDishNum = dishNum;
+        }
+
+        @Override
+        protected Boolean doInBackground(Void... infos) {
+            int dishTotal = mJobParameter.getExtras().getInt(EXTRA_DISH_COUNT);
+
+            Log.d(TAG, "jobId: " + mJobParameter.getJobId() + " totalDish: " + dishTotal
+                    + " washing: #" + mMyDishNum);
+            wash();
+            if (mMyDishNum >= dishTotal - 1) {
+                // all done!
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        protected void onPostExecute(Boolean result) {
+            if (mHandler.get() == null) {
+                return;
+            }
+            if (result) {
+                Message msg = mHandler.get().obtainMessage(MSG_RUN_JOB,
+                        mMyDishNum +1, 0, mJobParameter);
+                mHandler.get().sendMessageDelayed(msg, DELAY_MS);
+            } else {
+                Message msg = mHandler.get().obtainMessage(MSG_FINISHED, 0,
+                        0, mJobParameter);
+                mHandler.get().sendMessage(msg);
+            }
+        }
+
+        private void wash() {
+            // TODO: add heavy wash tasks here...
+        }
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/JobSchedulerFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/JobSchedulerFragment.java
new file mode 100644
index 0000000..3cf96e4
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/JobSchedulerFragment.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.job;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.List;
+
+public class JobSchedulerFragment extends Fragment {
+    private static final String TAG = "JobSchedulerFragment";
+    private static final String PREFS_NEXT_JOB_ID = "next_job_id";
+
+    private Button mScheduleButton;
+    private Button mRefreshButton;
+    private Button mCancelButton;
+    private CheckBox mRequireCharging;
+    private CheckBox mRequireIdle;
+    private CheckBox mRequirePersisted;
+    private RadioGroup mNetworkGroup;
+    private EditText mDishNum;
+    private TextView mJobInfo;
+    private JobScheduler mJobScheduler;
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View v = inflater.inflate(R.layout.job_scheduler, container, false);
+        mScheduleButton = (Button) v.findViewById(R.id.schedule_button);
+        mScheduleButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                scheduleJob();
+            }
+        });
+        mRefreshButton = (Button) v.findViewById(R.id.refresh_button);
+        mRefreshButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                refreshCurrentJobs();
+            }
+        });
+
+        mNetworkGroup = (RadioGroup) v.findViewById(R.id.network_group);
+        mDishNum = (EditText) v.findViewById(R.id.dish_num);
+        mRequireCharging = (CheckBox) v.findViewById(R.id.require_charging);
+        mRequireIdle = (CheckBox) v.findViewById(R.id.require_idle);
+        mRequirePersisted = (CheckBox) v.findViewById(R.id.require_persisted);
+        mJobScheduler = (JobScheduler) getContext()
+                .getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        mJobInfo = (TextView) v.findViewById(R.id.current_jobs);
+
+        mCancelButton = (Button) v.findViewById(R.id.cancel_button);
+        mCancelButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mJobScheduler.cancelAll();
+                refreshCurrentJobs();
+            }
+        });
+        return v;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        refreshCurrentJobs();
+    }
+
+    private void refreshCurrentJobs() {
+        StringBuilder sb = new StringBuilder();
+        List<JobInfo> jobs = mJobScheduler.getAllPendingJobs();
+        for (JobInfo job : jobs) {
+            sb.append("JobId: ");
+            sb.append(job.getId());
+            sb.append("\nDishCount: ");
+            sb.append(job.getExtras().getInt(DishService.EXTRA_DISH_COUNT, 0));
+            sb.append("\n");
+        }
+        mJobInfo.setText(sb.toString());
+    }
+
+    private void scheduleJob() {
+        ComponentName jobComponentName = new ComponentName(getContext(), DishService.class);
+        SharedPreferences prefs = getContext()
+                .getSharedPreferences(PREFS_NEXT_JOB_ID, Context.MODE_PRIVATE);
+        int jobId = prefs.getInt(PREFS_NEXT_JOB_ID, 0);
+        PersistableBundle bundle = new PersistableBundle();
+        int count = 50;
+        try {
+            count = Integer.valueOf(mDishNum.getText().toString());
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "NOT A NUMBER!!!");
+        }
+
+        int selected = mNetworkGroup.getCheckedRadioButtonId();
+        int networkType = JobInfo.NETWORK_TYPE_ANY;
+        switch (selected) {
+            case R.id.network_none:
+                networkType = JobInfo.NETWORK_TYPE_NONE;
+                break;
+            case R.id.network_unmetered:
+                networkType = JobInfo.NETWORK_TYPE_UNMETERED;
+                break;
+            case R.id.network_any:
+                networkType = JobInfo.NETWORK_TYPE_ANY;
+                break;
+        }
+        bundle.putInt(DishService.EXTRA_DISH_COUNT, count);
+        JobInfo jobInfo = new JobInfo.Builder(jobId, jobComponentName)
+                .setRequiresCharging(mRequireCharging.isChecked())
+                .setRequiresDeviceIdle(mRequireIdle.isChecked())
+                // TODO: figure out why we crash here even we hold
+                // the RECEIVE_BOOT_COMPLETE permission
+                //.setPersisted(mRequirePersisted.isChecked())
+                .setExtras(bundle)
+                .setRequiredNetworkType(networkType)
+                .build();
+
+
+        mJobScheduler.schedule(jobInfo);
+        Toast.makeText(getContext(), "Scheduled: " + jobInfo, Toast.LENGTH_LONG ).show();
+
+        Log.d(TAG, "Scheduled a job: " + jobInfo);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putInt(PREFS_NEXT_JOB_ID, jobId + 1);
+        editor.commit();
+
+        refreshCurrentJobs();
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/keyboard/KeyboardFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/keyboard/KeyboardFragment.java
new file mode 100644
index 0000000..4ee0b16
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/keyboard/KeyboardFragment.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.keyboard;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.support.car.app.menu.CarDrawerActivity;
+import android.support.car.app.menu.SearchBoxEditListener;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.google.android.car.kitchensink.R;
+
+public class KeyboardFragment extends Fragment {
+    public static final int CARD = 0xfffafafa;
+    public static final int TEXT_PRIMARY_DAY = 0xde000000;
+    public static final int TEXT_SECONDARY_DAY = 0x8a000000;
+
+    private Button mImeButton;
+    private Button mCloseImeButton;
+    private Button mShowHideInputButton;
+    private CarDrawerActivity mActivity;
+    private TextView mOnSearchText;
+    private TextView mOnEditText;
+
+    private final Handler mHandler = new Handler();
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        View v = inflater.inflate(R.layout.keyboard_test, container, false);
+        mActivity = (CarDrawerActivity) getHost();
+        mImeButton = (Button) v.findViewById(R.id.ime_button);
+        mImeButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mActivity.startInput("Hint");
+            }
+        });
+
+        mCloseImeButton = (Button) v.findViewById(R.id.stop_ime_button);
+        mCloseImeButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mActivity.stopInput();
+                resetInput();
+            }
+        });
+
+        mShowHideInputButton = (Button) v.findViewById(R.id.ime_button2);
+        mShowHideInputButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (mActivity.isShowingSearchBox()) {
+                    mActivity.hideSearchBox();
+                } else {
+                    resetInput();
+                }
+            }
+        });
+
+        mOnSearchText = (TextView) v.findViewById(R.id.search_text);
+        mOnEditText = (TextView) v.findViewById(R.id.edit_text);
+        resetInput();
+        mActivity.setSearchBoxEndView(View.inflate(getContext(), R.layout.keyboard_end_view, null));
+
+        return v;
+    }
+
+    private void resetInput() {
+        mActivity.showSearchBox(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mActivity.startInput("Hint");
+            }
+        });
+        mActivity.setSearchBoxEditListener(mEditListener);
+        mActivity.setSearchBoxColors(CARD, TEXT_SECONDARY_DAY,
+                TEXT_PRIMARY_DAY, TEXT_SECONDARY_DAY);
+    }
+
+
+    private final SearchBoxEditListener mEditListener = new SearchBoxEditListener() {
+        @Override
+        public void onSearch(final String text) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mOnSearchText.setText("Search: " + text);
+                    resetInput();
+                }
+            });
+        }
+
+        @Override
+        public void onEdit(final String text) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mOnEditText.setText("Edit: " + text);
+                }
+            });
+        }
+    };
+}
diff --git a/tests/api_test/Android.mk b/tests/android_car_api_test/Android.mk
similarity index 90%
copy from tests/api_test/Android.mk
copy to tests/android_car_api_test/Android.mk
index e9d2827..aec4c71 100644
--- a/tests/api_test/Android.mk
+++ b/tests/android_car_api_test/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CarApiTest
+LOCAL_PACKAGE_NAME := AndroidCarApiTest
 
 # for system|priviledged permission.
 LOCAL_CERTIFICATE := platform
@@ -32,7 +32,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES += android.support.car car-systemtest
+LOCAL_STATIC_JAVA_LIBRARIES += car-systemtest
 
 LOCAL_JAVA_LIBRARIES := android.car android.test.runner
 
diff --git a/tests/api_test/AndroidManifest.xml b/tests/android_car_api_test/AndroidManifest.xml
similarity index 100%
rename from tests/api_test/AndroidManifest.xml
rename to tests/android_car_api_test/AndroidManifest.xml
diff --git a/tests/api_test/src/com/android/support/car/apitest/AppBlockingPackageInfoTest.java b/tests/android_car_api_test/src/com/android/car/apitest/AppBlockingPackageInfoTest.java
similarity index 98%
rename from tests/api_test/src/com/android/support/car/apitest/AppBlockingPackageInfoTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/AppBlockingPackageInfoTest.java
index 1f29551..5fe56fe 100644
--- a/tests/api_test/src/com/android/support/car/apitest/AppBlockingPackageInfoTest.java
+++ b/tests/android_car_api_test/src/com/android/car/apitest/AppBlockingPackageInfoTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.support.car.apitest;
+package com.android.car.apitest;
 
 import android.car.content.pm.AppBlockingPackageInfo;
 import android.content.Context;
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarApiTestBase.java b/tests/android_car_api_test/src/com/android/car/apitest/CarApiTestBase.java
new file mode 100644
index 0000000..d253ae2
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarApiTestBase.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.apitest;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Looper;
+import android.car.Car;
+import android.test.AndroidTestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CarApiTestBase extends AndroidTestCase {
+    protected static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
+
+    private Car mCar;
+
+    private final DefaultServiceConnectionListener mConnectionListener =
+            new DefaultServiceConnectionListener();
+
+    protected void assertMainThread() {
+        assertTrue(Looper.getMainLooper().isCurrentThread());
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCar = Car.createCar(getContext(), mConnectionListener);
+        mCar.connect();
+        mConnectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mCar.disconnect();
+    }
+
+    protected synchronized Car getCar() {
+        return mCar;
+    }
+
+    protected class DefaultServiceConnectionListener implements ServiceConnection {
+        private final Semaphore mConnectionWait = new Semaphore(0);
+
+        public void waitForConnection(long timeoutMs) throws InterruptedException {
+            mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            assertMainThread();
+            mConnectionWait.release();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            assertMainThread();
+        }
+    }
+}
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarAppBlockingPolicyTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarAppBlockingPolicyTest.java
similarity index 97%
rename from tests/api_test/src/com/android/support/car/apitest/CarAppBlockingPolicyTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/CarAppBlockingPolicyTest.java
index c1a1e8e..633a93c 100644
--- a/tests/api_test/src/com/android/support/car/apitest/CarAppBlockingPolicyTest.java
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarAppBlockingPolicyTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.support.car.apitest;
+package com.android.car.apitest;
 
 import android.car.content.pm.AppBlockingPackageInfo;
 import android.car.content.pm.CarAppBlockingPolicy;
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java
new file mode 100644
index 0000000..6f73e47
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.apitest;
+
+import android.car.Car;
+import android.car.CarAppContextManager;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CarAppContextManagerTest extends CarApiTestBase {
+    private static final String TAG = CarAppContextManager.class.getSimpleName();
+    private CarAppContextManager mManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mManager = (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
+        assertNotNull(mManager);
+    }
+
+    public void testUnregisteredAccess() throws Exception {
+        try {
+            mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+            fail();
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    public void testRegisterNull() throws Exception {
+        try {
+            mManager.registerContextListener(null, 0);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    public void testRegisterUnregister() throws Exception {
+        ContextChangeListerner listener = new ContextChangeListerner();
+        ContextChangeListerner listener2 = new ContextChangeListerner();
+        mManager.registerContextListener(listener, 0);
+        mManager.registerContextListener(listener2, 0);
+        mManager.unregisterContextListener();
+        // this one is no-op
+        mManager.unregisterContextListener();
+    }
+
+    public void testContextChange() throws Exception {
+        DefaultServiceConnectionListener connectionListener =
+                new DefaultServiceConnectionListener();
+        Car car2 = Car.createCar(getContext(), connectionListener, null);
+        car2.connect();
+        connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+        CarAppContextManager manager2 = (CarAppContextManager)
+                car2.getCarManager(Car.APP_CONTEXT_SERVICE);
+        assertNotNull(manager2);
+
+        assertEquals(0, mManager.getActiveAppContexts());
+        ContextChangeListerner owner = new ContextChangeListerner();
+        ContextChangeListerner owner2 = new ContextChangeListerner();
+        mManager.registerContextListener(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        manager2.registerContextListener(owner2, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+
+        mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        int expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
+        assertEquals(expectedContexts, mManager.getActiveAppContexts());
+        assertEquals(expectedContexts, manager2.getActiveAppContexts());
+        assertTrue(mManager.isOwningContext(expectedContexts));
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertTrue(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+                expectedContexts));
+        // owner should not get notification for its own change
+        assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+        mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND;
+        assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertEquals(expectedContexts, mManager.getActiveAppContexts());
+        assertEquals(expectedContexts, manager2.getActiveAppContexts());
+        assertTrue(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+                expectedContexts));
+        // owner should not get notification for its own change
+        assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+        // this should be no-op
+        mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        assertEquals(expectedContexts, mManager.getActiveAppContexts());
+        assertEquals(expectedContexts, manager2.getActiveAppContexts());
+        assertFalse(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+        assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+        manager2.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertEquals(expectedContexts, mManager.getActiveAppContexts());
+        assertEquals(expectedContexts, manager2.getActiveAppContexts());
+        assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+                CarAppContextManager.APP_CONTEXT_NAVIGATION));
+
+        // no-op as it is not owning it
+        mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertEquals(expectedContexts, mManager.getActiveAppContexts());
+        assertEquals(expectedContexts, manager2.getActiveAppContexts());
+
+        mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
+        assertEquals(expectedContexts, mManager.getActiveAppContexts());
+        assertEquals(expectedContexts, manager2.getActiveAppContexts());
+        assertTrue(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+                CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+        manager2.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+        expectedContexts = 0;
+        assertEquals(expectedContexts, mManager.getActiveAppContexts());
+        assertEquals(expectedContexts, manager2.getActiveAppContexts());
+        assertTrue(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+        mManager.unregisterContextListener();
+        manager2.unregisterContextListener();
+    }
+
+    public void testFilter() throws Exception {
+        DefaultServiceConnectionListener connectionListener =
+                new DefaultServiceConnectionListener();
+        Car car2 = Car.createCar(getContext(), connectionListener);
+        car2.connect();
+        connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+        CarAppContextManager manager2 = (CarAppContextManager)
+                car2.getCarManager(Car.APP_CONTEXT_SERVICE);
+        assertNotNull(manager2);
+
+        assertEquals(0, mManager.getActiveAppContexts());
+        ContextChangeListerner owner = new ContextChangeListerner();
+        ContextChangeListerner listener = new ContextChangeListerner();
+        mManager.registerContextListener(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+                CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        manager2.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+                CarAppContextManager.APP_CONTEXT_NAVIGATION));
+        mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+        mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+        assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+        mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+    }
+
+    private class ContextChangeListerner implements CarAppContextManager.AppContextChangeListener {
+        private int mLastChangeEvent;
+        private final Semaphore mChangeWait = new Semaphore(0);
+        private int mLastLossEvent;
+        private final Semaphore mLossEventWait = new Semaphore(0);
+
+        public boolean waitForContextChangeAndAssert(long timeoutMs, int expectedContexts)
+                throws Exception {
+            if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+                return false;
+            }
+            assertEquals(expectedContexts, mLastChangeEvent);
+            return true;
+        }
+
+        public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedContexts)
+                throws Exception {
+            if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+                return false;
+            }
+            assertEquals(expectedContexts, mLastLossEvent);
+            return true;
+        }
+
+        @Override
+        public void onAppContextChange(int activeContexts) {
+            Log.i(TAG, "onAppContextChange " + Integer.toHexString(activeContexts));
+            assertMainThread();
+            mLastChangeEvent = activeContexts;
+            mChangeWait.release();
+        }
+
+        @Override
+        public void onAppContextOwnershipLoss(int context) {
+            Log.i(TAG, "onAppContextOwnershipLoss " + Integer.toHexString(context));
+            assertMainThread();
+            mLastLossEvent = context;
+            mLossEventWait.release();
+        }
+    }
+}
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java
new file mode 100644
index 0000000..3f74a8f
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.apitest;
+
+import android.os.Looper;
+import android.car.Car;
+import android.car.CarAppContextManager;
+import android.car.CarAppContextManager.AppContextChangeListener;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.car.CarNotConnectedException;
+import android.car.navigation.CarNavigationManager;
+import android.car.navigation.CarNavigationManager.CarNavigationListener;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for {@link android.support.car.navigation.CarNavigationStatusManager}
+ */
+public class CarNavigationManagerTest extends CarApiTestBase {
+
+    private CarNavigationManager mCarNavigationManager;
+    private CarAppContextManager mCarAppContextManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCarNavigationManager =
+                (CarNavigationManager) getCar().getCarManager(Car.CAR_NAVIGATION_SERVICE);
+        assertNotNull(mCarNavigationManager);
+        mCarAppContextManager =
+                (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
+        assertNotNull(mCarAppContextManager);
+    }
+
+    public void testStart() throws Exception {
+        final CountDownLatch onStartLatch = new CountDownLatch(1);
+
+        mCarNavigationManager.registerListener(new CarNavigationListener() {
+            @Override
+            public void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentCluster) {
+                // TODO: we should use VehicleHalMock once we implement HAL support in
+                // CarNavigationStatusService.
+                assertFalse(instrumentCluster.supportsCustomImages());
+                assertEquals(1000, instrumentCluster.getMinIntervalMs());
+                onStartLatch.countDown();
+            }
+
+            @Override
+            public void onInstrumentClusterStop() {
+              // TODO
+            }
+        });
+
+        assertTrue(onStartLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        try {
+            mCarNavigationManager.sendNavigationStatus(1);
+            fail();
+        } catch (IllegalStateException expected) {
+            // Expected. Client should acquire context ownership for APP_CONTEXT_NAVIGATION.
+        }
+
+        mCarAppContextManager.registerContextListener(new AppContextChangeListener() {
+            @Override
+            public void onAppContextChange(int activeContexts) {
+                // Nothing to do here.
+            }
+
+            @Override
+            public void onAppContextOwnershipLoss(int context) {
+                // Nothing to do here.
+            }
+        }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        mCarAppContextManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+        assertTrue(mCarAppContextManager.isOwningContext(
+                CarAppContextManager.APP_CONTEXT_NAVIGATION));
+
+        // TODO: we should use mocked HAL to be able to verify this, right now just make sure that
+        // it is not crashing and logcat has appropriate traces.
+        mCarNavigationManager.sendNavigationStatus(1);
+    }
+}
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarPackageManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarPackageManagerTest.java
new file mode 100644
index 0000000..bffb526
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarPackageManagerTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.apitest;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Looper;
+import android.car.Car;
+import android.car.content.pm.CarPackageManager;
+import android.test.AndroidTestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CarPackageManagerTest extends AndroidTestCase {
+    private static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
+
+    private final Semaphore mConnectionWait = new Semaphore(0);
+
+    private Car mCar;
+    private CarPackageManager mCarPackageManager;
+
+    private final ServiceConnection mConnectionListener = new ServiceConnection() {
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            assertMainThread();
+            mConnectionWait.release();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            assertMainThread();
+        }
+
+    };
+
+    private void assertMainThread() {
+        assertTrue(Looper.getMainLooper().isCurrentThread());
+    }
+    private void waitForConnection(long timeoutMs) throws InterruptedException {
+        mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCar = Car.createCar(getContext(), mConnectionListener);
+        mCar.connect();
+        waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+        mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
+        assertNotNull(mCarPackageManager);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mCar.disconnect();
+    }
+
+    public void testCreate() throws Exception {
+        //nothing to do for now
+    }
+}
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarSensorManagerTest.java
similarity index 83%
rename from tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/CarSensorManagerTest.java
index a608284..6175fc4 100644
--- a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarSensorManagerTest.java
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.support.car.apitest;
+package com.android.car.apitest;
 
 import android.content.ComponentName;
+import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.Looper;
-import android.support.car.Car;
-import android.support.car.ServiceConnectionListener;
-import android.support.car.hardware.CarSensorEvent;
-import android.support.car.hardware.CarSensorManager;
+import android.car.Car;
+import android.car.hardware.CarSensorEvent;
+import android.car.hardware.CarSensorManager;
 import android.test.AndroidTestCase;
 
 import java.util.concurrent.Semaphore;
@@ -37,12 +37,7 @@
     private Car mCar;
     private CarSensorManager mCarSensorManager;
 
-    private final ServiceConnectionListener mConnectionListener = new ServiceConnectionListener() {
-
-        @Override
-        public void onServiceSuspended(int cause) {
-            assertMainThread();
-        }
+    private final ServiceConnection mConnectionListener = new ServiceConnection() {
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
@@ -50,11 +45,6 @@
         }
 
         @Override
-        public void onServiceConnectionFailed(int cause) {
-            assertMainThread();
-        }
-
-        @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             assertMainThread();
             mConnectionWait.release();
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarTest.java
new file mode 100644
index 0000000..a82a4fd
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.apitest;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Looper;
+import android.car.Car;
+import android.car.hardware.CarSensorManager;
+import android.test.AndroidTestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+
+public class CarTest extends AndroidTestCase {
+    private static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
+
+    private final Semaphore mConnectionWait = new Semaphore(0);
+
+    private final ServiceConnection mConnectionListener = new ServiceConnection() {
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            assertMainThread();
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            assertMainThread();
+            mConnectionWait.release();
+        }
+    };
+
+    private void assertMainThread() {
+        assertTrue(Looper.getMainLooper().isCurrentThread());
+    }
+
+    private void waitForConnection(long timeoutMs) throws InterruptedException {
+        mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+    }
+
+    public void testCarConnection() throws Exception {
+        Car car = Car.createCar(getContext(), mConnectionListener);
+        assertFalse(car.isConnected());
+        assertFalse(car.isConnecting());
+        car.connect();
+        //TODO fix race here
+        assertTrue(car.isConnecting());
+        waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+        assertTrue(car.isConnected());
+        assertFalse(car.isConnecting());
+        CarSensorManager carSensorManager =
+                (CarSensorManager) car.getCarManager(Car.SENSOR_SERVICE);
+        assertNotNull(carSensorManager);
+        CarSensorManager carSensorManager2 =
+                (CarSensorManager) car.getCarManager(Car.SENSOR_SERVICE);
+        assertEquals(carSensorManager, carSensorManager2);
+        Object noSuchService = car.getCarManager("No such service");
+        assertNull(noSuchService);
+        // double disconnect should be safe.
+        car.disconnect();
+        car.disconnect();
+        assertFalse(car.isConnected());
+        assertFalse(car.isConnecting());
+    }
+
+    public void testDoubleConnect() throws Exception {
+        Car car = Car.createCar(getContext(), mConnectionListener);
+        assertFalse(car.isConnected());
+        assertFalse(car.isConnecting());
+        car.connect();
+        try {
+            car.connect();
+            fail("dobule connect should throw");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+        car.disconnect();
+    }
+}
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleDoorTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleDoorTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleDoorTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleDoorTest.java
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleSeatTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleSeatTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleSeatTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleSeatTest.java
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleWindowTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleWindowTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleWindowTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleWindowTest.java
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleZoneTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleZoneTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleZoneTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleZoneTest.java
diff --git a/tests/api_test/Android.mk b/tests/android_support_car_api_test/Android.mk
similarity index 90%
rename from tests/api_test/Android.mk
rename to tests/android_support_car_api_test/Android.mk
index e9d2827..f74fb44 100644
--- a/tests/api_test/Android.mk
+++ b/tests/android_support_car_api_test/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CarApiTest
+LOCAL_PACKAGE_NAME := AndroudSupportCarApiTest
 
 # for system|priviledged permission.
 LOCAL_CERTIFICATE := platform
@@ -32,7 +32,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES += android.support.car car-systemtest
+LOCAL_STATIC_JAVA_LIBRARIES += android.support.car
 
 LOCAL_JAVA_LIBRARIES := android.car android.test.runner
 
diff --git a/tests/api_test/AndroidManifest.xml b/tests/android_support_car_api_test/AndroidManifest.xml
similarity index 100%
copy from tests/api_test/AndroidManifest.xml
copy to tests/android_support_car_api_test/AndroidManifest.xml
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarActivityTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarActivityTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarActivityTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarActivityTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarApiTestBase.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarApiTestBase.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarApiTestBase.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarApiTestBase.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
similarity index 68%
copy from tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
copy to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
index a608284..e30afe0 100644
--- a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
@@ -103,4 +103,29 @@
                 CarSensorManager.SENSOR_TYPE_DRIVING_STATUS);
         assertNotNull(lastEvent);
     }
+
+    public void testSensorType() throws Exception {
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_CAR_SPEED,
+                CarSensorManager.SENSOR_TYPE_CAR_SPEED);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+                CarSensorManager.SENSOR_TYPE_DRIVING_STATUS);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_ENVIRONMENT,
+                CarSensorManager.SENSOR_TYPE_ENVIRONMENT);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_FUEL_LEVEL,
+                CarSensorManager.SENSOR_TYPE_FUEL_LEVEL);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_GEAR,
+                CarSensorManager.SENSOR_TYPE_GEAR);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_NIGHT,
+                CarSensorManager.SENSOR_TYPE_NIGHT);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_ODOMETER,
+                CarSensorManager.SENSOR_TYPE_ODOMETER);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_PARKING_BRAKE,
+                CarSensorManager.SENSOR_TYPE_PARKING_BRAKE);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_RPM,
+                CarSensorManager.SENSOR_TYPE_RPM);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_START,
+                CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_START);
+        assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_END,
+                CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_END);
+    }
 }
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/TestAction.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestAction.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/TestAction.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/TestAction.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/TestCarActivity.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarActivity.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/TestCarActivity.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarActivity.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
diff --git a/tests/car_activity_test_app/AndroidManifest.xml b/tests/car_activity_test_app/AndroidManifest.xml
deleted file mode 100644
index 5564954..0000000
--- a/tests/car_activity_test_app/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-        package="com.android.support.car.test.caractivitytest" >
-    <uses-sdk
-        android:minSdkVersion="22"
-        android:targetSdkVersion='23'/>
-	<uses-permission android:name="android.car.permission.CAR_SPEED" />
-    <application android:label="CarActivityTest"
-        android:icon="@drawable/ic_launcher">
-        <activity android:name=".HelloCarProxyActivity"
-            android:theme="@android:style/Theme.Holo.NoActionBar.TranslucentDecor"
-            android:label="HelloCarActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/car_activity_test_app/res/drawable-hdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-hdpi/ic_cheese.png
deleted file mode 100644
index 6577aee..0000000
--- a/tests/car_activity_test_app/res/drawable-hdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-hdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 288b665..0000000
--- a/tests/car_activity_test_app/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-mdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-mdpi/ic_cheese.png
deleted file mode 100644
index d24a3bc..0000000
--- a/tests/car_activity_test_app/res/drawable-mdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-mdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 6ae570b..0000000
--- a/tests/car_activity_test_app/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xhdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-xhdpi/ic_cheese.png
deleted file mode 100644
index 485353c..0000000
--- a/tests/car_activity_test_app/res/drawable-xhdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xhdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index d4fb7cd..0000000
--- a/tests/car_activity_test_app/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-xxhdpi/ic_cheese.png
deleted file mode 100644
index a99571a..0000000
--- a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 85a6081..0000000
--- a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/layout/hello_caractivity.xml b/tests/car_activity_test_app/res/layout/hello_caractivity.xml
deleted file mode 100644
index 02df7c9..0000000
--- a/tests/car_activity_test_app/res/layout/hello_caractivity.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_marginTop="100dp"
-    android:orientation="vertical" >
-    <TextView
-        android:id="@+id/textView1"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:textSize="30dp"
-        android:text="@string/app_title" />
-    <TextView
-        android:id="@+id/textView_dring_status"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-    <TextView
-        android:id="@+id/textView_gear"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-    <TextView
-        android:id="@+id/textView_speed"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-    <TextView
-        android:id="@+id/textView_parking_brake"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-</LinearLayout>
diff --git a/tests/car_activity_test_app/res/values/strings.xml b/tests/car_activity_test_app/res/values/strings.xml
deleted file mode 100644
index c7093e9..0000000
--- a/tests/car_activity_test_app/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="app_title">Android Auto Embedded App</string>
-    <string name="button1">Button</string>
-</resources>
diff --git a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/Cheeses.java b/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/Cheeses.java
deleted file mode 100644
index 9d632f0..0000000
--- a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/Cheeses.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.support.car.test.caractivitytest;
-
-public class Cheeses {
-
-    public static final String[] ITEMS = {
-            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
-            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
-            "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
-            "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
-            "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
-            "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
-            "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
-            "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
-            "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
-            "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
-            "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
-            "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
-            "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
-            "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
-            "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
-            "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
-            "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
-            "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
-            "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
-            "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
-            "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
-            "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
-            "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
-            "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
-            "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
-            "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
-            "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
-            "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
-            "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
-            "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
-            "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
-            "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
-            "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
-            "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
-            "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
-            "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
-            "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
-            "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
-            "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
-            "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
-            "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
-            "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
-            "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
-            "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
-            "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
-            "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
-            "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
-            "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
-            "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
-            "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
-            "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
-            "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
-            "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
-            "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
-            "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
-            "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
-            "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
-            "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
-            "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
-            "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
-            "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
-            "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
-            "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
-            "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
-            "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
-            "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
-            "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
-            "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
-            "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
-            "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
-            "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
-            "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
-            "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
-            "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
-            "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
-            "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
-            "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
-            "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
-            "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
-            "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
-            "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
-            "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
-            "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
-            "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
-            "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
-            "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
-            "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
-            "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
-            "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
-            "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
-            "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
-            "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
-            "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
-            "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
-            "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
-            "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
-            "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
-            "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
-            "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
-            "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
-            "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
-            "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
-            "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
-            "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
-            "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
-            "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
-            "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
-            "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
-            "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
-            "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
-            "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
-            "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
-            "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
-            "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
-            "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
-            "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
-            "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
-            "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
-            "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
-            "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
-            "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
-            "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
-            "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
-            "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
-            "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
-            "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
-            "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
-            "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
-            "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
-            "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
-    };
-}
diff --git a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java b/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java
deleted file mode 100644
index 3dcb489..0000000
--- a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.support.car.test.caractivitytest;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.support.car.Car;
-import android.support.car.CarNotConnectedException;
-import android.support.car.ServiceConnectionListener;
-import android.support.car.app.menu.CarDrawerActivity;
-import android.support.car.app.menu.CarMenu;
-import android.support.car.app.menu.CarMenuCallbacks;
-import android.support.car.app.menu.Root;
-import android.support.car.hardware.CarSensorEvent;
-import android.support.car.hardware.CarSensorManager;
-import android.util.Log;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class HelloCarActivity extends CarDrawerActivity {
-    private static final String TAG = "HelloCarActivity";
-    private TextView mTextView1;
-    private TextView mTextViewDrivingStatus;
-    private TextView mTextViewGear;
-    private TextView mTextViewParkingBrake;
-    private TextView mTextViewSpeed;
-    private Car mCarApi;
-    private CarSensorManager mCarSensorManager;
-    private final Handler mHandler = new Handler();
-    private final CarSensorManager.CarSensorEventListener mListener =
-            new CarSensorManager.CarSensorEventListener() {
-        @Override
-        public void onSensorChanged(CarSensorEvent event) {
-            switch (event.sensorType) {
-                case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
-                    mTextViewSpeed.setText("speed:" + event.floatValues[0]);
-                    break;
-                case CarSensorManager.SENSOR_TYPE_GEAR:
-                    mTextViewGear.setText("gear:" + event.intValues[0]);
-                    break;
-                case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
-                    mTextViewParkingBrake.setText("parking brake:" + event.intValues[0]);
-                    break;
-                case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
-                    mTextViewDrivingStatus.setText("driving status:" + event.intValues[0]);
-                    break;
-            }
-        }
-    };
-
-    public HelloCarActivity(Proxy proxy, Context context, Car car) {
-        super(proxy, context, car);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        resetTitle();
-        setScrimColor(Color.RED);
-        setLightMode();
-        setCarMenuCallbacks(new MyCarMenuCallbacks());
-        setContentView(R.layout.hello_caractivity);
-        mTextView1 = (TextView) findViewById(R.id.textView1);
-        mTextViewDrivingStatus = (TextView) findViewById(R.id.textView_dring_status);
-        mTextViewGear = (TextView) findViewById(R.id.textView_gear);
-        mTextViewParkingBrake = (TextView) findViewById(R.id.textView_parking_brake);
-        mTextViewSpeed = (TextView) findViewById(R.id.textView_speed);
-        mCarApi = Car.createCar(getContext(), mServiceConnectionListener);
-        // Connection to Car Service does not work for non-automotive yet.
-        if (mCarApi != null) {
-            mCarApi.connect();
-        }
-        Log.i(TAG, "onCreate");
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        Log.i(TAG, "onStart");
-    }
-
-    @Override
-    protected void onRestart() {
-        super.onRestart();
-        Log.i(TAG, "onRestart");
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_apps_bg);
-        setBackground(bitmap);
-        Log.i(TAG, "onResume");
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        Log.i(TAG, "onPause");
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        Log.i(TAG, "onStop");
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        if (mCarSensorManager != null) {
-            mCarSensorManager.unregisterListener(mListener);
-        }
-        if (mCarApi != null) {
-            mCarApi.disconnect();
-        }
-        Log.i(TAG, "onDestroy");
-    }
-
-    private void resetTitle() {
-        setTitle(getContext().getString(R.string.app_title));
-    }
-
-    private final ServiceConnectionListener mServiceConnectionListener = new ServiceConnectionListener() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            Log.d(TAG, "Connected to Car Service");
-            try {
-                mCarSensorManager = (CarSensorManager) mCarApi.getCarManager(Car.SENSOR_SERVICE);
-                mCarSensorManager.registerListener(mListener,
-                        CarSensorManager.SENSOR_TYPE_CAR_SPEED,
-                        CarSensorManager.SENSOR_RATE_NORMAL);
-                mCarSensorManager.registerListener(mListener,
-                        CarSensorManager.SENSOR_TYPE_GEAR,
-                        CarSensorManager.SENSOR_RATE_NORMAL);
-                mCarSensorManager.registerListener(mListener,
-                        CarSensorManager.SENSOR_TYPE_PARKING_BRAKE,
-                        CarSensorManager.SENSOR_RATE_NORMAL);
-                mCarSensorManager.registerListener(mListener,
-                        CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
-                        CarSensorManager.SENSOR_RATE_NORMAL);
-            } catch (CarNotConnectedException e) {
-                Log.e(TAG, "Car is not connected!");
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            Log.d(TAG, "Disconnect from Car Service");
-        }
-
-        @Override
-        public void onServiceSuspended(int cause) {
-            Log.d(TAG, "Car Service connection suspended");
-        }
-
-        @Override
-        public void onServiceConnectionFailed(int cause) {
-            Log.d(TAG, "Car Service connection failed");
-        }
-    };
-
-    private final class MyCarMenuCallbacks extends CarMenuCallbacks {
-        /** Id for the root menu */
-        private static final String ROOT = "ROOT";
-        /** Id for the asynchronous menu */
-        private static final String ASYNC = "ASYNC";
-        /** Id for the checkbox menu itme */
-        private static final String CHECKBOX = "checkbox";
-
-        // for shared preferences
-        /** Key used to store the checkbox state */
-        private static final String KEY_CHECKED = "checked";
-        /** Shared preferences name  */
-        private static final String PREFERENCES = "preferences";
-
-        @Override
-        public Root onGetRoot(Bundle hints) {
-            return new Root(ROOT);
-        }
-
-        @Override
-        public void onLoadChildren(String parentId, CarMenu result) {
-            List<CarMenu.Item> items = new ArrayList<>();
-            final Bitmap cheeseBitmap = BitmapFactory.decodeResource(getResources(),
-                    R.drawable.ic_cheese);
-            // Demonstrate fetching the root menu.
-            // It is not required to set all the fields.
-            if (parentId.equals(ROOT)) {
-                items.add(new CarMenu.Builder(ASYNC)
-                        .setTitle("Async")
-                        .setText("Send menu back asynchronously")
-                        .setFlags(CarMenu.FLAG_BROWSABLE)
-                        .build());
-                items.add(new CarMenu.Builder(ROOT)
-                        .setTitle("Will be capped after 6 clicks")
-                        .setFlags(CarMenu.FLAG_BROWSABLE)
-                        .build());
-
-                boolean checked = getContext()
-                        .getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE)
-                        .getBoolean(KEY_CHECKED, true);
-                items.add(new CarMenu.Builder(CHECKBOX)
-                        .setTitle("Example checkbox")
-                        .setWidget(CarMenu.WIDGET_CHECKBOX)
-                        .setWidgetState(checked)
-                        .build());
-                for (int i = 0; i < 26; ++i) {
-                    String id = String.valueOf((char)('A' + i));
-                    switch (i % 3) {
-                        case 0:
-                            // Set all the fields
-                            items.add(new CarMenu.Builder(id)
-                                    .setTitle(String.valueOf(id))
-                                    .setText("Cheeses that start with " + id)
-                                    .setFlags(CarMenu.FLAG_BROWSABLE)
-                                    .setIcon(cheeseBitmap)
-                                    .build());
-                            break;
-                        case 1:
-                            // Only set title and text
-                            items.add(new CarMenu.Builder(id)
-                                    .setTitle(String.valueOf(id))
-                                    .setText("Cheeses that start with " + id)
-                                    .setFlags(CarMenu.FLAG_BROWSABLE)
-                                    .build());
-                            break;
-                        case 2:
-                            // Only set title
-                            items.add(new CarMenu.Builder(id)
-                                    .setTitle(String.valueOf(id))
-                                    .setFlags(CarMenu.FLAG_BROWSABLE)
-                                    .build());
-                            break;
-                    }
-                }
-
-                mHandler.postDelayed(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyChildChanged(ROOT, new CarMenu.Builder(ROOT)
-                                .setIcon(cheeseBitmap)
-                                .build());
-                    }
-                }, 5000);
-            } else if (parentId.equals(ASYNC)) {
-                result.detach();
-                sendAsync(result);
-                return;
-            } else {
-                boolean started = false;
-                for (String cheese : Cheeses.ITEMS) {
-                    if (cheese.startsWith(parentId)) {
-                        if (!started) {
-                            started = true;
-                        }
-                        items.add(new CarMenu.Builder(cheese)
-                                .setTitle(cheese)
-                                .setRightIcon(cheeseBitmap)
-                                .build());
-                    } else {
-                        if (started) {
-                            break;
-                        }
-                    }
-                }
-            }
-            result.sendResult(items);
-        }
-
-        @Override
-        public void onItemClicked(String id) {
-            if (id.equals(CHECKBOX)) {
-                boolean checked = getContext()
-                        .getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE)
-                        .getBoolean(KEY_CHECKED, true);
-                getContext().getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).edit()
-                        .putBoolean(KEY_CHECKED, !checked)
-                        .commit();
-            }
-            mTextView1.setText(id);
-        }
-
-        @Override
-        public void onCarMenuClosed() {
-        }
-
-        private void sendAsync(final CarMenu result) {
-            final List<CarMenu.Item> items = new ArrayList<>();
-            items.add(new CarMenu.Builder("Test")
-                    .setTitle("Appear after 5 sec")
-                    .build());
-            mHandler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    result.sendResult(items);
-                }
-            }, 5000);
-        }
-    }
-
-}
diff --git a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
index 9ff5511..3e05dd9 100644
--- a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
+++ b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
@@ -27,7 +27,6 @@
 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
 import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
 import com.android.car.vehiclenetwork.VehiclePropValueUtil;
-import com.android.support.car.test.MockedCarTestBase;
 
 import java.util.LinkedList;
 import java.util.concurrent.Semaphore;
@@ -120,7 +119,7 @@
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG |
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG |
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG |
-                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND |
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG |
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG,
                 v.getInt32Values(
                         VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS)
@@ -149,7 +148,7 @@
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG |
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG |
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG |
-                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND |
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG |
                 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG,
                 v.getInt32Values(
                         VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS)
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarAudioFocusTest.java b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
similarity index 90%
rename from tests/carservice_test/src/com/android/support/car/test/CarAudioFocusTest.java
rename to tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
index 2f6e284..d3085f9 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarAudioFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.support.car.test;
+package com.android.car.test;
 
 import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
 import android.content.Context;
@@ -24,6 +24,7 @@
 import android.support.car.media.CarAudioManager;
 
 import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
@@ -129,19 +130,10 @@
                         VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS,
                         VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
                         VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
-                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3,
+                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4,
                         VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
                         0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
                         mAudioFocusPropertyHandler);
-        getVehicleHalEmulator().addProperty(
-                VehiclePropConfigUtil.getBuilder(
-                        VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT,
-                        VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE,
-                        VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
-                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32,
-                        VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
-                        0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
-                        mAppContextPropertyHandler);
         getVehicleHalEmulator().addStaticProperty(
                 VehiclePropConfigUtil.createStaticStringProperty(
                         VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT),
@@ -155,35 +147,40 @@
         checkSingleRequestRelease(
                 AudioManager.STREAM_MUSIC,
                 AudioManager.AUDIOFOCUS_GAIN,
-                VehicleAudioStream.VEHICLE_AUDIO_STREAM0);
+                VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
     }
 
     public void testMediaGainTransientFocus() throws Exception {
         checkSingleRequestRelease(
                 AudioManager.STREAM_MUSIC,
                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
-                VehicleAudioStream.VEHICLE_AUDIO_STREAM0);
+                VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
     }
 
     public void testMediaGainTransientMayDuckFocus() throws Exception {
         checkSingleRequestRelease(
                 AudioManager.STREAM_MUSIC,
                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
-                VehicleAudioStream.VEHICLE_AUDIO_STREAM0);
+                VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
     }
 
     public void testAlarmGainTransientFocus() throws Exception {
         checkSingleRequestRelease(
                 AudioManager.STREAM_ALARM,
                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
-                VehicleAudioStream.VEHICLE_AUDIO_STREAM1);
+                VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG);
     }
 
     public void testAlarmGainTransientMayDuckFocus() throws Exception {
         checkSingleRequestRelease(
                 AudioManager.STREAM_ALARM,
                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
-                VehicleAudioStream.VEHICLE_AUDIO_STREAM1);
+                VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG);
     }
 
     public void testMediaNavFocus() throws Exception {
@@ -197,6 +194,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 request[1],
@@ -214,6 +212,8 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
         assertEquals(0x3, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG |
+                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
                 VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
@@ -224,6 +224,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
                 VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
@@ -234,6 +235,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
         assertEquals(0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(0, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS, request[1],
                 VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
@@ -250,6 +252,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 request[1],
@@ -275,6 +278,8 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK,
                 request[0]);
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
+        assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT,
                 0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
@@ -286,6 +291,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
         assertEquals(0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(0, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
                 0,
@@ -302,6 +308,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 request[1],
@@ -313,6 +320,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
         assertEquals(0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(0, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
                 0,
@@ -330,6 +338,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 request[1],
@@ -350,6 +359,8 @@
         assertEquals(0, request[1]);
         assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
                 request[2]);
+        // no android side context for radio
+        assertEquals(0, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 0,
@@ -369,6 +380,7 @@
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
         assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
                 request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
@@ -382,6 +394,7 @@
         assertEquals(0, request[1]);
         assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
                 request[2]);
+        assertEquals(0, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 0,
@@ -396,6 +409,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
         assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
                 0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
@@ -407,13 +421,15 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
         assertEquals(0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(0, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
                 0,
                 VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
     }
 
-    private void checkSingleRequestRelease(int streamType, int androidFocus, int streamNumber)
+    private void checkSingleRequestRelease(int streamType, int androidFocus, int streamNumber,
+            int context)
             throws Exception {
         AudioFocusListener lister = new AudioFocusListener();
         int res = mAudioManager.requestAudioFocus(lister,
@@ -442,6 +458,7 @@
         assertEquals(expectedRequest, request[0]);
         assertEquals(0x1 << streamNumber, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(context, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 response,
                 request[1],
@@ -451,6 +468,7 @@
         assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
         assertEquals(0, request[1]);
         assertEquals(0, request[2]);
+        assertEquals(0, request[3]);
         mAudioFocusPropertyHandler.sendAudioFocusState(
                 VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
                 request[1],
@@ -491,6 +509,7 @@
         private int mRequest;
         private int mRequestedStreams;
         private int mRequestedExtFocus;
+        private int mRequestedAudioContexts;
 
         private final Semaphore mSetWaitSemaphore = new Semaphore(0);
 
@@ -500,7 +519,7 @@
                 mStreams = streams;
                 mExtFocus = extFocus;
             }
-            int[] values = { state, streams, extFocus };
+            int[] values = { state, streams, extFocus, 0 };
             getVehicleHalEmulator().injectEvent(VehiclePropValueUtil.createIntVectorValue(
                     VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
                     SystemClock.elapsedRealtimeNanos()));
@@ -511,7 +530,8 @@
                 fail("timeout");
             }
             synchronized (this) {
-                return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus };
+                return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus,
+                        mRequestedAudioContexts };
             }
         }
 
@@ -525,6 +545,8 @@
                         VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
                 mRequestedExtFocus = value.getInt32Values(
                         VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
+                mRequestedAudioContexts = value.getInt32Values(
+                        VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS);
             }
             mSetWaitSemaphore.release();
         }
@@ -538,7 +560,7 @@
                 streams = mStreams;
                 extFocus = mExtFocus;
             }
-            int[] values = { state, streams, extFocus };
+            int[] values = { state, streams, extFocus, 0 };
             return VehiclePropValueUtil.createIntVectorValue(
                     VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
                     SystemClock.elapsedRealtimeNanos());
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarHvacManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/support/car/test/CarHvacManagerTest.java
rename to tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
index 37fa0ec..13d0936 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarHvacManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.support.car.test;
+package com.android.car.test;
 
 import android.util.Log;
 
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarPowerManagementTest.java b/tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/support/car/test/CarPowerManagementTest.java
rename to tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
index 6cb3113..e0dc3d9 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarPowerManagementTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.support.car.test;
+package com.android.car.test;
 
 import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
 import android.content.Context;
diff --git a/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
index ef7a371..4dcd3ce 100644
--- a/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
@@ -31,7 +31,6 @@
 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
 import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
 import com.android.car.vehiclenetwork.VehiclePropValueUtil;
-import com.android.support.car.test.MockedCarTestBase;
 
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
diff --git a/tests/carservice_test/src/com/android/support/car/test/GarageModeTest.java b/tests/carservice_test/src/com/android/car/test/GarageModeTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/support/car/test/GarageModeTest.java
rename to tests/carservice_test/src/com/android/car/test/GarageModeTest.java
index bc6086d..8380ec2 100644
--- a/tests/carservice_test/src/com/android/support/car/test/GarageModeTest.java
+++ b/tests/carservice_test/src/com/android/car/test/GarageModeTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.support.car.test;
+package com.android.car.test;
 
 import android.content.Context;
 import android.test.AndroidTestCase;
diff --git a/tests/carservice_test/src/com/android/support/car/test/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
similarity index 98%
rename from tests/carservice_test/src/com/android/support/car/test/MockedCarTestBase.java
rename to tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
index 05d7104..5234dd5 100644
--- a/tests/carservice_test/src/com/android/support/car/test/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.support.car.test;
+package com.android.car.test;
 
 import android.car.test.VehicleHalEmulator;
 import android.content.ComponentName;
diff --git a/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java b/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
index 76da972..18332da 100644
--- a/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
@@ -22,6 +22,7 @@
 import android.support.car.CarAppContextManager;
 import android.util.Log;
 
+import com.android.car.test.MockedCarTestBase;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
 import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
@@ -37,20 +38,10 @@
 public class AppContextTest extends MockedCarTestBase {
     private static final String TAG = AppContextTest.class.getSimpleName();
     private static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
-    private final AppContextHandler mAppContextHandler = new AppContextHandler();
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        getVehicleHalEmulator().addProperty(
-                VehiclePropConfigUtil.getBuilder(
-                        VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT,
-                        VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE,
-                        VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
-                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32,
-                        VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
-                        0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
-                        mAppContextHandler);
         getVehicleHalEmulator().start();
     }
 
@@ -61,55 +52,12 @@
         manager.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION |
                 CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
         manager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
-        mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
-                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG);
         manager.setActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
-        mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
-                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG |
-                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG);
         manager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
-        mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
-                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG);
         manager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
-        mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0);
         manager.unregisterContextListener();
     }
 
-    private class AppContextHandler implements VehicleHalPropertyHandler {
-        private final Semaphore mWaitSemaphore = new Semaphore(0);
-        private int mCurrentContext;
-
-        public boolean waitForPropertySetAndAssert(long timeoutMs, int expected) throws Exception {
-            if (!mWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
-                return false;
-            }
-            assertEquals(expected, mCurrentContext);
-            return true;
-        }
-
-        @Override
-        public void onPropertySet(VehiclePropValue value) {
-            mCurrentContext = value.getInt32Values(0);
-            mWaitSemaphore.release();
-        }
-
-        @Override
-        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
-            fail();
-            return null;
-        }
-
-        @Override
-        public void onPropertySubscribe(int property, float sampleRate, int zones) {
-            fail();
-        }
-
-        @Override
-        public void onPropertyUnsubscribe(int property) {
-            fail();
-        }
-    }
-
     private class ContextChangeListerner implements CarAppContextManager.AppContextChangeListener {
         private int mLastChangeEvent;
         private final Semaphore mChangeWait = new Semaphore(0);
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java b/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java
index 710c309..0b80872 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java
@@ -28,6 +28,8 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
+import com.android.car.test.MockedCarTestBase;
+
 public class CarConnectionListenerInMockingTest extends MockedCarTestBase {
 
     private static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java b/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java
index ae502d6..ada1d3a 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java
@@ -19,6 +19,7 @@
 import android.support.car.CarInfoManager;
 import android.util.Log;
 
+import com.android.car.test.MockedCarTestBase;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts;
 import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
 import com.android.car.vehiclenetwork.VehiclePropValueUtil;
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java b/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java
index 9776bff..1abc49e 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManager;
 import android.util.Log;
 
+import com.android.car.test.MockedCarTestBase;
 import com.android.car.test.TestAppBlockingPolicyService;
 
 public class CarPackageManagerTest extends MockedCarTestBase {
diff --git a/tests/libvehiclenetwork-native-test/AudioPropertyDef.c b/tests/libvehiclenetwork-native-test/AudioPropertyDef.c
index 28dbd23..0e00c7f 100644
--- a/tests/libvehiclenetwork-native-test/AudioPropertyDef.c
+++ b/tests/libvehiclenetwork-native-test/AudioPropertyDef.c
@@ -25,7 +25,7 @@
         .prop = VEHICLE_PROPERTY_AUDIO_FOCUS,
         .access = VEHICLE_PROP_ACCESS_READ_WRITE,
         .change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
-        .value_type = VEHICLE_VALUE_TYPE_INT32_VEC3,
+        .value_type = VEHICLE_VALUE_TYPE_INT32_VEC4,
         .config_flags = 0x0,
         .min_sample_rate = 0,
         .max_sample_rate = 0,
diff --git a/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h b/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
index 196c556..47a3a28 100644
--- a/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
+++ b/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
@@ -54,7 +54,7 @@
         mHalErrorCondition.signal();
     }
 
-    virtual void onHalRestart(bool inMocking) {
+    virtual void onHalRestart(bool /*inMocking*/) {
         Mutex::Autolock autolock(mHalRestartLock);
         mHalRestartCount++;
         mHalRestartCondition.signal();
diff --git a/tests/libvehiclenetwork-native-test/VehicleHalMock.h b/tests/libvehiclenetwork-native-test/VehicleHalMock.h
index 777bbbe..1d43a6d 100644
--- a/tests/libvehiclenetwork-native-test/VehicleHalMock.h
+++ b/tests/libvehiclenetwork-native-test/VehicleHalMock.h
@@ -42,19 +42,20 @@
         return mProperties;
     };
 
-    virtual status_t onPropertySet(const vehicle_prop_value_t& value) {
+    virtual status_t onPropertySet(const vehicle_prop_value_t& /*value*/) {
         return NO_ERROR;
     };
 
-    virtual status_t onPropertyGet(vehicle_prop_value_t* value) {
+    virtual status_t onPropertyGet(vehicle_prop_value_t* /*value*/) {
         return NO_ERROR;
     };
 
-    virtual status_t onPropertySubscribe(int32_t property, float sampleRate, int32_t zones) {
+    virtual status_t onPropertySubscribe(int32_t /*property*/, float /*sampleRate*/,
+            int32_t /*zones*/) {
         return NO_ERROR;
     };
 
-    virtual void onPropertyUnsubscribe(int32_t property) {
+    virtual void onPropertyUnsubscribe(int32_t /*property*/) {
     };
 
     bool isTheSameProperties(sp<VehiclePropertiesHolder>& list) {
diff --git a/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp b/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp
index 7dd6d73..af19b36 100644
--- a/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp
+++ b/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp
@@ -49,10 +49,11 @@
             mAudioProperties->getList().push_back(properties + i);
         }
         mValueToGet.prop = VEHICLE_PROPERTY_AUDIO_FOCUS;
-        mValueToGet.value_type = VEHICLE_VALUE_TYPE_INT32_VEC3;
+        mValueToGet.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4;
         mValueToGet.value.int32_array[0] = 0;
         mValueToGet.value.int32_array[1] = 0;
         mValueToGet.value.int32_array[2] = 0;
+        mValueToGet.value.int32_array[3] = 0;
     }
     virtual ~VehicleHalMockForAudioFocus() {};
 
@@ -76,7 +77,8 @@
         return NO_ERROR;
     };
 
-    virtual status_t onPropertySubscribe(int32_t property, float sampleRate) {
+    virtual status_t onPropertySubscribe(int32_t property, float /*sampleRate*/,
+            int32_t /*zones*/) {
         ALOGI("onPropertySubscribe 0x%x", property);
         return NO_ERROR;
     };
@@ -90,6 +92,7 @@
         mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_FOCUS] = state;
         mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_STREAMS] = streams;
         mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE] = extState;
+        mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS] = 0;
         mValueToGet.timestamp = elapsedRealtimeNano();
         mVN->injectEvent(mValueToGet);
     }
diff --git a/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h b/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
index 1260876..e661eff 100644
--- a/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
+++ b/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
@@ -61,11 +61,11 @@
         mCondition.signal();
     }
 
-    virtual void onHalError(int32_t errorCode, int32_t property, int32_t operation) {
+    virtual void onHalError(int32_t /*errorCode*/, int32_t /*property*/, int32_t /*operation*/) {
         //TODO
     }
 
-    virtual void onHalRestart(bool inMocking) {
+    virtual void onHalRestart(bool /*inMocking*/) {
         //TODO cannot test this in native world without plumbing mocking
     }
 
diff --git a/vehicle_network_service/VehicleHalPropertyUtil.h b/vehicle_network_service/VehicleHalPropertyUtil.h
index 3b22797..da0d476 100644
--- a/vehicle_network_service/VehicleHalPropertyUtil.h
+++ b/vehicle_network_service/VehicleHalPropertyUtil.h
@@ -48,7 +48,7 @@
                         config.float_max_value);
             } break;
             case VEHICLE_VALUE_TYPE_INT64: {
-                msg.appendFormat(",v min:" PRId64 " max:" PRId64 "\n", config.int64_min_value,
+                msg.appendFormat(",v min:%" PRId64 " max:%" PRId64 "\n", config.int64_min_value,
                         config.int64_max_value);
             } break;
             case VEHICLE_VALUE_TYPE_INT32:
diff --git a/vehicle_network_service/VehicleNetworkService.cpp b/vehicle_network_service/VehicleNetworkService.cpp
index e3b76c0..278b0cd 100644
--- a/vehicle_network_service/VehicleNetworkService.cpp
+++ b/vehicle_network_service/VehicleNetworkService.cpp
@@ -779,8 +779,11 @@
     do {
         Mutex::Autolock autoLock(mLock);
         if (mMockingEnabled) {
-            ALOGE("startMocking while already enabled");
-            return INVALID_OPERATION;
+            ALOGW("startMocking while already enabled");
+            // allow it as test can fail without clearing
+            if (mHalMock != NULL) {
+                IInterface::asBinder(mHalMock)->unlinkToDeath(mHalMockDeathHandler.get());
+            }
         }
         ALOGW("starting vehicle HAL mocking");
         sp<IBinder> ibinder = IInterface::asBinder(mock);