Merge "consumer ir: clarify @link in documentation" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index b5930ec..9252152 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10815,16 +10815,17 @@
   }
 
   public abstract interface CameraDevice implements java.lang.AutoCloseable {
-    method public abstract void capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close() throws java.lang.Exception;
     method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
-    method public abstract android.hardware.camera2.CaptureRequest createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
+    method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void flush() throws android.hardware.camera2.CameraAccessException;
+    method public abstract java.lang.String getId();
     method public abstract android.hardware.camera2.CameraProperties getProperties() throws android.hardware.camera2.CameraAccessException;
-    method public abstract void setErrorListener(android.hardware.camera2.CameraDevice.ErrorListener);
-    method public abstract void setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void setDeviceListener(android.hardware.camera2.CameraDevice.CameraDeviceListener, android.os.Handler);
+    method public abstract void setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
     method public abstract void waitUntilIdle() throws android.hardware.camera2.CameraAccessException;
     field public static final int TEMPLATE_MANUAL = 5; // 0x5
@@ -10834,39 +10835,38 @@
     field public static final int TEMPLATE_VIDEO_SNAPSHOT = 4; // 0x4
   }
 
-  public static abstract interface CameraDevice.CaptureListener {
-    method public abstract void onCaptureComplete(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
-    method public abstract void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest);
+  public static abstract class CameraDevice.CameraDeviceListener {
+    ctor public CameraDevice.CameraDeviceListener();
+    method public void onCameraDisconnected(android.hardware.camera2.CameraDevice);
+    method public void onCameraError(android.hardware.camera2.CameraDevice, int);
+    method public void onCameraIdle(android.hardware.camera2.CameraDevice);
+    field public static final int ERROR_CAMERA_DEVICE = 1; // 0x1
+    field public static final int ERROR_CAMERA_SERVICE = 2; // 0x2
   }
 
-  public static abstract interface CameraDevice.ErrorListener {
-    method public abstract void onCameraDeviceError(android.hardware.camera2.CameraDevice, int);
-    field public static final int DEVICE_DISCONNECTED = 1; // 0x1
-    field public static final int DEVICE_ERROR = 2; // 0x2
-    field public static final int SERVICE_ERROR = 3; // 0x3
+  public static abstract class CameraDevice.CaptureListener {
+    ctor public CameraDevice.CaptureListener();
+    method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest);
+    method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
   }
 
   public final class CameraManager {
+    method public void addAvailabilityListener(android.hardware.camera2.CameraManager.AvailabilityListener, android.os.Handler);
+    method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
     method public android.hardware.camera2.CameraProperties getCameraProperties(java.lang.String) throws android.hardware.camera2.CameraAccessException;
-    method public java.lang.String[] getDeviceIdList() throws android.hardware.camera2.CameraAccessException;
     method public android.hardware.camera2.CameraDevice openCamera(java.lang.String) throws android.hardware.camera2.CameraAccessException;
-    method public void registerCameraListener(android.hardware.camera2.CameraManager.CameraListener);
-    method public void unregisterCameraListener(android.hardware.camera2.CameraManager.CameraListener);
+    method public void removeAvailabilityListener(android.hardware.camera2.CameraManager.AvailabilityListener);
   }
 
-  public static abstract interface CameraManager.CameraListener {
-    method public abstract void onCameraAvailable(java.lang.String);
-    method public abstract void onCameraUnavailable(java.lang.String);
+  public static abstract class CameraManager.AvailabilityListener {
+    ctor public CameraManager.AvailabilityListener();
+    method public void onCameraAvailable(java.lang.String);
+    method public void onCameraUnavailable(java.lang.String);
   }
 
-  public class CameraMetadata implements java.lang.AutoCloseable android.os.Parcelable {
-    ctor public CameraMetadata();
-    method public void close() throws java.lang.Exception;
-    method public int describeContents();
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
-    method public void readFromParcel(android.os.Parcel);
-    method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T);
-    method public void writeToParcel(android.os.Parcel, int);
+  public abstract class CameraMetadata {
+    method public abstract T get(android.hardware.camera2.CameraMetadata.Key<T>);
     field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1
     field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0
@@ -10950,7 +10950,6 @@
     field public static final int CONTROL_SCENE_MODE_SUNSET = 10; // 0xa
     field public static final int CONTROL_SCENE_MODE_THEATRE = 7; // 0x7
     field public static final int CONTROL_SCENE_MODE_UNSUPPORTED = 0; // 0x0
-    field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int EDGE_MODE_FAST = 1; // 0x1
     field public static final int EDGE_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int EDGE_MODE_OFF = 0; // 0x0
@@ -10983,14 +10982,13 @@
   }
 
   public static class CameraMetadata.Key {
-    ctor public CameraMetadata.Key(java.lang.String, java.lang.Class<T>);
     method public final boolean equals(java.lang.Object);
     method public final java.lang.String getName();
     method public final int hashCode();
   }
 
   public final class CameraProperties extends android.hardware.camera2.CameraMetadata {
-    ctor public CameraProperties();
+    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
     field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
     field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
     field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_RANGE;
@@ -11033,10 +11031,10 @@
   }
 
   public final class CaptureRequest extends android.hardware.camera2.CameraMetadata implements android.os.Parcelable {
-    method public void addTarget(android.view.Surface);
+    method public int describeContents();
+    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
     method public java.lang.Object getTag();
-    method public void removeTarget(android.view.Surface);
-    method public void setTag(java.lang.Object);
+    method public void writeToParcel(android.os.Parcel, int);
     field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
     field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
     field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE;
@@ -11086,7 +11084,17 @@
     field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
   }
 
+  public static final class CaptureRequest.Builder {
+    method public void addTarget(android.view.Surface);
+    method public android.hardware.camera2.CaptureRequest build();
+    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+    method public void removeTarget(android.view.Surface);
+    method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T);
+    method public void setTag(java.lang.Object);
+  }
+
   public final class CaptureResult extends android.hardware.camera2.CameraMetadata {
+    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
     field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
     field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
     field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;
@@ -12745,7 +12753,9 @@
     field public static final java.lang.String KEY_IS_ADTS = "is-adts";
     field public static final java.lang.String KEY_I_FRAME_INTERVAL = "i-frame-interval";
     field public static final java.lang.String KEY_LANGUAGE = "language";
+    field public static final java.lang.String KEY_MAX_HEIGHT = "max-height";
     field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size";
+    field public static final java.lang.String KEY_MAX_WIDTH = "max-width";
     field public static final java.lang.String KEY_MIME = "mime";
     field public static final java.lang.String KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
     field public static final java.lang.String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index a4a56d7..75c0f7d 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -17,6 +17,8 @@
 package android.hardware.camera2;
 
 import android.view.Surface;
+import android.os.Handler;
+import android.util.Log;
 
 import java.lang.AutoCloseable;
 import java.util.List;
@@ -99,6 +101,26 @@
     public static final int TEMPLATE_MANUAL = 5;
 
     /**
+     * Get the ID of this camera device.
+     *
+     * <p>This matches the ID given to {@link CameraManager#openCamera} to instantiate this
+     * this camera device.</p>
+     *
+     * <p>This ID can be used to query the camera device's {@link
+     * CameraProperties fixed properties} with {@link
+     * CameraManager#getCameraProperties}.</p>
+     *
+     * <p>This method can be called even if the device has been closed or has encountered
+     * a serious error.</p>
+     *
+     * @return the ID for this camera device
+     *
+     * @see CameraManager#getCameraProperties
+     * @see CameraManager#getDeviceIdList
+     */
+    public String getId();
+
+    /**
      * Get the static properties for this camera. These are identical to the
      * properties returned by {@link CameraManager#getCameraProperties}.
      *
@@ -109,6 +131,7 @@
      * @see CameraManager#getCameraProperties
      */
     public CameraProperties getProperties() throws CameraAccessException;
+
     /**
      * <p>Set up a new output set of Surfaces for the camera device.</p>
      *
@@ -177,10 +200,16 @@
      * if the format is user-visible, it must be one of android.scaler.availableFormats;
      * and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).</p>
      *
-     * <p>To change the configuration after requests have been submitted to the
-     * device, the camera device must be idle. To idle the device, stop any
-     * repeating requests with {@link #stopRepeating stopRepeating}, and then
-     * call {@link #waitUntilIdle waitUntilIdle}.</p>
+     * <p>To change the output, the camera device must be idle. The device is considered
+     * to be idle once all in-flight and pending capture requests have been processed,
+     * and all output image buffers from the captures have been sent to their destination
+     * Surfaces.</p>
+     *
+     * <p>To reach an idle state without cancelling any submitted captures, first
+     * stop any repeating request/burst with {@link #stopRepeating}, and then
+     * wait for the {@link CameraDeviceListener#onCameraIdle} callback to be
+     * called. To idle as fast as possible, use {@link #flush} and wait for the
+     * idle callback.</p>
      *
      * <p>Using larger resolution outputs, or more outputs, can result in slower
      * output rate from the device.</p>
@@ -193,19 +222,25 @@
      * @throws CameraAccessException if the camera device is no longer connected
      * @throws IllegalStateException if the camera device is not idle, or has
      * encountered a fatal error
+     *
+     * @see CameraDeviceListener#onCameraIdle
+     * @see #stopRepeating
+     * @see #flush
      */
     public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
 
     /**
-     * <p>Create a {@link CaptureRequest} initialized with template for a target
-     * use case. The settings are chosen to be the best options for the specific
-     * camera device, so it is not recommended to reuse the same request for a
-     * different camera device; create a request for that device and override
-     * the settings as desired, instead.</p>
+     * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
+     * initialized with template for a target use case. The settings are chosen
+     * to be the best options for the specific camera device, so it is not
+     * recommended to reuse the same request for a different camera device;
+     * create a builder specific for that device and template and override the
+     * settings as desired, instead.</p>
      *
      * @param templateType An enumeration selecting the use case for this
      * request; one of the CameraDevice.TEMPLATE_ values.
-     * @return a filled-in CaptureRequest, except for output streams
+     * @return a builder for a capture request, initialized with default
+     * settings for that template, and no output streams
      *
      * @throws IllegalArgumentException if the templateType is not in the list
      * of supported templates.
@@ -219,7 +254,7 @@
      * @see #TEMPLATE_VIDEO_SNAPSHOT
      * @see #TEMPLATE_MANUAL
      */
-    public CaptureRequest createCaptureRequest(int templateType)
+    public CaptureRequest.Builder createCaptureRequest(int templateType)
             throws CameraAccessException;
 
     /**
@@ -229,7 +264,10 @@
      * including sensor, lens, flash, and post-processing settings.</p>
      *
      * <p>Each request will produce one {@link CaptureResult} and produce new
-     * frames for one or more target Surfaces, as defined by the request's .</p>
+     * frames for one or more target Surfaces, set with the CaptureRequest
+     * builder's {@link CaptureRequest.Builder#addTarget} method. The target
+     * surfaces must be configured as active outputs with
+     * {@link #configureOutputs} before calling this method.</p>
      *
      * <p>Multiple requests can be in progress at once. They are processed in
      * first-in, first-out order, with minimal delays between each
@@ -242,27 +280,36 @@
      * @param listener The callback object to notify once this request has been
      * processed. If null, no metadata will be produced for this capture,
      * although image data will still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
      *
      * @throws CameraAccessException if the camera device is no longer connected
      * @throws IllegalStateException if the camera device has been closed or the
      * device has encountered a fatal error.
+     * @throws IllegalArgumentException If the request targets Surfaces not
+     * currently configured as outputs. Or if the handler is null, the listener
+     * is not null, and the calling thread has no looper.
      *
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      */
-    public void capture(CaptureRequest request, CaptureListener listener)
+    public void capture(CaptureRequest request, CaptureListener listener, Handler handler)
             throws CameraAccessException;
 
     /**
-     * <p>Submit a list of requests to be captured in sequence as a burst. The
+     * Submit a list of requests to be captured in sequence as a burst. The
      * burst will be captured in the minimum amount of time possible, and will
      * not be interleaved with requests submitted by other capture or repeat
-     * calls.</p>
+     * calls.
      *
      * <p>The requests will be captured in order, each capture producing one
-     * {@link CaptureResult} and frames for one or more
-     * target {@link android.view.Surface surfaces}.</p>
+     * {@link CaptureResult} and image buffers for one or more target
+     * {@link android.view.Surface surfaces}. The target surfaces for each
+     * request (set with {@link CaptureRequest.Builder#addTarget}) must be
+     * configured as active outputs with {@link #configureOutputs} before
+     * calling this method.</p>
      *
      * <p>The main difference between this method and simply calling
      * {@link #capture} repeatedly is that this method guarantees that no
@@ -273,29 +320,38 @@
      * requests in the burst has been processed. If null, no metadata will be
      * produced for any requests in this burst, although image data will still
      * be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
      *
      * @throws CameraAccessException if the camera device is no longer connected
      * @throws IllegalStateException if the camera device has been closed or the
      * device has encountered a fatal error.
+     * @throws IllegalArgumentException If the requests target Surfaces not
+     * currently configured as outputs. Or if the handler is null, the listener
+     * is not null, and the calling thread has no looper.
      *
      * @see #capture
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      */
-    public void captureBurst(List<CaptureRequest> requests,
-            CaptureListener listener) throws CameraAccessException;
+    public void captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
 
     /**
-     * <p>Request endlessly repeating capture of images by this
-     * CameraDevice.</p>
+     * Request endlessly repeating capture of images by this CameraDevice.
      *
-     * <p>With this method, the CameraDevice will continually capture
-     * images using the settings in the provided {@link
-     * CaptureRequest}, at the maximum rate possible.</p>
+     * <p>With this method, the CameraDevice will continually capture images
+     * using the settings in the provided {@link CaptureRequest}, at the maximum
+     * rate possible.</p>
+     *
+     * <p>Repeating requests are a simple way for an application to maintain a
+     * preview or other continuous stream of frames, without having to
+     * continually submit identical requests through {@link #capture}.</p>
      *
      * <p>Repeat requests have lower priority than those submitted
      * through {@link #capture} or {@link #captureBurst}, so if
-     * capture() is called when a repeating request is active, the
+     * {@link #capture} is called when a repeating request is active, the
      * capture request will be processed before any further repeating
      * requests are processed.<p>
      *
@@ -306,20 +362,27 @@
      * <p>To stop the repeating capture, call {@link #stopRepeating}. Calling
      * {@link #flush} will also clear the request.</p>
      *
-     * <p>Calling repeat will replace a burst set up by {@link
-     * #setRepeatingBurst}, although any in-progress burst will be
-     * completed before the new repeat request will be used.</p>
+     * <p>Calling this method will replace any earlier repeating request or
+     * burst set up by this method or {@link #setRepeatingBurst}, although any
+     * in-progress burst will be completed before the new repeat request will be
+     * used.</p>
      *
      * @param request the request to repeat indefinitely
      * @param listener The callback object to notify every time the
      * request finishes processing. If null, no metadata will be
      * produced for this stream of requests, although image data will
      * still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
      *
      * @throws CameraAccessException if the camera device is no longer
      * connected
      * @throws IllegalStateException if the camera device has been closed or the
      * device has encountered a fatal error.
+     * @throws IllegalArgumentException If the requests reference Surfaces not
+     * currently configured as outputs. Or if the handler is null, the listener
+     * is not null, and the calling thread has no looper.
      *
      * @see #capture
      * @see #captureBurst
@@ -327,8 +390,8 @@
      * @see #stopRepeating
      * @see #flush
      */
-    public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
-            throws CameraAccessException;
+    public void setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
 
     /**
      * <p>Request endlessly repeating capture of a sequence of images by this
@@ -347,26 +410,33 @@
      *
      * <p>Repeating burst requests are a simple way for an application to
      * maintain a preview or other continuous stream of frames where each
-     * request is different in a predicatable way, without having to submit
-     * requests through {@link #capture} at video rates.</p>
+     * request is different in a predicatable way, without having to continually
+     * submit requests through {@link #captureBurst} .</p>
      *
      * <p>To stop the repeating capture, call {@link #stopRepeating}. Any
      * ongoing burst will still be completed, however. Calling
      * {@link #flush} will also clear the request.</p>
      *
-     * <p>Calling repeatBurst will replace a repeating request set up by
-     * {@link #setRepeatingRequest}, although any in-progress capture will be completed
-     * before the new repeat burst will be used.</p>
+     * <p>Calling this method will replace a previously-set repeating request or
+     * burst set up by this method or {@link #setRepeatingRequest}, although any
+     * in-progress burst will be completed before the new repeat burst will be
+     * used.</p>
      *
      * @param requests the list of requests to cycle through indefinitely
      * @param listener The callback object to notify each time one of the
      * requests in the repeating bursts has finished processing. If null, no
      * metadata will be produced for this stream of requests, although image
      * data will still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
      *
      * @throws CameraAccessException if the camera device is no longer connected
      * @throws IllegalStateException if the camera device has been closed or the
      * device has encountered a fatal error.
+     * @throws IllegalArgumentException If the requests reference Surfaces not
+     * currently configured as outputs. Or if the handler is null, the listener
+     * is not null, and the calling thread has no looper.
      *
      * @see #capture
      * @see #captureBurst
@@ -374,8 +444,8 @@
      * @see #stopRepeating
      * @see #flush
      */
-    public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
-            throws CameraAccessException;
+    public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
 
     /**
      * <p>Cancel any ongoing repeating capture set by either
@@ -385,8 +455,9 @@
      *
      * <p>Any currently in-flight captures will still complete, as will any
      * burst that is mid-capture. To ensure that the device has finished
-     * processing all of its capture requests and is in idle state, use the
-     * {@link #waitUntilIdle waitUntilIdle} method.</p>
+     * processing all of its capture requests and is in idle state, wait for the
+     * {@link CameraDeviceListener#onCameraIdle} callback after calling this
+     * method..</p>
      *
      * @throws CameraAccessException if the camera device is no longer connected
      * @throws IllegalStateException if the camera device has been closed or the
@@ -394,7 +465,7 @@
      *
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
-     * @see #waitUntilIdle
+     * @see CameraDeviceListener#onCameraIdle
      *
      * @throws CameraAccessException if the camera device is no longer connected
      * @throws IllegalStateException if the camera device has been closed, the
@@ -429,16 +500,27 @@
     public void waitUntilIdle() throws CameraAccessException;
 
     /**
-     * Set the error listener object to call when an asynchronous error
-     * occurs. The errors reported here are only device-wide errors; errors
-     * about individual requests or frames are reported through
-     * {@link CaptureListener#onCaptureFailed}.
+     * Set the listener object to call when an asynchronous device event occurs,
+     * such as errors or idle notifications.
      *
-     * @param listener the ErrorListener to send asynchronous error
-     * notifications to. Setting this to null will stop notifications about
-     * asynchronous errors.
+     * <p>The events reported here are device-wide; notifications about
+     * individual capture requests or capture results are reported through
+     * {@link CaptureListener}.</p>
+     *
+     * <p>If the camera device is idle when the listener is set, then the
+     * {@link CameraDeviceListener#onCameraIdle} method will be immediately called,
+     * even if the device has never been active before.
+     * </p>
+     *
+     * @param listener the CameraDeviceListener to send device-level event
+     * notifications to. Setting this to null will stop notifications.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if handler is null, the listener is
+     * not null, and the calling thread has no looper
      */
-    public void setErrorListener(ErrorListener listener);
+    public void setDeviceListener(CameraDeviceListener listener, Handler handler);
 
     /**
      * Flush all captures currently pending and in-progress as fast as
@@ -446,7 +528,7 @@
      *
      * <p>The camera device will discard all of its current work as fast as
      * possible. Some in-flight captures may complete successfully and call
-     * {@link CaptureListener#onCaptureComplete}, while others will trigger
+     * {@link CaptureListener#onCaptureCompleted}, while others will trigger
      * their {@link CaptureListener#onCaptureFailed} callbacks. If a repeating
      * request or a repeating burst is set, it will be cleared by the flush.</p>
      *
@@ -483,14 +565,60 @@
     // TODO: We should decide on the behavior of in-flight requests should be on close.
 
     /**
-     * A listener for receiving metadata about completed image captures. The
-     * metadata includes, among other things, the final capture settings and the
-     * state of the control algorithms.
+     * <p>A listener for tracking the progress of a {@link CaptureRequest}
+     * submitted to the camera device.</p>
+     *
+     * <p>This listener is called when a request triggers a capture to start,
+     * and when the capture is complete. In case on an error capturing an image,
+     * the error method is triggered instead of the completion method.</p>
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     *
      */
-    public interface CaptureListener {
+    public static abstract class CaptureListener {
+
         /**
-         * <p>Called when a capture request has been processed by a
-         * {@link CameraDevice}.</p>
+         * This method is called when the camera device has started capturing
+         * the output image for the request, at the beginning of image exposure.
+         *
+         * <p>This callback is invoked right as the capture of a frame begins,
+         * so it is the most appropriate time for playing a shutter sound,
+         * or triggering UI indicators of capture.</p>
+         *
+         * <p>The request that is being used for this capture is provided, along
+         * with the actual timestamp for the start of exposure. This timestamp
+         * matches the timestamp that will be included in
+         * {@link CaptureResult#SENSOR_TIMESTAMP the result timestamp field},
+         * and in the buffers sent to each output Surface. These buffer
+         * timestamps are accessible through, for example,
+         * {@link android.media.Image#getTimestamp() Image.getTimestamp()} or
+         * {@link android.graphics.SurfaceTexture#getTimestamp()}.</p>
+         *
+         * <p>For the simplest way to play a shutter sound camera shutter or a
+         * video recording start/stop sound, see the
+         * {@link android.media.MediaActionSound} class.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera the CameraDevice sending the callback
+         * @param request the request for the capture that just begun
+         * @param timestamp the timestamp at start of capture, in nanoseconds.
+         *
+         * @see android.media.MediaActionSound
+         */
+        public void onCaptureStarted(CameraDevice camera,
+                CaptureRequest request, long timestamp) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when an image capture has completed and the
+         * result metadata is available.
+         *
+         * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera The CameraDevice sending the callback.
          * @param request The request that was given to the CameraDevice
@@ -503,14 +631,21 @@
          * @see #setRepeatingRequest
          * @see #setRepeatingBurst
          */
-        public void onCaptureComplete(CameraDevice camera,
-                CaptureRequest request, CaptureResult result);
+        public void onCaptureCompleted(CameraDevice camera,
+                CaptureRequest request, CaptureResult result) {
+            // default empty implementation
+        }
 
         /**
-         * <p>Called instead of onCaptureComplete when the camera device failed
-         * to produce a CaptureResult for the request. Other requests are
-         * unaffected, and some or all image buffers from the capture may have
-         * been pushed to their respective output streams.</p>
+         * This method is called instead of {@link #onCaptureCompleted} when the
+         * camera device failed to produce a {@link CaptureResult} for the
+         * request.
+         *
+         * <p>Other requests are unaffected, and some or all image buffers from
+         * the capture may have been pushed to their respective output
+         * streams.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera The CameraDevice sending the callback.
          * @param request The request that was given to the CameraDevice
@@ -521,62 +656,129 @@
          * @see #setRepeatingBurst
          */
         public void onCaptureFailed(CameraDevice camera,
-                CaptureRequest request);
+                CaptureRequest request) {
+            // default empty implementation
+        }
     }
 
     /**
-     * <p>A listener for asynchronous errors from the camera device. Errors
-     * about specific {@link CaptureRequest CaptureRequests} are sent through
-     * the capture {@link CaptureListener#onCaptureFailed listener}
-     * interface. Errors reported through this listener affect the device as a
-     * whole.</p>
+     * A listener for notifications about the state of a camera
+     * device.
+     *
+     * <p>These events include notifications about the device becoming idle (
+     * allowing for {@link #configureOutputs} to be called), about device
+     * disconnection, and about unexpected device errors.</p>
+     *
+     * <p>Events about the progress of specific {@link CaptureRequest
+     * CaptureRequests} are provided through a {@link CaptureListener} given to
+     * the {@link #capture}, {@link #captureBurst}, {@link
+     * #setRepeatingRequest}, or {@link #setRepeatingBurst} methods.
+     *
+     * @see #setDeviceListener
      */
-    public interface ErrorListener {
+    public static abstract class CameraDeviceListener {
+
         /**
-         * <p>This camera device has been disconnected by the camera
-         * service. Any attempt to call methods on this CameraDevice will throw
-         * a {@link CameraAccessException}. The disconnection could be due to a
+         * An error code that can be reported by {@link #onCameraError}
+         * indicating that the camera device has encountered a fatal error.
+         *
+         * <p>The camera device needs to be re-opened to be used again.</p>
+         *
+         * @see #onCameraDeviceError
+         */
+        public static final int ERROR_CAMERA_DEVICE = 1;
+
+        /**
+         * An error code that can be reported by {@link #onCameraError}
+         * indicating that the camera service has encountered a fatal error.
+         *
+         * <p>The Android device may need to be shut down and restarted to restore
+         * camera function, or there may be a persistent hardware problem.</p>
+         *
+         * @see #onCameraDeviceError
+         */
+        public static final int ERROR_CAMERA_SERVICE = 2;
+
+        /**
+         * The method called when a camera device has finished processing all
+         * submitted capture requests and has reached an idle state.
+         *
+         * <p>An idle camera device can have its outputs changed by calling
+         * {@link CameraDevice#configureOutputs}.</p>
+         *
+         * <p>To idle and reconfigure outputs without cancelling any submitted
+         * capture requests, the application needs to clear its repeating
+         * request/burst, if set, with {@link CameraDevice#stopRepeating}, and
+         * then wait for this callback to be called before calling {@link
+         * CameraDevice#configureOutputs}.</p>
+         *
+         * <p>To idle and reconfigure a camera device as fast as possible, the
+         * {@link CameraDevice#flush} method can be used, which will discard all
+         * pending and in-progess capture requests. Once the {@link
+         * CameraDevice#flush} method is called, the application must wait for
+         * this callback to fire before calling {@link
+         * CameraDevice#configureOutputs}.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera the camera device that has become idle
+         *
+         * @see CameraDevice#configureOutputs
+         * @see CameraDevice#stopRepeating
+         * @see CameraDevice#flush
+         */
+        public void onCameraIdle(CameraDevice camera) {
+            // Default empty implementation
+        }
+
+        /**
+         * The method called when a camera device is no longer available for
+         * use.
+         *
+         * <p>Any attempt to call methods on this CameraDevice will throw a
+         * {@link CameraAccessException}. The disconnection could be due to a
          * change in security policy or permissions; the physical disconnection
          * of a removable camera device; or the camera being needed for a
          * higher-priority use case.</p>
          *
-         * <p>There may still be capture completion or camera stream listeners
-         * that will be called after this error is received.</p>
+         * <p>There may still be capture listener callbacks that are called
+         * after this method is called, or new image buffers that are delivered
+         * to active outputs.</p>
+         *
+         * <p>The default implementation logs a notice to the system log
+         * about the disconnection.</p>
+         *
+         * @param camera the device that has been disconnected
          */
-        public static final int DEVICE_DISCONNECTED = 1;
+        public void onCameraDisconnected(CameraDevice camera) {
+            Log.i("CameraListener",
+                    String.format("Camera device %s disconnected", camera.getId()));
+        }
 
         /**
-         * <p>The camera device has encountered a fatal error. Any attempt to
-         * call methods on this CameraDevice will throw a
-         * {@link java.lang.IllegalStateException}.</p>
+         * The method called when a camera device has encountered a serious error.
+         *
+         * <p>This indicates a failure of the camera device or camera service in
+         * some way. Any attempt to call methods on this CameraDevice in the
+         * future will throw a {@link java.lang.IllegalStateException}.</p>
          *
          * <p>There may still be capture completion or camera stream listeners
          * that will be called after this error is received.</p>
          *
-         * <p>The application needs to re-open the camera device to use it
-         * again.</p>
-         */
-        public static final int DEVICE_ERROR = 2;
-
-        /**
-         * <p>The camera service has encountered a fatal error. Any attempt to
-         * call methods on this CameraDevice in the future will throw a
-         * {@link java.lang.IllegalStateException}.</p>
-         *
-         * <p>There may still be capture completion or camera stream listeners
-         * that will be called after this error is received.</p>
-         *
-         * <p>The device may need to be shut down and restarted to restore
-         * camera function, or there may be a persistent hardware problem.</p>
-         */
-        public static final int SERVICE_ERROR = 3;
-
-        /**
-         * The method to call when a camera device has encountered an error.
+         * <p>The default implementation logs an error to the system log about
+         * the camera failure.</p>
          *
          * @param camera The device reporting the error
-         * @param error The error code, one of the ErrorListener.ERROR_ values.
+         * @param error The error code, one of the
+         *     {@code CameraDeviceListener.ERROR_*} values.
+         *
+         * @see #ERROR_CAMERA_DEVICE
+         * @see #ERROR_CAMERA_SERVICE
          */
-        public void onCameraDeviceError(CameraDevice camera, int error);
+        public void onCameraError(CameraDevice camera, int error) {
+            Log.e("CameraListener",
+                    String.format("Camera device %s has encountered an error: %d",
+                            camera.getId(), error));
+        }
     }
 }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index ff9282e..4ad9259 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -20,17 +20,19 @@
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
 import android.hardware.IProCameraUser;
+import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.BinderHolder;
 import android.os.IBinder;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
+import android.util.ArrayMap;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
 
 /**
  * <p>An interface for iterating, listing, and connecting to
@@ -55,7 +57,10 @@
 
     private final ICameraService mCameraService;
     private ArrayList<String> mDeviceIdList;
-    private final HashSet<CameraListener> mListenerSet = new HashSet<CameraListener>();
+
+    private ArrayMap<AvailabilityListener, Handler> mListenerMap =
+            new ArrayMap<AvailabilityListener, Handler>();
+
     private final Context mContext;
     private final Object mLock = new Object();
 
@@ -85,14 +90,16 @@
     }
 
     /**
-     * <p>Return the list of currently connected camera devices by
-     * identifier. Non-removable cameras use integers starting at 0 for their
+     * Return the list of currently connected camera devices by
+     * identifier.
+     *
+     * <p>Non-removable cameras use integers starting at 0 for their
      * identifiers, while removable cameras have a unique identifier for each
      * individual device, even if they are the same model.</p>
      *
      * @return The list of currently connected camera devices.
      */
-    public String[] getDeviceIdList() throws CameraAccessException {
+    public String[] getCameraIdList() throws CameraAccessException {
         synchronized (mLock) {
             try {
                 return getOrCreateDeviceIdListLocked().toArray(new String[0]);
@@ -107,13 +114,25 @@
     /**
      * Register a listener to be notified about camera device availability.
      *
-     * Registering a listener more than once has no effect.
+     * <p>Registering the same listener again will replace the handler with the
+     * new one provided.</p>
      *
      * @param listener The new listener to send camera availability notices to
+     * @param handler The handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper looper}.
      */
-    public void registerCameraListener(CameraListener listener) {
+    public void addAvailabilityListener(AvailabilityListener listener, Handler handler) {
+        if (handler == null) {
+            Looper looper = Looper.myLooper();
+            if (looper == null) {
+                throw new IllegalArgumentException(
+                        "No handler given, and current thread has no looper!");
+            }
+            handler = new Handler(looper);
+        }
+
         synchronized (mLock) {
-            mListenerSet.add(listener);
+            mListenerMap.put(listener, handler);
         }
     }
 
@@ -121,13 +140,13 @@
      * Remove a previously-added listener; the listener will no longer receive
      * connection and disconnection callbacks.
      *
-     * Removing a listener that isn't registered has no effect.
+     * <p>Removing a listener that isn't registered has no effect.</p>
      *
      * @param listener The listener to remove from the notification list
      */
-    public void unregisterCameraListener(CameraListener listener) {
+    public void removeAvailabilityListener(AvailabilityListener listener) {
         synchronized (mLock) {
-            mListenerSet.remove(listener);
+            mListenerMap.remove(listener);
         }
     }
 
@@ -144,7 +163,7 @@
      * @throws SecurityException if the application does not have permission to
      * access the camera
      *
-     * @see #getDeviceIdList
+     * @see #getCameraIdList
      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
      */
     public CameraProperties getCameraProperties(String cameraId)
@@ -160,14 +179,14 @@
         // TODO: implement and call a service function to get the capabilities on C++ side
 
         // TODO: get properties from service
-        return new CameraProperties();
+        return new CameraProperties(new CameraMetadataNative());
     }
 
     /**
      * Open a connection to a camera with the given ID. Use
-     * {@link #getDeviceIdList} to get the list of available camera
+     * {@link #getCameraIdList} to get the list of available camera
      * devices. Note that even if an id is listed, open may fail if the device
-     * is disconnected between the calls to {@link #getDeviceIdList} and
+     * is disconnected between the calls to {@link #getCameraIdList} and
      * {@link #openCamera}.
      *
      * @param cameraId The unique identifier of the camera device to open
@@ -179,7 +198,7 @@
      * @throws SecurityException if the application does not have permission to
      * access the camera
      *
-     * @see #getDeviceIdList
+     * @see #getCameraIdList
      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
      */
     public CameraDevice openCamera(String cameraId) throws CameraAccessException {
@@ -218,29 +237,43 @@
     }
 
     /**
-     * Interface for listening to cameras becoming available or unavailable.
-     * Cameras become available when they are no longer in use, or when a new
+     * Interface for listening to camera devices becoming available or
+     * unavailable.
+     *
+     * <p>Cameras become available when they are no longer in use, or when a new
      * removable camera is connected. They become unavailable when some
      * application or service starts using a camera, or when a removable camera
-     * is disconnected.
+     * is disconnected.</p>
+     *
+     * @see addAvailabilityListener
      */
-    public interface CameraListener {
+    public static abstract class AvailabilityListener {
+
         /**
          * A new camera has become available to use.
          *
+         * <p>The default implementation of this method does nothing.</p>
+         *
          * @param cameraId The unique identifier of the new camera.
          */
-        public void onCameraAvailable(String cameraId);
+        public void onCameraAvailable(String cameraId) {
+            // default empty implementation
+        }
 
         /**
-         * A previously-available camera has become unavailable for use. If an
-         * application had an active CameraDevice instance for the
-         * now-disconnected camera, that application will receive a {@link
-         * CameraDevice.ErrorListener#DEVICE_DISCONNECTED disconnection error}.
+         * A previously-available camera has become unavailable for use.
+         *
+         * <p>If an application had an active CameraDevice instance for the
+         * now-disconnected camera, that application will receive a
+         * {@link CameraDevice.CameraDeviceListener#onCameraDisconnected disconnection error}.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
          *
          * @param cameraId The unique identifier of the disconnected camera.
          */
-        public void onCameraUnavailable(String cameraId);
+        public void onCameraUnavailable(String cameraId) {
+            // default empty implementation
+        }
     }
 
     private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
@@ -285,7 +318,7 @@
         public static final int STATUS_NOT_AVAILABLE = 0x80000000;
 
         // Camera ID -> Status map
-        private final HashMap<String, Integer> mDeviceStatus = new HashMap<String, Integer>();
+        private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
 
         private static final String TAG = "CameraServiceListener";
 
@@ -322,7 +355,7 @@
                 Log.v(TAG,
                         String.format("Camera id %d has status changed to 0x%x", cameraId, status));
 
-                String id = String.valueOf(cameraId);
+                final String id = String.valueOf(cameraId);
 
                 if (!validStatus(status)) {
                     Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
@@ -363,11 +396,24 @@
                     return;
                 }
 
-                for (CameraListener listener : mListenerSet) {
+                final int listenerCount = mListenerMap.size();
+                for (int i = 0; i < listenerCount; i++) {
+                    Handler handler = mListenerMap.valueAt(i);
+                    final AvailabilityListener listener = mListenerMap.keyAt(i);
                     if (isAvailable(status)) {
-                        listener.onCameraAvailable(id);
+                        handler.post(
+                            new Runnable() {
+                                public void run() {
+                                    listener.onCameraAvailable(id);
+                                }
+                            });
                     } else {
-                        listener.onCameraUnavailable(id);
+                        handler.post(
+                            new Runnable() {
+                                public void run() {
+                                    listener.onCameraUnavailable(id);
+                                }
+                            });
                     }
                 } // for
             } // synchronized
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 70d777f..18fffc0 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,20 +16,7 @@
 
 package android.hardware.camera2;
 
-import android.hardware.camera2.impl.MetadataMarshalClass;
-import android.hardware.camera2.impl.MetadataMarshalRect;
-import android.hardware.camera2.impl.MetadataMarshalSize;
-import android.hardware.camera2.impl.MetadataMarshalString;
-import android.os.Parcelable;
-import android.os.Parcel;
-import android.util.Log;
-
-import java.lang.reflect.Array;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
+import android.hardware.camera2.impl.CameraMetadataNative;
 
 /**
  * The base class for camera controls and information.
@@ -42,62 +29,12 @@
  * @see CameraManager
  * @see CameraProperties
  **/
-public class CameraMetadata implements Parcelable, AutoCloseable {
-
-    public CameraMetadata() {
-        mMetadataMap = new HashMap<Key<?>, Object>();
-
-        mMetadataPtr = nativeAllocate();
-        if (mMetadataPtr == 0) {
-            throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
-        }
-    }
-
-    public static final Parcelable.Creator<CameraMetadata> CREATOR =
-            new Parcelable.Creator<CameraMetadata>() {
-        @Override
-        public CameraMetadata createFromParcel(Parcel in) {
-            CameraMetadata metadata = new CameraMetadata();
-            metadata.readFromParcel(in);
-            return metadata;
-        }
-
-        @Override
-        public CameraMetadata[] newArray(int size) {
-            return new CameraMetadata[size];
-        }
-    };
-
-    private static final String TAG = "CameraMetadataJV";
+public abstract class CameraMetadata {
 
     /**
-     * Set a camera metadata field to a value. The field definitions can be
-     * found in {@link CameraProperties}, {@link CaptureResult}, and
-     * {@link CaptureRequest}.
-     *
-     * @param key The metadata field to write.
-     * @param value The value to set the field to, which must be of a matching
-     * type to the key.
+     * @hide
      */
-    public <T> void set(Key<T> key, T value) {
-        int tag = key.getTag();
-
-        if (value == null) {
-            writeValues(tag, null);
-            return;
-        }
-
-        int nativeType = getNativeType(tag);
-
-        int size = packSingle(value, null, key.mType, nativeType, /* sizeOnly */true);
-
-        // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
-        byte[] values = new byte[size];
-
-        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
-        packSingle(value, buffer, key.mType, nativeType, /*sizeOnly*/false);
-
-        writeValues(tag, values);
+    protected CameraMetadata() {
     }
 
     /**
@@ -110,342 +47,16 @@
      * @param key The metadata field to read.
      * @return The value of that key, or {@code null} if the field is not set.
      */
-    @SuppressWarnings("unchecked")
-    public <T> T get(Key<T> key) {
-        int tag = key.getTag();
-        byte[] values = readValues(tag);
-        if (values == null) {
-            return null;
-        }
-
-        int nativeType = getNativeType(tag);
-
-        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
-        return unpackSingle(buffer, key.mType, nativeType);
-    }
-
-    // Keep up-to-date with camera_metadata.h
-    /**
-     * @hide
-     */
-    public static final int TYPE_BYTE = 0;
-    /**
-     * @hide
-     */
-    public static final int TYPE_INT32 = 1;
-    /**
-     * @hide
-     */
-    public static final int TYPE_FLOAT = 2;
-    /**
-     * @hide
-     */
-    public static final int TYPE_INT64 = 3;
-    /**
-     * @hide
-     */
-    public static final int TYPE_DOUBLE = 4;
-    /**
-     * @hide
-     */
-    public static final int TYPE_RATIONAL = 5;
-    /**
-     * @hide
-     */
-    public static final int NUM_TYPES = 6;
-
-    private static int getTypeSize(int nativeType) {
-        switch(nativeType) {
-            case TYPE_BYTE:
-                return 1;
-            case TYPE_INT32:
-            case TYPE_FLOAT:
-                return 4;
-            case TYPE_INT64:
-            case TYPE_DOUBLE:
-            case TYPE_RATIONAL:
-                return 8;
-        }
-
-        throw new UnsupportedOperationException("Unknown type, can't get size "
-                + nativeType);
-    }
-
-    private static Class<?> getExpectedType(int nativeType) {
-        switch(nativeType) {
-            case TYPE_BYTE:
-                return Byte.TYPE;
-            case TYPE_INT32:
-                return Integer.TYPE;
-            case TYPE_FLOAT:
-                return Float.TYPE;
-            case TYPE_INT64:
-                return Long.TYPE;
-            case TYPE_DOUBLE:
-                return Double.TYPE;
-            case TYPE_RATIONAL:
-                return Rational.class;
-        }
-
-        throw new UnsupportedOperationException("Unknown type, can't map to Java type "
-                + nativeType);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type,
-            int nativeType, boolean sizeOnly) {
-
-        if (!sizeOnly) {
-            /**
-             * Rewrite types when the native type doesn't match the managed type
-             *  - Boolean -> Byte
-             *  - Integer -> Byte
-             */
-
-            if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
-                // Since a boolean can't be cast to byte, and we don't want to use putBoolean
-                boolean asBool = (Boolean) value;
-                byte asByte = (byte) (asBool ? 1 : 0);
-                value = (T) (Byte) asByte;
-            } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
-                int asInt = (Integer) value;
-                byte asByte = (byte) asInt;
-                value = (T) (Byte) asByte;
-            } else if (type != getExpectedType(nativeType)) {
-                throw new UnsupportedOperationException("Tried to pack a type of " + type +
-                        " but we expected the type to be " + getExpectedType(nativeType));
-            }
-
-            if (nativeType == TYPE_BYTE) {
-                buffer.put((Byte) value);
-            } else if (nativeType == TYPE_INT32) {
-                buffer.putInt((Integer) value);
-            } else if (nativeType == TYPE_FLOAT) {
-                buffer.putFloat((Float) value);
-            } else if (nativeType == TYPE_INT64) {
-                buffer.putLong((Long) value);
-            } else if (nativeType == TYPE_DOUBLE) {
-                buffer.putDouble((Double) value);
-            } else if (nativeType == TYPE_RATIONAL) {
-                Rational r = (Rational) value;
-                buffer.putInt(r.getNumerator());
-                buffer.putInt(r.getDenominator());
-            }
-
-        }
-
-        return getTypeSize(nativeType);
-    }
-
-    @SuppressWarnings({"unchecked", "rawtypes"})
-    private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType,
-            boolean sizeOnly) {
-
-        int size = 0;
-
-        if (type.isPrimitive() || type == Rational.class) {
-            size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
-        } else if (type.isEnum()) {
-            size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly);
-        } else if (type.isArray()) {
-            size = packArray(value, buffer, type, nativeType, sizeOnly);
-        } else {
-            size = packClass(value, buffer, type, nativeType, sizeOnly);
-        }
-
-        return size;
-    }
-
-    private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type,
-            int nativeType, boolean sizeOnly) {
-
-        // TODO: add support for enums with their own values.
-        return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
-            boolean sizeOnly) {
-
-        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
-        if (marshaler == null) {
-            throw new IllegalArgumentException(String.format("Unknown Key type: %s", type));
-        }
-
-        return marshaler.marshal(value, buffer, nativeType, sizeOnly);
-    }
-
-    private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType,
-            boolean sizeOnly) {
-
-        int size = 0;
-        int arrayLength = Array.getLength(value);
-
-        @SuppressWarnings("unchecked")
-        Class<Object> componentType = (Class<Object>)type.getComponentType();
-
-        for (int i = 0; i < arrayLength; ++i) {
-            size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly);
-        }
-
-        return size;
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        T val;
-
-        if (nativeType == TYPE_BYTE) {
-            val = (T) (Byte) buffer.get();
-        } else if (nativeType == TYPE_INT32) {
-            val = (T) (Integer) buffer.getInt();
-        } else if (nativeType == TYPE_FLOAT) {
-            val = (T) (Float) buffer.getFloat();
-        } else if (nativeType == TYPE_INT64) {
-            val = (T) (Long) buffer.getLong();
-        } else if (nativeType == TYPE_DOUBLE) {
-            val = (T) (Double) buffer.getDouble();
-        } else if (nativeType == TYPE_RATIONAL) {
-            val = (T) new Rational(buffer.getInt(), buffer.getInt());
-        } else {
-            throw new UnsupportedOperationException("Unknown type, can't unpack a native type "
-                + nativeType);
-        }
-
-        /**
-         * Rewrite types when the native type doesn't match the managed type
-         *  - Byte -> Boolean
-         *  - Byte -> Integer
-         */
-
-        if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
-            // Since a boolean can't be cast to byte, and we don't want to use getBoolean
-            byte asByte = (Byte) val;
-            boolean asBool = asByte != 0;
-            val = (T) (Boolean) asBool;
-        } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
-            byte asByte = (Byte) val;
-            int asInt = asByte;
-            val = (T) (Integer) asInt;
-        } else if (type != getExpectedType(nativeType)) {
-            throw new UnsupportedOperationException("Tried to unpack a type of " + type +
-                    " but we expected the type to be " + getExpectedType(nativeType));
-        }
-
-        return val;
-    }
-
-    @SuppressWarnings({"unchecked", "rawtypes"})
-    private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        if (type.isPrimitive() || type == Rational.class) {
-            return unpackSingleNative(buffer, type, nativeType);
-        }
-
-        if (type.isEnum()) {
-            return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType);
-        }
-
-        if (type.isArray()) {
-            return unpackArray(buffer, type, nativeType);
-        }
-
-        T instance = unpackClass(buffer, type, nativeType);
-
-        return instance;
-    }
-
-    private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type,
-            int nativeType) {
-        int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType);
-        return getEnumFromValue(type, ordinal);
-    }
-
-    private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
-        if (marshaler == null) {
-            throw new IllegalArgumentException("Unknown class type: " + type);
-        }
-
-        return marshaler.unmarshal(buffer, nativeType);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        Class<?> componentType = type.getComponentType();
-        Object array;
-
-        int elementSize = getTypeSize(nativeType);
-
-        MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
-        if (marshaler != null) {
-            elementSize = marshaler.getNativeSize(nativeType);
-        }
-
-        if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
-            int remaining = buffer.remaining();
-            int arraySize = remaining / elementSize;
-
-            Log.v(TAG,
-                    String.format(
-                            "Attempting to unpack array (count = %d, element size = %d, bytes " +
-                                    "remaining = %d) for type %s",
-                            arraySize, elementSize, remaining, type));
-
-            array = Array.newInstance(componentType, arraySize);
-            for (int i = 0; i < arraySize; ++i) {
-               Object elem = unpackSingle(buffer, componentType, nativeType);
-               Array.set(array, i, elem);
-            }
-        } else {
-            // Dynamic size, use an array list.
-            ArrayList<Object> arrayList = new ArrayList<Object>();
-
-            int primitiveSize = getTypeSize(nativeType);
-            while (buffer.remaining() >= primitiveSize) {
-                Object elem = unpackSingle(buffer, componentType, nativeType);
-                arrayList.add(elem);
-            }
-
-            array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
-        }
-
-        if (buffer.remaining() != 0) {
-            Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
-                    + type);
-        }
-
-        return (T) array;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        nativeWriteToParcel(dest);
-    }
-
-    /**
-     * Expand this object from a Parcel.
-     * @param in The Parcel from which the object should be read
-     */
-    public void readFromParcel(Parcel in) {
-        nativeReadFromParcel(in);
-    }
+    public abstract <T> T get(Key<T> key);
 
     public static class Key<T> {
 
         private boolean mHasTag;
         private int mTag;
         private final Class<T> mType;
+        private final String mName;
 
-        /*
+        /**
          * @hide
          */
         public Key(String name, Class<T> type) {
@@ -483,8 +94,6 @@
             return mName.equals(lhs.mName);
         }
 
-        private final String mName;
-
         /**
          * <p>
          * Get the tag corresponding to this key. This enables insertion into the
@@ -499,267 +108,20 @@
          */
         public final int getTag() {
             if (!mHasTag) {
-                mTag = CameraMetadata.getTag(mName);
+                mTag = CameraMetadataNative.getTag(mName);
                 mHasTag = true;
             }
             return mTag;
         }
-    }
 
-    private final Map<Key<?>, Object> mMetadataMap;
-
-    private long mMetadataPtr; // native CameraMetadata*
-
-    private native long nativeAllocate();
-    private native synchronized void nativeWriteToParcel(Parcel dest);
-    private native synchronized void nativeReadFromParcel(Parcel source);
-    private native synchronized void nativeSwap(CameraMetadata other) throws NullPointerException;
-    private native synchronized void nativeClose();
-    private native synchronized boolean nativeIsEmpty();
-    private native synchronized int nativeGetEntryCount();
-
-    private native synchronized byte[] nativeReadValues(int tag);
-    private native synchronized void nativeWriteValues(int tag, byte[] src);
-
-    private static native int nativeGetTagFromKey(String keyName)
-            throws IllegalArgumentException;
-    private static native int nativeGetTypeFromTag(int tag)
-            throws IllegalArgumentException;
-    private static native void nativeClassInit();
-
-    /**
-     * <p>Perform a 0-copy swap of the internal metadata with another object.</p>
-     *
-     * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
-     *
-     * @param other Metadata to swap with
-     * @throws NullPointerException if other was null
-     * @hide
-     */
-    public void swap(CameraMetadata other) {
-        nativeSwap(other);
-    }
-
-    /**
-     * @hide
-     */
-    public int getEntryCount() {
-        return nativeGetEntryCount();
-    }
-
-    /**
-     * Does this metadata contain at least 1 entry?
-     *
-     * @hide
-     */
-    public boolean isEmpty() {
-        return nativeIsEmpty();
-    }
-
-    /**
-     * <p>Closes this object, and releases all native resources associated with it.</p>
-     *
-     * <p>Calling any other public method after this will result in an IllegalStateException
-     * being thrown.</p>
-     */
-    @Override
-    public void close() throws Exception {
-        // this sets mMetadataPtr to 0
-        nativeClose();
-        mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
-    }
-
-    /**
-     * Whether or not {@link #close} has already been called (at least once) on this object.
-     * @hide
-     */
-    public boolean isClosed() {
-        synchronized (this) {
-            return mMetadataPtr == 0;
+        /**
+         * @hide
+         */
+        public final Class<T> getType() {
+            return mType;
         }
     }
 
-    /**
-     * Convert a key string into the equivalent native tag.
-     *
-     * @throws IllegalArgumentException if the key was not recognized
-     * @throws NullPointerException if the key was null
-     *
-     * @hide
-     */
-    public static int getTag(String key) {
-        return nativeGetTagFromKey(key);
-    }
-
-    /**
-     * Get the underlying native type for a tag.
-     *
-     * @param tag An integer tag, see e.g. {@link #getTag}
-     * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
-     *
-     * @hide
-     */
-    public static int getNativeType(int tag) {
-        return nativeGetTypeFromTag(tag);
-    }
-
-    /**
-     * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
-     * the entry if src was null.</p>
-     *
-     * <p>An empty array can be passed in to update the entry to 0 elements.</p>
-     *
-     * @param tag An integer tag, see e.g. {@link #getTag}
-     * @param src An array of bytes, or null to erase the entry
-     *
-     * @hide
-     */
-    public void writeValues(int tag, byte[] src) {
-        nativeWriteValues(tag, src);
-    }
-
-    /**
-     * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
-     * the data properly.</p>
-     *
-     * <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
-     *
-     * @param tag An integer tag, see e.g. {@link #getTag}
-     *
-     * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
-     * @hide
-     */
-    public byte[] readValues(int tag) {
-     // TODO: Optimization. Native code returns a ByteBuffer instead.
-        return nativeReadValues(tag);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
-            new HashMap<Class<? extends Enum>, int[]>();
-    /**
-     * Register a non-sequential set of values to be used with the pack/unpack functions.
-     * This enables get/set to correctly marshal the enum into a value that is C-compatible.
-     *
-     * @param enumType The class for an enum
-     * @param values A list of values mapping to the ordinals of the enum
-     *
-     * @hide
-     */
-    public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
-        if (enumType.getEnumConstants().length != values.length) {
-            throw new IllegalArgumentException(
-                    "Expected values array to be the same size as the enumTypes values "
-                            + values.length + " for type " + enumType);
-        }
-
-        Log.v(TAG, "Registered enum values for type " + enumType + " values");
-
-        sEnumValues.put(enumType, values);
-    }
-
-    /**
-     * Get the numeric value from an enum. This is usually the same as the ordinal value for
-     * enums that have fully sequential values, although for C-style enums the range of values
-     * may not map 1:1.
-     *
-     * @param enumValue Enum instance
-     * @return Int guaranteed to be ABI-compatible with the C enum equivalent
-     */
-    private static <T extends Enum<T>> int getEnumValue(T enumValue) {
-        int[] values;
-        values = sEnumValues.get(enumValue.getClass());
-
-        int ordinal = enumValue.ordinal();
-        if (values != null) {
-            return values[ordinal];
-        }
-
-        return ordinal;
-    }
-
-    /**
-     * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
-     *
-     * @param enumType Class of the enum we want to find
-     * @param value The numeric value of the enum
-     * @return An instance of the enum
-     */
-    private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
-        int ordinal;
-
-        int[] registeredValues = sEnumValues.get(enumType);
-        if (registeredValues != null) {
-            ordinal = -1;
-
-            for (int i = 0; i < registeredValues.length; ++i) {
-                if (registeredValues[i] == value) {
-                    ordinal = i;
-                    break;
-                }
-            }
-        } else {
-            ordinal = value;
-        }
-
-        T[] values = enumType.getEnumConstants();
-
-        if (ordinal < 0 || ordinal >= values.length) {
-            throw new IllegalArgumentException(
-                    String.format(
-                            "Argument 'value' (%d) was not a valid enum value for type %s "
-                                    + "(registered? %b)",
-                            value,
-                            enumType, (registeredValues != null)));
-        }
-
-        return values[ordinal];
-    }
-
-    static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
-            HashMap<Class<?>, MetadataMarshalClass<?>>();
-
-    private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
-        sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
-        MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
-
-        if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
-            throw new UnsupportedOperationException("Unsupported type " + nativeType +
-                    " to be marshalled to/from a " + type);
-        }
-
-        return marshaler;
-    }
-
-    /**
-     * We use a class initializer to allow the native code to cache some field offsets
-     */
-    static {
-        System.loadLibrary("media_jni");
-        nativeClassInit();
-
-        Log.v(TAG, "Shall register metadata marshalers");
-
-        // load built-in marshallers
-        registerMarshaler(new MetadataMarshalRect());
-        registerMarshaler(new MetadataMarshalSize());
-        registerMarshaler(new MetadataMarshalString());
-
-        Log.v(TAG, "Registered metadata marshalers");
-    }
-
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * The enum values below this point are generated from metadata
      * definitions in /system/media/camera/docs. Do not modify by hand or
diff --git a/core/java/android/hardware/camera2/CameraProperties.java b/core/java/android/hardware/camera2/CameraProperties.java
index ebbdd88..45c009f 100644
--- a/core/java/android/hardware/camera2/CameraProperties.java
+++ b/core/java/android/hardware/camera2/CameraProperties.java
@@ -16,6 +16,8 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.impl.CameraMetadataNative;
+
 /**
  * <p>The properties describing a
  * {@link CameraDevice CameraDevice}.</p>
@@ -29,6 +31,21 @@
  */
 public final class CameraProperties extends CameraMetadata {
 
+    private final CameraMetadataNative mProperties;
+
+    /**
+     * Takes ownership of the passed-in properties object
+     * @hide
+     */
+    public CameraProperties(CameraMetadataNative properties) {
+        mProperties = properties;
+    }
+
+    @Override
+    public <T> T get(Key<T> key) {
+        return mProperties.get(key);
+    }
+
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * The key entries below this point are generated from metadata
      * definitions in /system/media/camera/docs. Do not modify by hand or
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 7735146..c3a636d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.impl.CameraMetadataNative;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.Surface;
@@ -24,13 +25,16 @@
 
 
 /**
- * <p>All the settings required to capture a single image from the image sensor.</p>
+ * <p>An immutable package of settings and outputs needed to capture a single
+ * image from the camera device.</p>
  *
  * <p>Contains the configuration for the capture hardware (sensor, lens, flash),
- * the processing pipeline, the control algorithms, and the output buffers.</p>
+ * the processing pipeline, the control algorithms, and the output buffers. Also
+ * contains the list of target Surfaces to send image data to for this
+ * capture.</p>
  *
- * <p>CaptureRequests can be created by calling
- * {@link CameraDevice#createCaptureRequest}</p>
+ * <p>CaptureRequests can be created by using a {@link Builder} instance,
+ * obtained by calling {@link CameraDevice#createCaptureRequest}</p>
  *
  * <p>CaptureRequests are given to {@link CameraDevice#capture} or
  * {@link CameraDevice#setRepeatingRequest} to capture images from a camera.</p>
@@ -38,7 +42,8 @@
  * <p>Each request can specify a different subset of target Surfaces for the
  * camera to send the captured data to. All the surfaces used in a request must
  * be part of the surface list given to the last call to
- * {@link CameraDevice#configureOutputs}.</p>
+ * {@link CameraDevice#configureOutputs}, when the request is submitted to the
+ * camera device.</p>
  *
  * <p>For example, a request meant for repeating preview might only include the
  * Surface for the preview SurfaceView or SurfaceTexture, while a
@@ -47,64 +52,43 @@
  *
  * @see CameraDevice#capture
  * @see CameraDevice#setRepeatingRequest
- * @see CameraDevice#createRequest
+ * @see CameraDevice#createCaptureRequest
  */
 public final class CaptureRequest extends CameraMetadata implements Parcelable {
 
-    private final Object mLock = new Object();
-    private final HashSet<Surface> mSurfaceSet = new HashSet<Surface>();
+    private final HashSet<Surface> mSurfaceSet;
+    private final CameraMetadataNative mSettings;
+
     private Object mUserTag;
 
     /**
+     * Construct empty request
      * @hide
      */
     public CaptureRequest() {
+        mSettings = new CameraMetadataNative();
+        mSurfaceSet = new HashSet<Surface>();
     }
 
     /**
-     * <p>Add a surface to the list of targets for this request</p>
-     *
-     * <p>The Surface added must be one of the surfaces included in the last
-     * call to {@link CameraDevice#configureOutputs}.</p>
-     *
-     * <p>Adding a target more than once has no effect.</p>
-     *
-     * @param outputTarget Surface to use as an output target for this request
+     * Clone from source capture request
      */
-    public void addTarget(Surface outputTarget) {
-        synchronized (mLock) {
-            mSurfaceSet.add(outputTarget);
-        }
+    private CaptureRequest(CaptureRequest source) {
+        mSettings = new CameraMetadataNative(source.mSettings);
+        mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
     }
 
     /**
-     * <p>Remove a surface from the list of targets for this request.</p>
-     *
-     * <p>Removing a target that is not currently added has no effect.</p>
-     *
-     * @param outputTarget Surface to use as an output target for this request
+     * Take ownership of passed-in settings
      */
-    public void removeTarget(Surface outputTarget) {
-        synchronized (mLock) {
-            mSurfaceSet.remove(outputTarget);
-        }
+    private CaptureRequest(CameraMetadataNative settings) {
+        mSettings = settings;
+        mSurfaceSet = new HashSet<Surface>();
     }
 
-    /**
-     * Set a tag for this request.
-     *
-     * <p>This tag is not used for anything by the camera device, but can be
-     * used by an application to easily identify a CaptureRequest when it is
-     * returned by
-     * {@link CameraDevice.CaptureListener#onCaptureComplete CaptureListener.onCaptureComplete}
-     *
-     * @param tag an arbitrary Object to store with this request
-     * @see #getTag
-     */
-    public void setTag(Object tag) {
-        synchronized (mLock) {
-            mUserTag = tag;
-        }
+    @Override
+    public <T> T get(Key<T> key) {
+        return mSettings.get(key);
     }
 
     /**
@@ -113,17 +97,15 @@
      * <p>This tag is not used for anything by the camera device, but can be
      * used by an application to easily identify a CaptureRequest when it is
      * returned by
-     * {@link CameraDevice.CaptureListener#onCaptureComplete CaptureListener.onCaptureComplete}
+     * {@link CameraDevice.CaptureListener#onCaptureCompleted CaptureListener.onCaptureCompleted}
      * </p>
      *
      * @return the last tag Object set on this request, or {@code null} if
      *     no tag has been set.
-     * @see #setTag
+     * @see Builder#setTag
      */
     public Object getTag() {
-        synchronized (mLock) {
-            return mUserTag;
-        }
+        return mUserTag;
     }
 
     public static final Parcelable.Creator<CaptureRequest> CREATOR =
@@ -132,6 +114,7 @@
         public CaptureRequest createFromParcel(Parcel in) {
             CaptureRequest request = new CaptureRequest();
             request.readFromParcel(in);
+
             return request;
         }
 
@@ -143,35 +126,152 @@
 
     /**
      * Expand this object from a Parcel.
+     * Hidden since this breaks the immutability of CaptureRequest, but is
+     * needed to receive CaptureRequests with aidl.
+     *
      * @param in The parcel from which the object should be read
+     * @hide
      */
-    @Override
     public void readFromParcel(Parcel in) {
-        synchronized (mLock) {
-            super.readFromParcel(in);
+        mSettings.readFromParcel(in);
 
-            mSurfaceSet.clear();
+        mSurfaceSet.clear();
 
-            Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader());
+        Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader());
 
-            if (parcelableArray == null) {
-                return;
-            }
+        if (parcelableArray == null) {
+            return;
+        }
 
-            for (Parcelable p : parcelableArray) {
-                Surface s = (Surface) p;
-                mSurfaceSet.add(s);
-            }
+        for (Parcelable p : parcelableArray) {
+            Surface s = (Surface) p;
+            mSurfaceSet.add(s);
         }
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        synchronized (mLock) {
-            super.writeToParcel(dest, flags);
+    public int describeContents() {
+        return 0;
+    }
 
-            dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags);
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mSettings.writeToParcel(dest, flags);
+        dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags);
+    }
+
+    /**
+     * A builder for capture requests.
+     *
+     * <p>To obtain a builder instance, use the
+     * {@link CameraDevice#createCaptureRequest} method, which initializes the
+     * request fields to one of the templates defined in {@link CameraDevice}.
+     *
+     * @see CameraDevice#createCaptureRequest
+     * @see #TEMPLATE_PREVIEW
+     * @see #TEMPLATE_RECORD
+     * @see #TEMPLATE_STILL_CAPTURE
+     * @see #TEMPLATE_VIDEO_SNAPSHOT
+     * @see #TEMPLATE_MANUAL
+     */
+    public final static class Builder {
+
+        private CaptureRequest mRequest;
+
+        /**
+         * Initialize the builder using the template; the request takes
+         * ownership of the template.
+         *
+         * @hide
+         */
+        public Builder(CameraMetadataNative template) {
+            mRequest = new CaptureRequest(template);
         }
+
+        /**
+         * <p>Add a surface to the list of targets for this request</p>
+         *
+         * <p>The Surface added must be one of the surfaces included in the most
+         * recent call to {@link CameraDevice#configureOutputs}, when the
+         * request is given to the camera device.</p>
+         *
+         * <p>Adding a target more than once has no effect.</p>
+         *
+         * @param outputTarget Surface to use as an output target for this request
+         */
+        public void addTarget(Surface outputTarget) {
+            mRequest.mSurfaceSet.add(outputTarget);
+        }
+
+        /**
+         * <p>Remove a surface from the list of targets for this request.</p>
+         *
+         * <p>Removing a target that is not currently added has no effect.</p>
+         *
+         * @param outputTarget Surface to use as an output target for this request
+         */
+        public void removeTarget(Surface outputTarget) {
+            mRequest.mSurfaceSet.remove(outputTarget);
+        }
+
+        /**
+         * Set a capture request field to a value. The field definitions can be
+         * found in {@link CaptureRequest}.
+         *
+         * @param key The metadata field to write.
+         * @param value The value to set the field to, which must be of a matching
+         * type to the key.
+         */
+        public <T> void set(Key<T> key, T value) {
+            mRequest.mSettings.set(key, value);
+        }
+
+        /**
+         * Get a capture request field value. The field definitions can be
+         * found in {@link CaptureRequest}.
+         *
+         * @throws IllegalArgumentException if the key was not valid
+         *
+         * @param key The metadata field to read.
+         * @return The value of that key, or {@code null} if the field is not set.
+         */
+        public <T> T get(Key<T> key) {
+            return mRequest.mSettings.get(key);
+        }
+
+        /**
+         * Set a tag for this request.
+         *
+         * <p>This tag is not used for anything by the camera device, but can be
+         * used by an application to easily identify a CaptureRequest when it is
+         * returned by
+         * {@link CameraDevice.CaptureListener#onCaptureCompleted CaptureListener.onCaptureCompleted}
+         *
+         * @param tag an arbitrary Object to store with this request
+         * @see CaptureRequest#getTag
+         */
+        public void setTag(Object tag) {
+            mRequest.mUserTag = tag;
+        }
+
+        /**
+         * Build a request using the current target Surfaces and settings.
+         *
+         * @return A new capture request instance, ready for submission to the
+         * camera device.
+         */
+        public CaptureRequest build() {
+            return new CaptureRequest(mRequest);
+        }
+
+
+        /**
+         * @hide
+         */
+        public boolean isEmpty() {
+            return mRequest.mSettings.isEmpty();
+        }
+
     }
 
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index bd151a2..f83dad7 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.camera2.impl.CameraMetadataNative;
 
 /**
  * <p>The results of a single image capture from the image sensor.</p>
@@ -34,10 +35,20 @@
  *
  */
 public final class CaptureResult extends CameraMetadata {
+
+    private final CameraMetadataNative mResults;
+
     /**
+     * Takes ownership of the passed-in properties object
      * @hide
      */
-    public CaptureResult() {
+    public CaptureResult(CameraMetadataNative results) {
+        mResults = results;
+    }
+
+    @Override
+    public <T> T get(Key<T> key) {
+        return mResults.get(key);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index 4172238..4054a92 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -16,7 +16,7 @@
 
 package android.hardware.camera2;
 
-import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.impl.CameraMetadataNative;
 
 /** @hide */
 interface ICameraDeviceCallbacks
@@ -26,5 +26,5 @@
      */
 
     oneway void notifyCallback(int msgType, int ext1, int ext2);
-    oneway void onResultReceived(int frameId, in CameraMetadata result);
+    oneway void onResultReceived(int frameId, in CameraMetadataNative result);
 }
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index b1724de..1936963 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -17,7 +17,7 @@
 package android.hardware.camera2;
 
 import android.view.Surface;
-import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.CaptureRequest;
 
 /** @hide */
@@ -40,9 +40,9 @@
     // non-negative value is the stream ID. negative value is status_t
     int createStream(int width, int height, int format, in Surface surface);
 
-    int createDefaultRequest(int templateId, out CameraMetadata request);
+    int createDefaultRequest(int templateId, out CameraMetadataNative request);
 
-    int getCameraInfo(out CameraMetadata info);
+    int getCameraInfo(out CameraMetadataNative info);
 
     int waitUntilIdle();
 
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index 86a073f..995555a 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -29,6 +29,8 @@
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Surface;
@@ -40,7 +42,7 @@
 import java.util.Stack;
 
 /**
- * HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate
+ * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
  */
 public class CameraDevice implements android.hardware.camera2.CameraDevice {
 
@@ -53,10 +55,11 @@
     private final Object mLock = new Object();
     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
 
-    // XX: Make this a WeakReference<CaptureListener> ?
-    // TODO: Convert to SparseIntArray
-    private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap =
-            new HashMap<Integer, CaptureListenerHolder>();
+    private CameraDeviceListener mDeviceListener;
+    private Handler mDeviceHandler;
+
+    private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
+            new SparseArray<CaptureListenerHolder>();
 
     private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
     // Map stream IDs to Surfaces
@@ -80,10 +83,14 @@
     }
 
     @Override
+    public String getId() {
+        return mCameraId;
+    }
+
+    @Override
     public CameraProperties getProperties() throws CameraAccessException {
 
-        CameraProperties properties = new CameraProperties();
-        CameraMetadata info = new CameraMetadata();
+        CameraMetadataNative info = new CameraMetadataNative();
 
         try {
             mRemoteDevice.getCameraInfo(/*out*/info);
@@ -94,7 +101,7 @@
             return null;
         }
 
-        properties.swap(info);
+        CameraProperties properties = new CameraProperties(info);
         return properties;
     }
 
@@ -149,11 +156,11 @@
     }
 
     @Override
-    public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException {
-
+    public CaptureRequest.Builder createCaptureRequest(int templateType)
+            throws CameraAccessException {
         synchronized (mLock) {
 
-            CameraMetadata templatedRequest = new CameraMetadata();
+            CameraMetadataNative templatedRequest = new CameraMetadataNative();
 
             try {
                 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
@@ -164,23 +171,22 @@
                 return null;
             }
 
-            CaptureRequest request = new CaptureRequest();
-            request.swap(templatedRequest);
+            CaptureRequest.Builder builder =
+                    new CaptureRequest.Builder(templatedRequest);
 
-            return request;
-
+            return builder;
         }
     }
 
     @Override
-    public void capture(CaptureRequest request, CaptureListener listener)
+    public void capture(CaptureRequest request, CaptureListener listener, Handler handler)
             throws CameraAccessException {
-        submitCaptureRequest(request, listener, /*streaming*/false);
+        submitCaptureRequest(request, listener, handler, /*streaming*/false);
     }
 
     @Override
-    public void captureBurst(List<CaptureRequest> requests, CaptureListener listener)
-            throws CameraAccessException {
+    public void captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException {
         if (requests.isEmpty()) {
             Log.w(TAG, "Capture burst request list is empty, do nothing!");
             return;
@@ -191,7 +197,18 @@
     }
 
     private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
-            boolean repeating) throws CameraAccessException {
+            Handler handler, boolean repeating) throws CameraAccessException {
+
+        // Need a valid handler, or current thread needs to have a looper, if
+        // listener is valid
+        if (handler == null && listener != null) {
+            Looper looper = Looper.myLooper();
+            if (looper == null) {
+                throw new IllegalArgumentException(
+                        "No handler given, and current thread has no looper!");
+            }
+            handler = new Handler(looper);
+        }
 
         synchronized (mLock) {
 
@@ -205,9 +222,10 @@
                 // impossible
                 return;
             }
-
-            mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
-                    repeating));
+            if (listener != null) {
+                mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
+                        handler, repeating));
+            }
 
             if (repeating) {
                 mRepeatingRequestIdStack.add(requestId);
@@ -217,14 +235,14 @@
     }
 
     @Override
-    public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
-            throws CameraAccessException {
-        submitCaptureRequest(request, listener, /*streaming*/true);
+    public void setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+            Handler handler) throws CameraAccessException {
+        submitCaptureRequest(request, listener, handler, /*streaming*/true);
     }
 
     @Override
-    public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
-            throws CameraAccessException {
+    public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException {
         if (requests.isEmpty()) {
             Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
             return;
@@ -274,9 +292,11 @@
     }
 
     @Override
-    public void setErrorListener(ErrorListener listener) {
-        // TODO Auto-generated method stub
-
+    public void setDeviceListener(CameraDeviceListener listener, Handler handler) {
+        synchronized (mLock) {
+            mDeviceListener = listener;
+            mDeviceHandler = handler;
+        }
     }
 
     @Override
@@ -332,9 +352,16 @@
         private final boolean mRepeating;
         private final CaptureListener mListener;
         private final CaptureRequest mRequest;
+        private final Handler mHandler;
 
-        CaptureListenerHolder(CaptureListener listener, CaptureRequest request, boolean repeating) {
+        CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
+                boolean repeating) {
+            if (listener == null || handler == null) {
+                throw new UnsupportedOperationException(
+                    "Must have a valid handler and a valid listener");
+            }
             mRepeating = repeating;
+            mHandler = handler;
             mRequest = request;
             mListener = listener;
         }
@@ -350,6 +377,11 @@
         public CaptureRequest getRequest() {
             return mRequest;
         }
+
+        public Handler getHandler() {
+            return mHandler;
+        }
+
     }
 
     // TODO: unit tests
@@ -370,11 +402,12 @@
         }
 
         @Override
-        public void onResultReceived(int requestId, CameraMetadata result) throws RemoteException {
+        public void onResultReceived(int requestId, CameraMetadataNative result)
+                throws RemoteException {
             if (DEBUG) {
                 Log.d(TAG, "Received result for id " + requestId);
             }
-            CaptureListenerHolder holder;
+            final CaptureListenerHolder holder;
 
             synchronized (mLock) {
                 // TODO: move this whole map into this class to make it more testable,
@@ -393,18 +426,22 @@
                 }
             }
 
+            // Check if we have a listener for this
             if (holder == null) {
-                Log.e(TAG, "Result had no listener holder associated with it, dropping result");
                 return;
             }
 
-            CaptureResult resultAsCapture = new CaptureResult();
-            resultAsCapture.swap(result);
+            final CaptureResult resultAsCapture = new CaptureResult(result);
 
-            if (holder.getListener() != null) {
-                holder.getListener().onCaptureComplete(CameraDevice.this, holder.getRequest(),
-                        resultAsCapture);
-            }
+            holder.getHandler().post(
+                new Runnable() {
+                    public void run() {
+                        holder.getListener().onCaptureCompleted(
+                            CameraDevice.this,
+                            holder.getRequest(),
+                            resultAsCapture);
+                    }
+                });
         }
 
     }
diff --git a/core/java/android/hardware/camera2/CameraMetadata.aidl b/core/java/android/hardware/camera2/impl/CameraMetadataNative.aidl
similarity index 89%
rename from core/java/android/hardware/camera2/CameraMetadata.aidl
rename to core/java/android/hardware/camera2/impl/CameraMetadataNative.aidl
index 71dd471..4a89129 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.aidl
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.hardware.camera2;
+package android.hardware.camera2.impl;
 
 /** @hide */
-parcelable CameraMetadata;
+parcelable CameraMetadataNative;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
new file mode 100644
index 0000000..020d7b6
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2013 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.hardware.camera2.impl;
+
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.Rational;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of camera metadata marshal/unmarshal across Binder to
+ * the camera service
+ */
+public class CameraMetadataNative extends CameraMetadata implements Parcelable {
+
+    private static final String TAG = "CameraMetadataJV";
+
+    public CameraMetadataNative() {
+        super();
+        mMetadataPtr = nativeAllocate();
+        if (mMetadataPtr == 0) {
+            throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
+        }
+    }
+
+    /**
+     * Copy constructor - clone metadata
+     */
+    public CameraMetadataNative(CameraMetadataNative other) {
+        super();
+        mMetadataPtr = nativeAllocateCopy(other);
+        if (mMetadataPtr == 0) {
+            throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
+        }
+    }
+
+    public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
+            new Parcelable.Creator<CameraMetadataNative>() {
+        @Override
+        public CameraMetadataNative createFromParcel(Parcel in) {
+            CameraMetadataNative metadata = new CameraMetadataNative();
+            metadata.readFromParcel(in);
+            return metadata;
+        }
+
+        @Override
+        public CameraMetadataNative[] newArray(int size) {
+            return new CameraMetadataNative[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        nativeWriteToParcel(dest);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T get(Key<T> key) {
+        int tag = key.getTag();
+        byte[] values = readValues(tag);
+        if (values == null) {
+            return null;
+        }
+
+        int nativeType = getNativeType(tag);
+
+        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
+        return unpackSingle(buffer, key.getType(), nativeType);
+    }
+
+    public void readFromParcel(Parcel in) {
+        nativeReadFromParcel(in);
+    }
+
+    /**
+     * Set a camera metadata field to a value. The field definitions can be
+     * found in {@link CameraProperties}, {@link CaptureResult}, and
+     * {@link CaptureRequest}.
+     *
+     * @param key The metadata field to write.
+     * @param value The value to set the field to, which must be of a matching
+     * type to the key.
+     */
+    public <T> void set(Key<T> key, T value) {
+        int tag = key.getTag();
+
+        if (value == null) {
+            writeValues(tag, null);
+            return;
+        }
+
+        int nativeType = getNativeType(tag);
+
+        int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true);
+
+        // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
+        byte[] values = new byte[size];
+
+        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
+        packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false);
+
+        writeValues(tag, values);
+    }
+
+    // Keep up-to-date with camera_metadata.h
+    /**
+     * @hide
+     */
+    public static final int TYPE_BYTE = 0;
+    /**
+     * @hide
+     */
+    public static final int TYPE_INT32 = 1;
+    /**
+     * @hide
+     */
+    public static final int TYPE_FLOAT = 2;
+    /**
+     * @hide
+     */
+    public static final int TYPE_INT64 = 3;
+    /**
+     * @hide
+     */
+    public static final int TYPE_DOUBLE = 4;
+    /**
+     * @hide
+     */
+    public static final int TYPE_RATIONAL = 5;
+    /**
+     * @hide
+     */
+    public static final int NUM_TYPES = 6;
+
+    private void close() {
+        // this sets mMetadataPtr to 0
+        nativeClose();
+        mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
+    }
+
+    private static int getTypeSize(int nativeType) {
+        switch(nativeType) {
+            case TYPE_BYTE:
+                return 1;
+            case TYPE_INT32:
+            case TYPE_FLOAT:
+                return 4;
+            case TYPE_INT64:
+            case TYPE_DOUBLE:
+            case TYPE_RATIONAL:
+                return 8;
+        }
+
+        throw new UnsupportedOperationException("Unknown type, can't get size "
+                + nativeType);
+    }
+
+    private static Class<?> getExpectedType(int nativeType) {
+        switch(nativeType) {
+            case TYPE_BYTE:
+                return Byte.TYPE;
+            case TYPE_INT32:
+                return Integer.TYPE;
+            case TYPE_FLOAT:
+                return Float.TYPE;
+            case TYPE_INT64:
+                return Long.TYPE;
+            case TYPE_DOUBLE:
+                return Double.TYPE;
+            case TYPE_RATIONAL:
+                return Rational.class;
+        }
+
+        throw new UnsupportedOperationException("Unknown type, can't map to Java type "
+                + nativeType);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type,
+            int nativeType, boolean sizeOnly) {
+
+        if (!sizeOnly) {
+            /**
+             * Rewrite types when the native type doesn't match the managed type
+             *  - Boolean -> Byte
+             *  - Integer -> Byte
+             */
+
+            if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
+                // Since a boolean can't be cast to byte, and we don't want to use putBoolean
+                boolean asBool = (Boolean) value;
+                byte asByte = (byte) (asBool ? 1 : 0);
+                value = (T) (Byte) asByte;
+            } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
+                int asInt = (Integer) value;
+                byte asByte = (byte) asInt;
+                value = (T) (Byte) asByte;
+            } else if (type != getExpectedType(nativeType)) {
+                throw new UnsupportedOperationException("Tried to pack a type of " + type +
+                        " but we expected the type to be " + getExpectedType(nativeType));
+            }
+
+            if (nativeType == TYPE_BYTE) {
+                buffer.put((Byte) value);
+            } else if (nativeType == TYPE_INT32) {
+                buffer.putInt((Integer) value);
+            } else if (nativeType == TYPE_FLOAT) {
+                buffer.putFloat((Float) value);
+            } else if (nativeType == TYPE_INT64) {
+                buffer.putLong((Long) value);
+            } else if (nativeType == TYPE_DOUBLE) {
+                buffer.putDouble((Double) value);
+            } else if (nativeType == TYPE_RATIONAL) {
+                Rational r = (Rational) value;
+                buffer.putInt(r.getNumerator());
+                buffer.putInt(r.getDenominator());
+            }
+
+        }
+
+        return getTypeSize(nativeType);
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType,
+            boolean sizeOnly) {
+
+        int size = 0;
+
+        if (type.isPrimitive() || type == Rational.class) {
+            size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
+        } else if (type.isEnum()) {
+            size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly);
+        } else if (type.isArray()) {
+            size = packArray(value, buffer, type, nativeType, sizeOnly);
+        } else {
+            size = packClass(value, buffer, type, nativeType, sizeOnly);
+        }
+
+        return size;
+    }
+
+    private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type,
+            int nativeType, boolean sizeOnly) {
+
+        // TODO: add support for enums with their own values.
+        return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
+            boolean sizeOnly) {
+
+        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+        if (marshaler == null) {
+            throw new IllegalArgumentException(String.format("Unknown Key type: %s", type));
+        }
+
+        return marshaler.marshal(value, buffer, nativeType, sizeOnly);
+    }
+
+    private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType,
+            boolean sizeOnly) {
+
+        int size = 0;
+        int arrayLength = Array.getLength(value);
+
+        @SuppressWarnings("unchecked")
+        Class<Object> componentType = (Class<Object>)type.getComponentType();
+
+        for (int i = 0; i < arrayLength; ++i) {
+            size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly);
+        }
+
+        return size;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        T val;
+
+        if (nativeType == TYPE_BYTE) {
+            val = (T) (Byte) buffer.get();
+        } else if (nativeType == TYPE_INT32) {
+            val = (T) (Integer) buffer.getInt();
+        } else if (nativeType == TYPE_FLOAT) {
+            val = (T) (Float) buffer.getFloat();
+        } else if (nativeType == TYPE_INT64) {
+            val = (T) (Long) buffer.getLong();
+        } else if (nativeType == TYPE_DOUBLE) {
+            val = (T) (Double) buffer.getDouble();
+        } else if (nativeType == TYPE_RATIONAL) {
+            val = (T) new Rational(buffer.getInt(), buffer.getInt());
+        } else {
+            throw new UnsupportedOperationException("Unknown type, can't unpack a native type "
+                + nativeType);
+        }
+
+        /**
+         * Rewrite types when the native type doesn't match the managed type
+         *  - Byte -> Boolean
+         *  - Byte -> Integer
+         */
+
+        if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
+            // Since a boolean can't be cast to byte, and we don't want to use getBoolean
+            byte asByte = (Byte) val;
+            boolean asBool = asByte != 0;
+            val = (T) (Boolean) asBool;
+        } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
+            byte asByte = (Byte) val;
+            int asInt = asByte;
+            val = (T) (Integer) asInt;
+        } else if (type != getExpectedType(nativeType)) {
+            throw new UnsupportedOperationException("Tried to unpack a type of " + type +
+                    " but we expected the type to be " + getExpectedType(nativeType));
+        }
+
+        return val;
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        if (type.isPrimitive() || type == Rational.class) {
+            return unpackSingleNative(buffer, type, nativeType);
+        }
+
+        if (type.isEnum()) {
+            return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType);
+        }
+
+        if (type.isArray()) {
+            return unpackArray(buffer, type, nativeType);
+        }
+
+        T instance = unpackClass(buffer, type, nativeType);
+
+        return instance;
+    }
+
+    private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type,
+            int nativeType) {
+        int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType);
+        return getEnumFromValue(type, ordinal);
+    }
+
+    private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+        if (marshaler == null) {
+            throw new IllegalArgumentException("Unknown class type: " + type);
+        }
+
+        return marshaler.unmarshal(buffer, nativeType);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        Class<?> componentType = type.getComponentType();
+        Object array;
+
+        int elementSize = getTypeSize(nativeType);
+
+        MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
+        if (marshaler != null) {
+            elementSize = marshaler.getNativeSize(nativeType);
+        }
+
+        if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
+            int remaining = buffer.remaining();
+            int arraySize = remaining / elementSize;
+
+            Log.v(TAG,
+                    String.format(
+                            "Attempting to unpack array (count = %d, element size = %d, bytes " +
+                                    "remaining = %d) for type %s",
+                            arraySize, elementSize, remaining, type));
+
+            array = Array.newInstance(componentType, arraySize);
+            for (int i = 0; i < arraySize; ++i) {
+               Object elem = unpackSingle(buffer, componentType, nativeType);
+               Array.set(array, i, elem);
+            }
+        } else {
+            // Dynamic size, use an array list.
+            ArrayList<Object> arrayList = new ArrayList<Object>();
+
+            int primitiveSize = getTypeSize(nativeType);
+            while (buffer.remaining() >= primitiveSize) {
+                Object elem = unpackSingle(buffer, componentType, nativeType);
+                arrayList.add(elem);
+            }
+
+            array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
+        }
+
+        if (buffer.remaining() != 0) {
+            Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+                    + type);
+        }
+
+        return (T) array;
+    }
+
+    private long mMetadataPtr; // native CameraMetadata*
+
+    private native long nativeAllocate();
+    private native long nativeAllocateCopy(CameraMetadataNative other)
+            throws NullPointerException;
+
+    private native synchronized void nativeWriteToParcel(Parcel dest);
+    private native synchronized void nativeReadFromParcel(Parcel source);
+    private native synchronized void nativeSwap(CameraMetadataNative other)
+            throws NullPointerException;
+    private native synchronized void nativeClose();
+    private native synchronized boolean nativeIsEmpty();
+    private native synchronized int nativeGetEntryCount();
+
+    private native synchronized byte[] nativeReadValues(int tag);
+    private native synchronized void nativeWriteValues(int tag, byte[] src);
+
+    private static native int nativeGetTagFromKey(String keyName)
+            throws IllegalArgumentException;
+    private static native int nativeGetTypeFromTag(int tag)
+            throws IllegalArgumentException;
+    private static native void nativeClassInit();
+
+    /**
+     * <p>Perform a 0-copy swap of the internal metadata with another object.</p>
+     *
+     * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
+     *
+     * @param other Metadata to swap with
+     * @throws NullPointerException if other was null
+     * @hide
+     */
+    public void swap(CameraMetadataNative other) {
+        nativeSwap(other);
+    }
+
+    /**
+     * @hide
+     */
+    public int getEntryCount() {
+        return nativeGetEntryCount();
+    }
+
+    /**
+     * Does this metadata contain at least 1 entry?
+     *
+     * @hide
+     */
+    public boolean isEmpty() {
+        return nativeIsEmpty();
+    }
+
+    /**
+     * Convert a key string into the equivalent native tag.
+     *
+     * @throws IllegalArgumentException if the key was not recognized
+     * @throws NullPointerException if the key was null
+     *
+     * @hide
+     */
+    public static int getTag(String key) {
+        return nativeGetTagFromKey(key);
+    }
+
+    /**
+     * Get the underlying native type for a tag.
+     *
+     * @param tag An integer tag, see e.g. {@link #getTag}
+     * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
+     *
+     * @hide
+     */
+    public static int getNativeType(int tag) {
+        return nativeGetTypeFromTag(tag);
+    }
+
+    /**
+     * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
+     * the entry if src was null.</p>
+     *
+     * <p>An empty array can be passed in to update the entry to 0 elements.</p>
+     *
+     * @param tag An integer tag, see e.g. {@link #getTag}
+     * @param src An array of bytes, or null to erase the entry
+     *
+     * @hide
+     */
+    public void writeValues(int tag, byte[] src) {
+        nativeWriteValues(tag, src);
+    }
+
+    /**
+     * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
+     * the data properly.</p>
+     *
+     * <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
+     *
+     * @param tag An integer tag, see e.g. {@link #getTag}
+     *
+     * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
+     * @hide
+     */
+    public byte[] readValues(int tag) {
+     // TODO: Optimization. Native code returns a ByteBuffer instead.
+        return nativeReadValues(tag);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
+            new HashMap<Class<? extends Enum>, int[]>();
+    /**
+     * Register a non-sequential set of values to be used with the pack/unpack functions.
+     * This enables get/set to correctly marshal the enum into a value that is C-compatible.
+     *
+     * @param enumType The class for an enum
+     * @param values A list of values mapping to the ordinals of the enum
+     *
+     * @hide
+     */
+    public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
+        if (enumType.getEnumConstants().length != values.length) {
+            throw new IllegalArgumentException(
+                    "Expected values array to be the same size as the enumTypes values "
+                            + values.length + " for type " + enumType);
+        }
+
+        Log.v(TAG, "Registered enum values for type " + enumType + " values");
+
+        sEnumValues.put(enumType, values);
+    }
+
+    /**
+     * Get the numeric value from an enum. This is usually the same as the ordinal value for
+     * enums that have fully sequential values, although for C-style enums the range of values
+     * may not map 1:1.
+     *
+     * @param enumValue Enum instance
+     * @return Int guaranteed to be ABI-compatible with the C enum equivalent
+     */
+    private static <T extends Enum<T>> int getEnumValue(T enumValue) {
+        int[] values;
+        values = sEnumValues.get(enumValue.getClass());
+
+        int ordinal = enumValue.ordinal();
+        if (values != null) {
+            return values[ordinal];
+        }
+
+        return ordinal;
+    }
+
+    /**
+     * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
+     *
+     * @param enumType Class of the enum we want to find
+     * @param value The numeric value of the enum
+     * @return An instance of the enum
+     */
+    private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
+        int ordinal;
+
+        int[] registeredValues = sEnumValues.get(enumType);
+        if (registeredValues != null) {
+            ordinal = -1;
+
+            for (int i = 0; i < registeredValues.length; ++i) {
+                if (registeredValues[i] == value) {
+                    ordinal = i;
+                    break;
+                }
+            }
+        } else {
+            ordinal = value;
+        }
+
+        T[] values = enumType.getEnumConstants();
+
+        if (ordinal < 0 || ordinal >= values.length) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "Argument 'value' (%d) was not a valid enum value for type %s "
+                                    + "(registered? %b)",
+                            value,
+                            enumType, (registeredValues != null)));
+        }
+
+        return values[ordinal];
+    }
+
+    static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
+            HashMap<Class<?>, MetadataMarshalClass<?>>();
+
+    private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
+        sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
+        MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
+
+        if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
+            throw new UnsupportedOperationException("Unsupported type " + nativeType +
+                    " to be marshalled to/from a " + type);
+        }
+
+        return marshaler;
+    }
+
+    /**
+     * We use a class initializer to allow the native code to cache some field offsets
+     */
+    static {
+        System.loadLibrary("media_jni");
+        nativeClassInit();
+
+        Log.v(TAG, "Shall register metadata marshalers");
+
+        // load built-in marshallers
+        registerMarshaler(new MetadataMarshalRect());
+        registerMarshaler(new MetadataMarshalSize());
+        registerMarshaler(new MetadataMarshalString());
+
+        Log.v(TAG, "Registered metadata marshalers");
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java b/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java
index a934d75..6d224ef 100644
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java
+++ b/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java
@@ -26,7 +26,7 @@
      * @param value the value of type T that we wish to write into the byte buffer
      * @param buffer the byte buffer into which the marshalled object will be written
      * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
      *        Guaranteed to be one for which isNativeTypeSupported returns true.
      * @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only.
      * @return the size that needs to be written to the byte buffer
@@ -37,7 +37,7 @@
      * Unmarshal a new object instance from the byte buffer.
      * @param buffer the byte buffer, from which we will read the object
      * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
      *        Guaranteed to be one for which isNativeTypeSupported returns true.
      * @return a new instance of type T read from the byte buffer
      */
@@ -50,7 +50,7 @@
      * will are likely to only support one type.
      *
      * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
      * @return true if it supports, false otherwise
      */
     boolean isNativeTypeSupported(int nativeType);
@@ -60,7 +60,7 @@
     /**
      * How many bytes T will take up if marshalled to/from nativeType
      * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
      * @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic
      */
     int getNativeSize(int nativeType);
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java b/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java
index 384223c..ab72c4f 100644
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java
+++ b/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java
@@ -16,7 +16,6 @@
 package android.hardware.camera2.impl;
 
 import android.graphics.Rect;
-import android.hardware.camera2.CameraMetadata;
 
 import java.nio.ByteBuffer;
 
@@ -58,7 +57,7 @@
 
     @Override
     public boolean isNativeTypeSupported(int nativeType) {
-        return nativeType == CameraMetadata.TYPE_INT32;
+        return nativeType == CameraMetadataNative.TYPE_INT32;
     }
 
     @Override
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java b/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java
index 793bba7..e8143e0 100644
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java
+++ b/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java
@@ -15,7 +15,6 @@
  */
 package android.hardware.camera2.impl;
 
-import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.Size;
 
 import java.nio.ByteBuffer;
@@ -51,7 +50,7 @@
 
     @Override
     public boolean isNativeTypeSupported(int nativeType) {
-        return nativeType == CameraMetadata.TYPE_INT32;
+        return nativeType == CameraMetadataNative.TYPE_INT32;
     }
 
     @Override
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java b/core/java/android/hardware/camera2/impl/MetadataMarshalString.java
index 50b3347..b61b8d3 100644
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java
+++ b/core/java/android/hardware/camera2/impl/MetadataMarshalString.java
@@ -15,8 +15,6 @@
  */
 package android.hardware.camera2.impl;
 
-import android.hardware.camera2.CameraMetadata;
-
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 
@@ -71,7 +69,7 @@
 
     @Override
     public boolean isNativeTypeSupported(int nativeType) {
-        return nativeType == CameraMetadata.TYPE_BYTE;
+        return nativeType == CameraMetadataNative.TYPE_BYTE;
     }
 
     @Override
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 0bebc04..a0d39ca 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -159,6 +159,7 @@
     private static final long TOUCH_STABILIZE_TIME = 128; // ms
     private static final int DOUBLE_TAP_MODE_NONE = 0;
     private static final int DOUBLE_TAP_MODE_IN_PROGRESS = 1;
+    private static final float SCALE_FACTOR = .5f;
 
 
     /**
@@ -197,7 +198,7 @@
      * @throws NullPointerException if {@code listener} is null.
      */
     public ScaleGestureDetector(Context context, OnScaleGestureListener listener,
-            Handler handler) {
+                                Handler handler) {
         mContext = context;
         mListener = listener;
         mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
@@ -409,7 +410,9 @@
             mPrevSpanY = mCurrSpanY = spanY;
             mInitialSpan = mPrevSpan = mCurrSpan = span;
         }
-        if (!mInProgress && span >= mMinSpan &&
+
+        final int minSpan = inDoubleTapMode() ? mSpanSlop : mMinSpan;
+        if (!mInProgress && span >=  minSpan &&
                 (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
             mPrevSpanX = mCurrSpanX = spanX;
             mPrevSpanY = mCurrSpanY = spanY;
@@ -464,7 +467,7 @@
                             mDoubleTapMode = DOUBLE_TAP_MODE_IN_PROGRESS;
                             return true;
                         }
-            };
+                    };
             mGestureDetector = new GestureDetector(mContext, gestureListener, mHandler);
         }
     }
@@ -572,11 +575,15 @@
      * @return The current scaling factor.
      */
     public float getScaleFactor() {
-        if (inDoubleTapMode() && mEventBeforeOrAboveStartingGestureEvent) {
+        if (inDoubleTapMode()) {
             // Drag is moving up; the further away from the gesture
             // start, the smaller the span should be, the closer,
             // the larger the span, and therefore the larger the scale
-            return (1 / mCurrSpan) / (1 / mPrevSpan);
+            final boolean scaleUp =
+                    (mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan < mPrevSpan)) ||
+                    (!mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan > mPrevSpan));
+            final float spanDiff = (Math.abs(1 - (mCurrSpan / mPrevSpan)) * SCALE_FACTOR);
+            return mPrevSpan <= 0 ? 1 : scaleUp ? (1 + spanDiff) : (1 - spanDiff);
         }
         return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
     }
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index f70f0d1..6e496fd 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -19,6 +19,7 @@
 #include <jni.h>
 #include <JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
 
 #include <utils/Log.h>
 #include <utils/String8.h>
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 852c4d4..3c7da1e 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -38,7 +38,7 @@
 #endif
 
 // fully-qualified class name
-#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/CameraMetadata"
+#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative"
 
 using namespace android;
 
@@ -152,6 +152,21 @@
     return reinterpret_cast<jlong>(new CameraMetadata());
 }
 
+static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz,
+        jobject other) {
+    ALOGV("%s", __FUNCTION__);
+
+    CameraMetadata* otherMetadata =
+            CameraMetadata_getPointerThrow(env, other, "other");
+
+    // In case of exception, return
+    if (otherMetadata == NULL) return NULL;
+
+    // Clone native metadata and return new pointer
+    return reinterpret_cast<jlong>(new CameraMetadata(*otherMetadata));
+}
+
+
 static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) {
     ALOGV("%s", __FUNCTION__);
 
@@ -361,6 +376,9 @@
   { "nativeAllocate",
     "()J",
     (void*)CameraMetadata_allocate },
+  { "nativeAllocateCopy",
+    "(L" CAMERA_METADATA_CLASS_NAME ";)J",
+    (void *)CameraMetadata_allocateCopy },
   { "nativeIsEmpty",
     "()Z",
     (void*)CameraMetadata_isEmpty },
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 0cd6f4a..1c43cc5 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -190,7 +190,7 @@
     int frameSize = nbChannels * bytesPerSample;
     size_t frameCount = buffSizeInBytes / frameSize;
 
-    if (uint32_t(source) >= AUDIO_SOURCE_CNT) {
+    if ((uint32_t(source) >= AUDIO_SOURCE_CNT) && (uint32_t(source) != AUDIO_SOURCE_HOTWORD)) {
         ALOGE("Error creating AudioRecord: unknown source.");
         return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
     }
@@ -387,6 +387,9 @@
                                             (jint)recorderBuffSize : sizeInBytes );
     env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
 
+    if (readSize < 0) {
+        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
+    }
     return (jint) readSize;
 }
 
@@ -427,8 +430,12 @@
     }
 
     // read new data from the recorder
-    return (jint) lpRecorder->read(nativeFromJavaBuf,
+    ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
                                    capacity < sizeInBytes ? capacity : sizeInBytes);
+    if (readSize < 0) {
+        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
+    }
+    return (jint)readSize;
 }
 
 
diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp
index 3fd8ed9..463be5e 100644
--- a/core/jni/android_media_RemoteDisplay.cpp
+++ b/core/jni/android_media_RemoteDisplay.cpp
@@ -24,6 +24,7 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
+#include <android_runtime/Log.h>
 
 #include <binder/IServiceManager.h>
 
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index dd07c4f..1779c9f 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -21,6 +21,7 @@
 #include "utils/misc.h"
 #include "cutils/debugger.h"
 
+#include <cutils/log.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 17a0677..c83541d 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -19,6 +19,7 @@
 #include "JNIHelp.h"
 
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
 #include <utils/Log.h>
 #include <input/Input.h>
 #include "android_view_KeyEvent.h"
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index ad6c0f2..f1b90e1 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -20,6 +20,7 @@
 
 #include <SkMatrix.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
 #include <utils/Log.h>
 #include <input/Input.h>
 #include "android_os_Parcel.h"
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index 8b6dc60..5e29213 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -21,6 +21,7 @@
 #include "android_view_PointerIcon.h"
 
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
 #include <utils/Log.h>
 #include <android/graphics/GraphicsJNI.h>
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3f54fd7..1868469 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -26,6 +26,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
+#include <android_runtime/Log.h>
 
 #include <binder/Parcel.h>
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d4ca5a0..a194240 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2084,6 +2084,14 @@
         android:description="@string/permdesc_captureAudioOutput"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows an application to capture audio for hotword detection.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
+        android:label="@string/permlab_captureAudioHotword"
+        android:description="@string/permdesc_captureAudioHotword"
+        android:protectionLevel="signature|system" />
+
     <!-- Allows an application to capture video output.
          <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
diff --git a/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png
deleted file mode 100644
index e591a7b..0000000
--- a/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png
deleted file mode 100644
index ea27290d..0000000
--- a/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png
deleted file mode 100644
index 7dfea4c..0000000
--- a/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png
deleted file mode 100644
index 99b0279..0000000
--- a/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png
deleted file mode 100644
index f01a79e..0000000
--- a/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png
deleted file mode 100644
index 7bea197..0000000
--- a/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-xxhdpi/activity_picker_bg_activated.9.png
deleted file mode 100644
index 47bc2b5..0000000
--- a/core/res/res/drawable-xxhdpi/activity_picker_bg_activated.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-xxhdpi/activity_picker_bg_focused.9.png
deleted file mode 100644
index 3bd381d..0000000
--- a/core/res/res/drawable-xxhdpi/activity_picker_bg_focused.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/activity_picker_bg.xml b/core/res/res/drawable/activity_picker_bg.xml
deleted file mode 100644
index a471c94..0000000
--- a/core/res/res/drawable/activity_picker_bg.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item android:state_window_focused="false" android:drawable="@color/transparent" />
-
-    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
-    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_light" />
-    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_light" />
-    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
-    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
-    <item android:state_focused="true"                                                             android:drawable="@drawable/activity_picker_bg_focused" />
-    <item android:state_activated="true"                                                             android:drawable="@drawable/activity_picker_bg_activated" />
-    <item android:drawable="@color/transparent" />
-
-</selector>
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 28c5b74..281541b 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -21,7 +21,7 @@
               android:orientation="horizontal"
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
-              android:background="@android:drawable/activity_picker_bg">
+              android:background="?attr/activatedBackgroundIndicator">
 
     <!-- Activity icon when presenting dialog
          Size will be filled in by ResolverActivity -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fa6ecc8..35d0fbd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1405,6 +1405,12 @@
     <string name="permdesc_captureAudioOutput">Allows the app to capture and redirect audio output.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_captureAudioHotword">Hotword detection</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_captureAudioHotword">Allows the app to capture audio for Hotword detection. The capture can
+      happen in the background but does not prevent other audio capture (e.g. Camcorder).</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_captureVideoOutput">capture video output</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_captureVideoOutput">Allows the app to capture and redirect video output.</string>
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index 7e0202e..b76c8be 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -79,6 +79,8 @@
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <!-- This permission is added for API call setAirplaneMode() in ConnectivityManager -->
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 463e999..a0cb1bb 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -651,19 +651,6 @@
         } catch (InterruptedException e) {}
     }
 
-    /**
-     * Set airplane mode
-     */
-    public void setAirplaneMode(Context context, boolean enableAM) {
-        //set the airplane mode
-        Settings.Global.putInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
-                enableAM ? 1 : 0);
-        // Post the intent
-        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", enableAM);
-        context.sendBroadcastAsUser(intent, UserHandle.ALL);
-    }
-
     protected static String convertToQuotedString(String string) {
         return "\"" + string + "\"";
     }
@@ -694,7 +681,7 @@
     //A thread to set the device into airplane mode then turn on wifi.
     Thread setDeviceWifiAndAirplaneThread = new Thread(new Runnable() {
         public void run() {
-            setAirplaneMode(mContext, true);
+            mCM.setAirplaneMode(true);
             connectToWifi(mPowerSsid);
         }
     });
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
index 3111489..729e1d2 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -62,7 +62,7 @@
         if (Settings.Global.getInt(getInstrumentation().getContext().getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON) == 1) {
             log("airplane is not disabled, disable it.");
-            cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
+            cmActivity.mCM.setAirplaneMode(false);
         }
 
         if (!mWifiOnlyFlag) {
@@ -87,7 +87,7 @@
         if (Settings.Global.getInt(getInstrumentation().getContext().getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON) == 1) {
             log("disable airplane mode if it is enabled");
-            cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
+            cmActivity.mCM.setAirplaneMode(false);
         }
         super.tearDown();
     }
@@ -344,7 +344,7 @@
 
         // Enable airplane mode
         log("Enable airplane mode");
-        cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
+        cmActivity.mCM.setAirplaneMode(true);
         sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
 
         networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
@@ -370,7 +370,7 @@
                 NetworkState.DO_NOTHING, State.DISCONNECTED);
 
         // disable airplane mode
-        cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
+        cmActivity.mCM.setAirplaneMode(false);
 
         assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
                 ConnectivityManagerTestActivity.LONG_TIMEOUT));
@@ -400,7 +400,7 @@
         assertNotNull("SSID is null", mTestAccessPoint);
         // Eanble airplane mode
         log("Enable airplane mode");
-        cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
+        cmActivity.mCM.setAirplaneMode(true);
 
         NetworkInfo networkInfo;
         if (!mWifiOnlyFlag) {
@@ -437,7 +437,7 @@
                 assertTrue("state validation failed", false);
             }
         }
-        cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
+        cmActivity.mCM.setAirplaneMode(false);
     }
 
     // Test case 7: test connectivity while transit from Wifi->AM->Wifi
@@ -463,7 +463,7 @@
         }
 
         // Enable airplane mode without clearing Wifi
-        cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
+        cmActivity.mCM.setAirplaneMode(true);
 
         assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
                 ConnectivityManagerTestActivity.LONG_TIMEOUT));
@@ -481,7 +481,7 @@
                 networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED);
 
         // Disable airplane mode
-        cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
+        cmActivity.mCM.setAirplaneMode(false);
 
         assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
                 ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT));
diff --git a/include/android_runtime/Log.h b/include/android_runtime/Log.h
new file mode 100644
index 0000000..aa6d202
--- /dev/null
+++ b/include/android_runtime/Log.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef _RUNTIME_ANDROID_LOG_H
+#define _RUNTIME_ANDROID_LOG_H
+
+// This relies on JNIHelp.h
+
+/* Logging macros.
+ *
+ * Logs an exception.  If the exception is omitted or NULL, logs the current exception
+ * from the JNI environment, if any.
+ */
+#define LOG_EX(env, priority, tag, ...) \
+    jniLogException(env, ANDROID_##priority, tag, ##__VA_ARGS__)
+#define LOGV_EX(env, ...) LOG_EX(env, LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+#define LOGD_EX(env, ...) LOG_EX(env, LOG_DEBUG, LOG_TAG, ##__VA_ARGS__)
+#define LOGI_EX(env, ...) LOG_EX(env, LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define LOGW_EX(env, ...) LOG_EX(env, LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define LOGE_EX(env, ...) LOG_EX(env, LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+
+#endif // _RUNTIME_ANDROID_LOG_H
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 9aceaa9..ec5f95c 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -645,7 +645,7 @@
 
         if (method == kCompressStored &&
             ((dataOffset >= cdOffset) ||
-             (uncompLen >= (cdOffset - dataOffset))))
+             (uncompLen > (cdOffset - dataOffset))))
         {
             ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n",
                 (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d1f7156..845baaf 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1532,16 +1532,6 @@
 
     /**
      * @hide
-     * Checks whether speech recognition is active
-     * @return true if a recording with source {@link MediaRecorder.AudioSource#VOICE_RECOGNITION}
-     *    is underway.
-     */
-    public boolean isSpeechRecognitionActive() {
-        return AudioSystem.isSourceActive(MediaRecorder.AudioSource.VOICE_RECOGNITION);
-    }
-
-    /**
-     * @hide
      * Checks whether the current audio focus is exclusive.
      * @return true if the top of the audio focus stack requested focus
      *     with {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index d20f5b9..f49ef2e 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -252,7 +252,8 @@
         //--------------
         // audio source
         if ( (audioSource < MediaRecorder.AudioSource.DEFAULT) ||
-             (audioSource > MediaRecorder.getAudioSourceMax()) )  {
+             ((audioSource > MediaRecorder.getAudioSourceMax()) &&
+              (audioSource != MediaRecorder.AudioSource.HOTWORD)) )  {
             throw new IllegalArgumentException("Invalid audio source.");
         }
         mRecordSource = audioSource;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 949a42c..fd4c81b 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -44,9 +44,19 @@
  *         for encoders, readable in the output format of decoders</b></td></tr>
  * <tr><td>{@link #KEY_FRAME_RATE}</td><td>Integer or Float</td><td><b>encoder-only</b></td></tr>
  * <tr><td>{@link #KEY_I_FRAME_INTERVAL}</td><td>Integer</td><td><b>encoder-only</b></td></tr>
+ * <tr><td>{@link #KEY_MAX_WIDTH}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution width</td></tr>
+ * <tr><td>{@link #KEY_MAX_HEIGHT}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution height</td></tr>
  * <tr><td>{@link #KEY_REPEAT_PREVIOUS_FRAME_AFTER}</td><td>Long</td><td><b>video encoder in surface-mode only</b></td></tr>
  * <tr><td>{@link #KEY_PUSH_BLANK_BUFFERS_ON_STOP}</td><td>Integer(1)</td><td><b>video decoder rendering to a surface only</b></td></tr>
  * </table>
+ * Specify both {@link #KEY_MAX_WIDTH} and {@link #KEY_MAX_HEIGHT} to enable
+ * adaptive playback (seamless resolution change) for a video decoder that
+ * supports it ({@link MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback}).
+ * The values are used as hints for the codec: they are the maximum expected
+ * resolution to prepare for.  Depending on codec support, preparing for larger
+ * maximum resolution may require more memory even if that resolution is never
+ * reached.  These fields have no effect for codecs that do not support adaptive
+ * playback.<br /><br />
  *
  * Audio formats have the following keys:
  * <table>
@@ -104,6 +114,20 @@
      */
     public static final String KEY_HEIGHT = "height";
 
+    /**
+     * A key describing the maximum expected width of the content in a video
+     * decoder format, in case there are resolution changes in the video content.
+     * The associated value is an integer
+     */
+    public static final String KEY_MAX_WIDTH = "max-width";
+
+    /**
+     * A key describing the maximum expected height of the content in a video
+     * decoder format, in case there are resolution changes in the video content.
+     * The associated value is an integer
+     */
+    public static final String KEY_MAX_HEIGHT = "max-height";
+
     /** A key describing the maximum size in bytes of a buffer of data
      * described by this MediaFormat.
      * The associated value is an integer
@@ -197,6 +221,28 @@
      */
     public static final String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
 
+    /**
+     * A key for boolean AUTOSELECT field. Tracks with AUTOSELECT=true are
+     * considered when automatically selecting a track without specific user
+     * choice (as defined by HLS).
+     * @hide
+     */
+    public static final String KEY_AUTOSELECT = "autoselect";
+
+    /**
+     * A key for boolean DEFAULT field. The track with DEFAULT=true is selected
+     * in the absence of a specific user choice (as defined by HLS).
+     * @hide
+     */
+    public static final String KEY_DEFAULT = "default";
+
+    /**
+     * A key for boolean FORCED field for subtitle tracks. True if it is a
+     * forced subtitle track.
+     * @hide
+     */
+    public static final String KEY_FORCED = "forced";
+
     /* package private */ MediaFormat(Map<String, Object> map) {
         mMap = map;
     }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ce1896a..bcdc1c8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1578,7 +1578,8 @@
          * unknown or could not be determined, null is returned.
          */
         public MediaFormat getFormat() {
-            if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
+            if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
+                    || mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
                 return mFormat;
             }
             return null;
@@ -1602,6 +1603,12 @@
             if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
                 mFormat = MediaFormat.createSubtitleFormat(
                     MEDIA_MIMETYPE_TEXT_SUBRIP, language);
+            } else if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
+                mFormat = MediaFormat.createSubtitleFormat(
+                    MEDIA_MIMETYPE_TEXT_VTT, language);
+                mFormat.setInteger(MediaFormat.KEY_AUTOSELECT, in.readInt());
+                mFormat.setInteger(MediaFormat.KEY_DEFAULT, in.readInt());
+                mFormat.setInteger(MediaFormat.KEY_FORCED, in.readInt());
             } else {
                 mFormat = new MediaFormat();
                 mFormat.setString(MediaFormat.KEY_LANGUAGE, language);
@@ -1629,6 +1636,12 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mTrackType);
             dest.writeString(getLanguage());
+
+            if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
+                dest.writeInt(mFormat.getInteger(MediaFormat.KEY_AUTOSELECT));
+                dest.writeInt(mFormat.getInteger(MediaFormat.KEY_DEFAULT));
+                dest.writeInt(mFormat.getInteger(MediaFormat.KEY_FORCED));
+            }
         }
 
         /**
@@ -1693,6 +1706,12 @@
      */
     public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
 
+    /**
+     * MIME type for WebVTT subtitle data.
+     * @hide
+     */
+    public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
+
     /*
      * A helper function to check if the mime type is supported by media framework.
      */
@@ -1829,9 +1848,8 @@
                 if (i < mInbandSubtitleTracks.length) {
                     inbandTracks[i] = mInbandSubtitleTracks[i];
                 } else {
-                    MediaFormat format = MediaFormat.createSubtitleFormat(
-                            "text/vtt", tracks[i].getLanguage());
-                    SubtitleTrack track = mSubtitleController.addTrack(format);
+                    SubtitleTrack track = mSubtitleController.addTrack(
+                            tracks[i].getFormat());
                     inbandTracks[i] = track;
                 }
             }
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 1d2b889..8dcbd6b 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -200,6 +200,19 @@
          * </p>
          */
         public static final int REMOTE_SUBMIX = 8;
+
+        /**
+         * Audio source for preemptible, low-priority software hotword detection
+         * It presents the same gain and pre processing tuning as {@link #VOICE_RECOGNITION}.
+         * <p>
+         * An application should use this audio source when it wishes to do
+         * always-on software hotword detection, while gracefully giving in to any other application
+         * that might want to read from the microphone.
+         * </p>
+         * This is a hidden audio source.
+         * @hide
+         */
+        protected static final int HOTWORD = 1999;
     }
 
     /**
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 666d111..bbb74d25b 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -21,6 +21,7 @@
 #include "android_media_MediaDrm.h"
 
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 #include "android_os_Parcel.h"
 #include "jni.h"
 #include "JNIHelp.h"
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 1704d5c..1ac45d4 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -22,6 +22,7 @@
 
 #include "android_media_Utils.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 #include "jni.h"
 #include "JNIHelp.h"
 
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 9b66c06..d134667 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -31,6 +31,7 @@
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
+#include "android_runtime/Log.h"
 #include "utils/Errors.h"  // for status_t
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 5d27966..4e3d14e 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -25,6 +25,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 
 using namespace android;
 
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index fbd5d21..77c7966 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -26,6 +26,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 
 #include "MtpDatabase.h"
 #include "MtpDataPacket.h"
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 113784e..b61b66c 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -28,6 +28,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 #include "private/android_filesystem_config.h"
 
 #include "MtpTypes.h"
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index 4982a47..d8099dd 100644
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "VideoEditorClasses"
 
 #include <VideoEditorClasses.h>
 #include <VideoEditorJava.h>
diff --git a/media/jni/mediaeditor/VideoEditorJava.cpp b/media/jni/mediaeditor/VideoEditorJava.cpp
index bcf9099..fde0fb5 100644
--- a/media/jni/mediaeditor/VideoEditorJava.cpp
+++ b/media/jni/mediaeditor/VideoEditorJava.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "VideoEditorJava"
+
 #include <VideoEditorClasses.h>
 #include <VideoEditorJava.h>
 #include <VideoEditorLogging.h>
diff --git a/media/jni/mediaeditor/VideoEditorLogging.h b/media/jni/mediaeditor/VideoEditorLogging.h
index 479d8b6..1f1228a 100644
--- a/media/jni/mediaeditor/VideoEditorLogging.h
+++ b/media/jni/mediaeditor/VideoEditorLogging.h
@@ -17,6 +17,16 @@
 #ifndef VIDEO_EDITOR_LOGGING_H
 #define VIDEO_EDITOR_LOGGING_H
 
+#ifndef LOG_TAG
+#error "No LOG_TAG defined!"
+#endif
+
+/*
+ * This file is used as a proxy for cutils/log.h.  Include cutils/log.h here to
+ * avoid relying on import ordering.
+ */
+#include <cutils/log.h>
+
 //#define VIDEOEDIT_LOGGING_ENABLED
 
 #define VIDEOEDIT_LOG_INDENTATION                       (3)
diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp
index a8c08ac..c12b1f5 100644
--- a/media/jni/mediaeditor/VideoEditorOsal.cpp
+++ b/media/jni/mediaeditor/VideoEditorOsal.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "VideoEditorOsal"
+
 #include <VideoEditorJava.h>
 #include <VideoEditorLogging.h>
 #include <VideoEditorOsal.h>
diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
index c8fb263..2f8e357 100644
--- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "VideoEditorPropertiesMain"
+
 #include <dlfcn.h>
 #include <stdio.h>
 #include <unistd.h>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 624bbaa..1b7faec 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -25,6 +25,7 @@
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.utils.BinderHolder;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.os.Binder;
@@ -155,7 +156,7 @@
         }
 
         @Override
-        public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException {
+        public void onResultReceived(int frameId, CameraMetadataNative result) throws RemoteException {
         }
     }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 2f271bb..56d73c0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -22,6 +22,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.utils.BinderHolder;
 import android.os.RemoteException;
 import android.test.AndroidTestCase;
@@ -62,13 +63,13 @@
         }
 
         @Override
-        public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException {
+        public void onResultReceived(int frameId, CameraMetadataNative result) throws RemoteException {
         }
     }
 
-    class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadata> {
+    class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> {
         public boolean matches(Object obj) {
-            return !((CameraMetadata) obj).isEmpty();
+            return !((CameraMetadataNative) obj).isEmpty();
         }
      }
 
@@ -78,20 +79,17 @@
         mSurface = new Surface(mSurfaceTexture);
     }
 
-    private CaptureRequest createDefaultRequest(boolean needStream) throws Exception {
-        CameraMetadata metadata = new CameraMetadata();
+    private CaptureRequest.Builder createDefaultBuilder(boolean needStream) throws Exception {
+        CameraMetadataNative metadata = new CameraMetadataNative();
         assertTrue(metadata.isEmpty());
 
-        CaptureRequest request = new CaptureRequest();
-        assertTrue(request.isEmpty());
-
         int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
         assertEquals(CameraBinderTestUtils.NO_ERROR, status);
         assertFalse(metadata.isEmpty());
 
-        request.swap(metadata);
+        CaptureRequest.Builder request = new CaptureRequest.Builder(metadata);
         assertFalse(request.isEmpty());
-        assertTrue(metadata.isEmpty());
+        assertFalse(metadata.isEmpty());
         if (needStream) {
             int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20,
                     /* ignored */30, mSurface);
@@ -150,14 +148,13 @@
 
     @SmallTest
     public void testCreateDefaultRequest() throws Exception {
-        CameraMetadata metadata = new CameraMetadata();
+        CameraMetadataNative metadata = new CameraMetadataNative();
         assertTrue(metadata.isEmpty());
 
         int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
         assertEquals(CameraBinderTestUtils.NO_ERROR, status);
         assertFalse(metadata.isEmpty());
 
-        metadata.close();
     }
 
     @SmallTest
@@ -208,37 +205,39 @@
     @SmallTest
     public void testSubmitBadRequest() throws Exception {
 
-        CaptureRequest request = createDefaultRequest(/* needStream */false);
-        int status = mCameraUser.submitRequest(request, /* streaming */false);
+        CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */false);
+        CaptureRequest request1 = builder.build();
+        int status = mCameraUser.submitRequest(request1, /* streaming */false);
         assertEquals("Expected submitRequest to return BAD_VALUE " +
                 "since we had 0 surface targets set.", CameraBinderTestUtils.BAD_VALUE, status);
 
-        request.addTarget(mSurface);
-        status = mCameraUser.submitRequest(request, /* streaming */false);
+        builder.addTarget(mSurface);
+        CaptureRequest request2 = builder.build();
+        status = mCameraUser.submitRequest(request2, /* streaming */false);
         assertEquals("Expected submitRequest to return BAD_VALUE since " +
                 "the target surface wasn't registered with createStream.",
                 CameraBinderTestUtils.BAD_VALUE, status);
-
-        request.close();
     }
 
     @SmallTest
     public void testSubmitGoodRequest() throws Exception {
 
-        CaptureRequest request = createDefaultRequest(/* needStream */true);
+        CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true);
+        CaptureRequest request = builder.build();
 
         // Submit valid request twice.
         int requestId1 = submitCameraRequest(request, /* streaming */false);
         int requestId2 = submitCameraRequest(request, /* streaming */false);
         assertNotSame("Request IDs should be unique for multiple requests", requestId1, requestId2);
 
-        request.close();
     }
 
     @SmallTest
     public void testSubmitStreamingRequest() throws Exception {
 
-        CaptureRequest request = createDefaultRequest(/* needStream */true);
+        CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true);
+
+        CaptureRequest request = builder.build();
 
         // Submit valid request once (non-streaming), and another time
         // (streaming)
@@ -260,12 +259,11 @@
         assertEquals("Streaming request IDs should be cancellable", CameraBinderTestUtils.NO_ERROR,
                 status);
 
-        request.close();
     }
 
     @SmallTest
     public void testCameraInfo() throws RemoteException {
-        CameraMetadata info = new CameraMetadata();
+        CameraMetadataNative info = new CameraMetadataNative();
 
         int status = mCameraUser.getCameraInfo(/*out*/info);
         assertEquals(CameraBinderTestUtils.NO_ERROR, status);
@@ -276,8 +274,8 @@
 
     @SmallTest
     public void testWaitUntilIdle() throws Exception {
-        CaptureRequest request = createDefaultRequest(/* needStream */true);
-        int requestIdStreaming = submitCameraRequest(request, /* streaming */true);
+        CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true);
+        int requestIdStreaming = submitCameraRequest(builder.build(), /* streaming */true);
 
         // Test Bad case first: waitUntilIdle when there is active repeating request
         int status = mCameraUser.waitUntilIdle();
@@ -294,7 +292,7 @@
     @SmallTest
     public void testCaptureResultCallbacks() throws Exception {
         IsMetadataNotEmpty matcher = new IsMetadataNotEmpty();
-        CaptureRequest request = createDefaultRequest(/* needStream */true);
+        CaptureRequest request = createDefaultBuilder(/* needStream */true).build();
 
         // Test both single request and streaming request.
         int requestId1 = submitCameraRequest(request, /* streaming */false);
@@ -307,7 +305,6 @@
                 .onResultReceived(
                         eq(streamingId),
                         argThat(matcher));
-        request.close();
     }
 
     @SmallTest
@@ -319,7 +316,7 @@
         assertEquals(CameraBinderTestUtils.NO_ERROR, status);
 
         // Then set up a stream
-        CaptureRequest request = createDefaultRequest(/* needStream */true);
+        CaptureRequest request = createDefaultBuilder(/* needStream */true).build();
 
         // Flush should still be a no-op, really
         status = mCameraUser.flush();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index ecf01d9..874e078 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -23,8 +23,9 @@
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.Rational;
 import android.hardware.camera2.Size;
+import android.hardware.camera2.impl.CameraMetadataNative;
 
-import static android.hardware.camera2.CameraMetadata.*;
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
 
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
@@ -42,7 +43,7 @@
  */
 public class CameraMetadataTest extends junit.framework.TestCase {
 
-    CameraMetadata mMetadata;
+    CameraMetadataNative mMetadata;
     Parcel mParcel;
 
     // Sections
@@ -62,13 +63,12 @@
 
     @Override
     public void setUp() {
-        mMetadata = new CameraMetadata();
+        mMetadata = new CameraMetadataNative();
         mParcel = Parcel.obtain();
     }
 
     @Override
     public void tearDown() throws Exception {
-        mMetadata.close();
         mMetadata = null;
 
         mParcel.recycle();
@@ -82,115 +82,47 @@
     }
 
     @SmallTest
-    public void testClose() throws Exception {
-        mMetadata.isEmpty(); // no throw
-
-        assertFalse(mMetadata.isClosed());
-
-        mMetadata.close();
-
-        assertTrue(mMetadata.isClosed());
-
-        // OK: second close should not throw
-        mMetadata.close();
-
-        assertTrue(mMetadata.isClosed());
-
-        // All other calls after close should throw IllegalStateException
-
-        try {
-            mMetadata.isEmpty();
-            fail("Unreachable -- isEmpty after close should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // good: we expect calling this method after close to fail
-        }
-
-        try {
-            mMetadata.getEntryCount();
-            fail("Unreachable -- getEntryCount after close should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // good: we expect calling this method after close to fail
-        }
-
-
-        try {
-            mMetadata.swap(mMetadata);
-            fail("Unreachable -- swap after close should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-         // good: we expect calling this method after close to fail
-        }
-
-        try {
-            mMetadata.readFromParcel(mParcel);
-            fail("Unreachable -- readFromParcel after close should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-         // good: we expect calling this method after close to fail
-        }
-
-        try {
-            mMetadata.writeToParcel(mParcel, /*flags*/0);
-            fail("Unreachable -- writeToParcel after close should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-         // good: we expect calling this method after close to fail
-        }
-
-        try {
-            mMetadata.readValues(/*tag*/0);
-            fail("Unreachable -- readValues after close should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-         // good: we expect calling this method after close to fail
-        }
-
-        try {
-            mMetadata.writeValues(/*tag*/0, /*source*/new byte[] { 1,2,3 });
-            fail("Unreachable -- readValues after close should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-         // good: we expect calling this method after close to fail
-        }
-    }
-
-    @SmallTest
     public void testGetTagFromKey() {
 
         // Test success
 
         assertEquals(ANDROID_COLOR_CORRECTION_MODE,
-                CameraMetadata.getTag("android.colorCorrection.mode"));
+                CameraMetadataNative.getTag("android.colorCorrection.mode"));
         assertEquals(ANDROID_COLOR_CORRECTION_TRANSFORM,
-                CameraMetadata.getTag("android.colorCorrection.transform"));
+                CameraMetadataNative.getTag("android.colorCorrection.transform"));
         assertEquals(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
-                CameraMetadata.getTag("android.control.aeAntibandingMode"));
+                CameraMetadataNative.getTag("android.control.aeAntibandingMode"));
         assertEquals(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
-                CameraMetadata.getTag("android.control.aeExposureCompensation"));
+                CameraMetadataNative.getTag("android.control.aeExposureCompensation"));
 
         // Test failures
 
         try {
-            CameraMetadata.getTag(null);
+            CameraMetadataNative.getTag(null);
             fail("A null key should throw NPE");
         } catch(NullPointerException e) {
         }
 
         try {
-            CameraMetadata.getTag("android.control");
+            CameraMetadataNative.getTag("android.control");
             fail("A section name only should not be a valid key");
         } catch(IllegalArgumentException e) {
         }
 
         try {
-            CameraMetadata.getTag("android.control.thisTagNameIsFakeAndDoesNotExist");
+            CameraMetadataNative.getTag("android.control.thisTagNameIsFakeAndDoesNotExist");
             fail("A valid section with an invalid tag name should not be a valid key");
         } catch(IllegalArgumentException e) {
         }
 
         try {
-            CameraMetadata.getTag("android");
+            CameraMetadataNative.getTag("android");
             fail("A namespace name only should not be a valid key");
         } catch(IllegalArgumentException e) {
         }
 
         try {
-            CameraMetadata.getTag("this.key.is.definitely.invalid");
+            CameraMetadataNative.getTag("this.key.is.definitely.invalid");
             fail("A completely fake key name should not be valid");
         } catch(IllegalArgumentException e) {
         }
@@ -198,14 +130,14 @@
 
     @SmallTest
     public void testGetTypeFromTag() {
-        assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
-        assertEquals(TYPE_FLOAT, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
-        assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
+        assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
+        assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
+        assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
         assertEquals(TYPE_INT32,
-                CameraMetadata.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
+                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
 
         try {
-            CameraMetadata.getNativeType(0xDEADF00D);
+            CameraMetadataNative.getNativeType(0xDEADF00D);
             fail("No type should exist for invalid tag 0xDEADF00D");
         } catch(IllegalArgumentException e) {
         }
@@ -454,7 +386,7 @@
 
     @SmallTest
     public void testReadWriteEnumWithCustomValues() {
-        CameraMetadata.registerEnumValues(AeAntibandingMode.class, new int[] {
+        CameraMetadataNative.registerEnumValues(AeAntibandingMode.class, new int[] {
             0,
             10,
             20,
@@ -475,7 +407,7 @@
         Key<AeAntibandingMode[]> aeAntibandingModeKey =
                 new Key<AeAntibandingMode[]>("android.control.aeAvailableAntibandingModes",
                         AeAntibandingMode[].class);
-        byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadata
+        byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative
                 .getTag("android.control.aeAvailableAntibandingModes"));
         byte[] expectedValues = new byte[] { 0, 10, 20, 30 };
         assertArrayEquals(expectedValues, aeAntibandingModeValues);
@@ -485,7 +417,7 @@
          * Stranger cases that don't use byte enums
          */
         // int (n)
-        CameraMetadata.registerEnumValues(AvailableFormat.class, new int[] {
+        CameraMetadataNative.registerEnumValues(AvailableFormat.class, new int[] {
             0x20,
             0x32315659,
             0x11,
@@ -505,7 +437,7 @@
         Key<AeAntibandingMode> availableFormatsKey =
                 new Key<AeAntibandingMode>("android.scaler.availableFormats",
                         AeAntibandingMode.class);
-        byte[] availableFormatValues = mMetadata.readValues(CameraMetadata
+        byte[] availableFormatValues = mMetadata.readValues(CameraMetadataNative
                 .getTag(availableFormatsKey.getName()));
 
         int[] expectedIntValues = new int[] {
diff --git a/services/jni/com_android_server_AssetAtlasService.cpp b/services/jni/com_android_server_AssetAtlasService.cpp
index 62e950f..885d21e 100644
--- a/services/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/jni/com_android_server_AssetAtlasService.cpp
@@ -20,6 +20,7 @@
 #include "JNIHelp.h"
 
 #include <android_view_GraphicBuffer.h>
+#include <cutils/log.h>
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
diff --git a/services/jni/com_android_server_UsbDeviceManager.cpp b/services/jni/com_android_server_UsbDeviceManager.cpp
index 0014db5..3551733 100644
--- a/services/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/jni/com_android_server_UsbDeviceManager.cpp
@@ -20,6 +20,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 
 #include <stdio.h>
 #include <asm/byteorder.h>
diff --git a/services/jni/com_android_server_UsbHostManager.cpp b/services/jni/com_android_server_UsbHostManager.cpp
index d0a6cdf..639790b7 100644
--- a/services/jni/com_android_server_UsbHostManager.cpp
+++ b/services/jni/com_android_server_UsbHostManager.cpp
@@ -20,6 +20,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 #include "utils/Vector.h"
 
 #include <usbhost/usbhost.h>
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index d8b8b94..4ab2086 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -29,6 +29,7 @@
 #include "jni.h"
 #include <limits.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
 
 #include <utils/Log.h>
 #include <utils/Looper.h>
diff --git a/services/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
index a187521..ac269eb 100644
--- a/services/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -23,6 +23,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 #include "hardware/fused_location.h"
 #include "hardware_legacy/power.h"
 
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 98de12a..aec254b 100644
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -26,6 +26,7 @@
 #include "utils/Log.h"
 #include "utils/misc.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
 
 #include <string.h>
 #include <pthread.h>
diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp
index 88b13b5..151e134 100644
--- a/services/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/jni/com_android_server_power_PowerManagerService.cpp
@@ -26,6 +26,7 @@
 #include <limits.h>
 
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
 #include <utils/Timers.h>
 #include <utils/misc.h>
 #include <utils/String8.h>