camera2: Refactor CameraMetadata.Key out into 3 key classes

Before:
* CameraMetadata.Key<T>

After:
* CameraCharacteristics.Key<T>
* CaptureResult.Key<T>
* CaptureRequest.Key<T>

CameraMetadata#get has been removed (each metadata subclass has
its own #get now) due to java generic limitations (in particular
a type bound <T1<T2> extends Key<T2>> is an illegal bound).

CameraMetadataNative gets a new #dumpToLog function to dump the native
metadata to logcat.

Bug: 15091017
Change-Id: Ic56c54c0d184e209e20de374dc8a6d79527c209f
diff --git a/api/current.txt b/api/current.txt
index 282f062..edf62b3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12166,70 +12166,76 @@
   }
 
   public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata {
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
-    method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureRequestKeys();
-    method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureResultKeys();
-    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_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;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_STEP;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_AVAILABLE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_EFFECTS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_SCENE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_AVAILABLE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MAX_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key EDGE_AVAILABLE_EDGE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_INFO_AVAILABLE;
-    field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key INFO_SUPPORTED_HARDWARE_LEVEL;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_AVAILABLE_THUMBNAIL_SIZES;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FACING;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_APERTURES;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_FILTER_DENSITIES;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_HYPERFOCAL_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_SHADING_MAP_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_AVAILABLE_CAPABILITIES;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_MAX_NUM_INPUT_STREAMS;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_MAX_NUM_OUTPUT_STREAMS;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PARTIAL_RESULT_COUNT;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PIPELINE_MAX_DEPTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROPPING_TYPE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_STREAM_CONFIGURATION_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_AVAILABLE_TEST_PATTERN_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BASE_GAIN_FACTOR;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BLACK_LEVEL_PATTERN;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_CALIBRATION_TRANSFORM1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_CALIBRATION_TRANSFORM2;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_COLOR_TRANSFORM1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_COLOR_TRANSFORM2;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FORWARD_MATRIX1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FORWARD_MATRIX2;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_ACTIVE_ARRAY_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_EXPOSURE_TIME_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_MAX_FRAME_DURATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_PHYSICAL_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_PIXEL_ARRAY_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_SENSITIVITY_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_WHITE_LEVEL;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_MAX_ANALOG_SENSITIVITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT2;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_MAX_FACE_COUNT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SYNC_MAX_LATENCY;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_AVAILABLE_TONE_MAP_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MAX_CURVE_POINTS;
+    method public T get(android.hardware.camera2.CameraCharacteristics.Key<T>);
+    method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys();
+    method public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys();
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_COMPENSATION_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_COMPENSATION_STEP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AF_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_EFFECTS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_SCENE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AWB_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key EDGE_AVAILABLE_EDGE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key FLASH_INFO_AVAILABLE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key INFO_SUPPORTED_HARDWARE_LEVEL;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key JPEG_AVAILABLE_THUMBNAIL_SIZES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_FACING;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_APERTURES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_FILTER_DENSITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_HYPERFOCAL_DISTANCE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_SHADING_MAP_SIZE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_AVAILABLE_CAPABILITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_INPUT_STREAMS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_STREAMS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PARTIAL_RESULT_COUNT;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PIPELINE_MAX_DEPTH;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_CROPPING_TYPE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_STREAM_CONFIGURATION_MAP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_AVAILABLE_TEST_PATTERN_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_BASE_GAIN_FACTOR;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_BLACK_LEVEL_PATTERN;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_CALIBRATION_TRANSFORM1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_CALIBRATION_TRANSFORM2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_COLOR_TRANSFORM1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_COLOR_TRANSFORM2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_FORWARD_MATRIX1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_FORWARD_MATRIX2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_ACTIVE_ARRAY_SIZE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_EXPOSURE_TIME_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_MAX_FRAME_DURATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PHYSICAL_SIZE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PIXEL_ARRAY_SIZE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_SENSITIVITY_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_WHITE_LEVEL;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_MAX_ANALOG_SENSITIVITY;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_ORIENTATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_REFERENCE_ILLUMINANT1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_REFERENCE_ILLUMINANT2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_MAX_FACE_COUNT;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SYNC_MAX_LATENCY;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key TONEMAP_AVAILABLE_TONE_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key TONEMAP_MAX_CURVE_POINTS;
+  }
+
+  public static final class CameraCharacteristics.Key {
+    method public final boolean equals(java.lang.Object);
+    method public java.lang.String getName();
+    method public final int hashCode();
   }
 
   public abstract interface CameraDevice implements java.lang.AutoCloseable {
@@ -12292,8 +12298,7 @@
   }
 
   public abstract class CameraMetadata {
-    method public abstract T get(android.hardware.camera2.CameraMetadata.Key<T>);
-    method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getKeys();
+    method public java.util.List<TKey> getKeys();
     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
@@ -12463,12 +12468,6 @@
     field public static final int TONEMAP_MODE_HIGH_QUALITY = 2; // 0x2
   }
 
-  public static class CameraMetadata.Key {
-    method public final boolean equals(java.lang.Object);
-    method public final java.lang.String getName();
-    method public final int hashCode();
-  }
-
   public class CaptureFailure {
     method public int getFrameNumber();
     method public int getReason();
@@ -12481,146 +12480,158 @@
 
   public final class CaptureRequest extends android.hardware.camera2.CameraMetadata implements android.os.Parcelable {
     method public int describeContents();
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+    method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
     method public java.lang.Object getTag();
     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;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_ANTIBANDING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_EXPOSURE_COMPENSATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_PRECAPTURE_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_TARGET_FPS_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_CAPTURE_INTENT;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_EFFECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_SCENE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_VIDEO_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key BLACK_LEVEL_LOCK;
+    field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_GAINS;
+    field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_TRANSFORM;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_ANTIBANDING_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_EXPOSURE_COMPENSATION;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_LOCK;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_PRECAPTURE_TRIGGER;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_REGIONS;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_TARGET_FPS_RANGE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_REGIONS;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_TRIGGER;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_LOCK;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_REGIONS;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_CAPTURE_INTENT;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_SCENE_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_VIDEO_STABILIZATION_MODE;
     field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final android.hardware.camera2.CameraMetadata.Key EDGE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_COORDINATES;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_PROCESSING_METHOD;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_TIMESTAMP;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_APERTURE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FILTER_DENSITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCAL_LENGTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_OPTICAL_STABILIZATION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROP_REGION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_EXPOSURE_TIME;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SHADING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_RED;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key EDGE_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key FLASH_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key HOT_PIXEL_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_COORDINATES;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_PROCESSING_METHOD;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_TIMESTAMP;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_ORIENTATION;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_QUALITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_THUMBNAIL_QUALITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_THUMBNAIL_SIZE;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_APERTURE;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_FILTER_DENSITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_FOCAL_LENGTH;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_OPTICAL_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key SCALER_CROP_REGION;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_EXPOSURE_TIME;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_FRAME_DURATION;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_SENSITIVITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_TEST_PATTERN_DATA;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_TEST_PATTERN_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key SHADING_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_FACE_DETECT_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_HOT_PIXEL_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_BLUE;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_GREEN;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_RED;
+    field public static final android.hardware.camera2.CaptureRequest.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 T get(android.hardware.camera2.CaptureRequest.Key<T>);
     method public void removeTarget(android.view.Surface);
-    method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T);
+    method public void set(android.hardware.camera2.CaptureRequest.Key<T>, T);
     method public void setTag(java.lang.Object);
   }
 
+  public static final class CaptureRequest.Key {
+    method public final boolean equals(java.lang.Object);
+    method public java.lang.String getName();
+    method public final int hashCode();
+  }
+
   public final class CaptureResult extends android.hardware.camera2.CameraMetadata {
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+    method public T get(android.hardware.camera2.CaptureResult.Key<T>);
     method public int getFrameNumber();
     method public android.hardware.camera2.CaptureRequest getRequest();
     method public int getSequenceId();
-    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;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_ANTIBANDING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_EXPOSURE_COMPENSATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_PRECAPTURE_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_TARGET_FPS_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_CAPTURE_INTENT;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_EFFECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_SCENE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_VIDEO_STABILIZATION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key EDGE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_COORDINATES;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_PROCESSING_METHOD;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_TIMESTAMP;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_APERTURE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FILTER_DENSITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCAL_LENGTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_OPTICAL_STABILIZATION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_FRAME_COUNT;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PIPELINE_DEPTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROP_REGION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_EXPOSURE_TIME;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_GREEN_SPLIT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_NEUTRAL_COLOR_POINT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TIMESTAMP;
-    field public static final android.hardware.camera2.CameraMetadata.Key SHADING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACES;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_SCENE_FLICKER;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_RED;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key BLACK_LEVEL_LOCK;
+    field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_GAINS;
+    field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_TRANSFORM;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_ANTIBANDING_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_EXPOSURE_COMPENSATION;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_LOCK;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_PRECAPTURE_TRIGGER;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_REGIONS;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_TARGET_FPS_RANGE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_REGIONS;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_TRIGGER;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_LOCK;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_REGIONS;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_CAPTURE_INTENT;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_SCENE_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_VIDEO_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key EDGE_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key FLASH_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key FLASH_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key HOT_PIXEL_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_COORDINATES;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_PROCESSING_METHOD;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_TIMESTAMP;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_ORIENTATION;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_QUALITY;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_THUMBNAIL_QUALITY;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_THUMBNAIL_SIZE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_APERTURE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FILTER_DENSITY;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCAL_LENGTH;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCUS_RANGE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_OPTICAL_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key REQUEST_FRAME_COUNT;
+    field public static final android.hardware.camera2.CaptureResult.Key REQUEST_PIPELINE_DEPTH;
+    field public static final android.hardware.camera2.CaptureResult.Key SCALER_CROP_REGION;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_EXPOSURE_TIME;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_FRAME_DURATION;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_GREEN_SPLIT;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_NEUTRAL_COLOR_POINT;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_SENSITIVITY;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEMPERATURE;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEST_PATTERN_DATA;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEST_PATTERN_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TIMESTAMP;
+    field public static final android.hardware.camera2.CaptureResult.Key SHADING_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_FACES;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_FACE_DETECT_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_SCENE_FLICKER;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_BLUE;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_GREEN;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_RED;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_MODE;
+  }
+
+  public static final class CaptureResult.Key {
+    method public final boolean equals(java.lang.Object);
+    method public java.lang.String getName();
+    method public final int hashCode();
   }
 
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 164e683..b9ff88e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -16,7 +16,9 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.CaptureResult.Key;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
 import android.util.Rational;
 
 import java.util.Collections;
@@ -35,18 +37,109 @@
  * @see CameraDevice
  * @see CameraManager
  */
-public final class CameraCharacteristics extends CameraMetadata {
+public final class CameraCharacteristics extends CameraMetadata<CameraCharacteristics.Key<?>> {
+
+    /**
+     * A {@code Key} is used to do camera characteristics field lookups with
+     * {@link CameraCharacteristics#get}.
+     *
+     * <p>For example, to get the stream configuration map:
+     * <code><pre>
+     * StreamConfigurationMap map = cameraCharacteristics.get(
+     *      CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+     * </pre></code>
+     * </p>
+     *
+     * <p>To enumerate over all possible keys for {@link CameraCharacteristics}, see
+     * {@link CameraCharacteristics#getKeys()}.</p>
+     *
+     * @see CameraCharacteristics#get
+     * @see CameraCharacteristics#getKeys()
+     */
+    public static final class Key<T> {
+        private final CameraMetadataNative.Key<T> mKey;
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, Class<T> type) {
+            mKey = new CameraMetadataNative.Key<T>(name,  type);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, TypeReference<T> typeReference) {
+            mKey = new CameraMetadataNative.Key<T>(name,  typeReference);
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public String getName() {
+            return mKey.getName();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mKey.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public final boolean equals(Object o) {
+            return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+        }
+
+        /**
+         * Visible for CameraMetadataNative implementation only; do not use.
+         *
+         * TODO: Make this private or remove it altogether.
+         *
+         * @hide
+         */
+        public CameraMetadataNative.Key<T> getNativeKey() {
+            return mKey;
+        }
+
+        @SuppressWarnings({
+                "unused", "unchecked"
+        })
+        private Key(CameraMetadataNative.Key<?> nativeKey) {
+            mKey = (CameraMetadataNative.Key<T>) nativeKey;
+        }
+    }
 
     private final CameraMetadataNative mProperties;
-    private List<Key<?>> mAvailableRequestKeys;
-    private List<Key<?>> mAvailableResultKeys;
+    private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
+    private List<CaptureResult.Key<?>> mAvailableResultKeys;
 
     /**
      * Takes ownership of the passed-in properties object
      * @hide
      */
     public CameraCharacteristics(CameraMetadataNative properties) {
-        mProperties = properties;
+        mProperties = CameraMetadataNative.move(properties);
     }
 
     /**
@@ -57,12 +150,55 @@
         return new CameraMetadataNative(mProperties);
     }
 
-    @Override
+    /**
+     * Get a camera characteristics field value.
+     *
+     * <p>The field definitions can be
+     * found in {@link CameraCharacteristics}.</p>
+     *
+     * <p>Querying the value for the same key more than once will return a value
+     * which is equal to the previous queried value.</p>
+     *
+     * @throws IllegalArgumentException if the key was not valid
+     *
+     * @param key The characteristics 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 mProperties.get(key);
     }
 
     /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T> T getProtected(Key<?> key) {
+        return (T) mProperties.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<Key<?>> getKeyClass() {
+        Object thisClass = Key.class;
+        return (Class<Key<?>>)thisClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Key<?>> getKeys() {
+        // Force the javadoc for this function to show up on the CameraCharacteristics page
+        return super.getKeys();
+    }
+
+    /**
      * Returns the list of keys supported by this {@link CameraDevice} for querying
      * with a {@link CaptureRequest}.
      *
@@ -76,9 +212,14 @@
      *
      * @return List of keys supported by this CameraDevice for CaptureRequests.
      */
-    public List<Key<?>> getAvailableCaptureRequestKeys() {
+    @SuppressWarnings({"unchecked"})
+    public List<CaptureRequest.Key<?>> getAvailableCaptureRequestKeys() {
         if (mAvailableRequestKeys == null) {
-            mAvailableRequestKeys = getAvailableKeyList(CaptureRequest.class);
+            Object crKey = CaptureRequest.Key.class;
+            Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+            mAvailableRequestKeys = Collections.unmodifiableList(
+                    getAvailableKeyList(CaptureRequest.class, crKeyTyped));
         }
         return mAvailableRequestKeys;
     }
@@ -97,9 +238,14 @@
      *
      * @return List of keys supported by this CameraDevice for CaptureResults.
      */
-    public List<Key<?>> getAvailableCaptureResultKeys() {
+    @SuppressWarnings({"unchecked"})
+    public List<CaptureResult.Key<?>> getAvailableCaptureResultKeys() {
         if (mAvailableResultKeys == null) {
-            mAvailableResultKeys = getAvailableKeyList(CaptureResult.class);
+            Object crKey = CaptureResult.Key.class;
+            Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
+
+            mAvailableResultKeys = Collections.unmodifiableList(
+                    getAvailableKeyList(CaptureResult.class, crKeyTyped));
         }
         return mAvailableResultKeys;
     }
@@ -113,12 +259,14 @@
      * <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
      *
      * @param metadataClass The subclass of CameraMetadata that you want to get the keys for.
+     * @param keyClass The class of the metadata key, e.g. CaptureRequest.Key.class
      *
      * @return List of keys supported by this CameraDevice for metadataClass.
      *
      * @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
      */
-    private <T extends CameraMetadata> List<Key<?>> getAvailableKeyList(Class<T> metadataClass) {
+    private <TKey> List<TKey>
+    getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass) {
 
         if (metadataClass.equals(CameraMetadata.class)) {
             throw new AssertionError(
@@ -128,7 +276,9 @@
                     "metadataClass must be a subclass of CameraMetadata");
         }
 
-        return Collections.unmodifiableList(getKeysStatic(metadataClass, /*instance*/null));
+        List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic(
+                metadataClass, keyClass, /*instance*/null);
+        return Collections.unmodifiableList(staticKeyList);
     }
 
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 5455189..5b8972f 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,8 +16,7 @@
 
 package android.hardware.camera2;
 
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -36,7 +35,7 @@
  *
  * <p>
  * All instances of CameraMetadata are immutable. The list of keys with {@link #getKeys()}
- * never changes, nor do the values returned by any key with {@link #get} throughout
+ * never changes, nor do the values returned by any key with {@code #get} throughout
  * the lifetime of the object.
  * </p>
  *
@@ -44,7 +43,10 @@
  * @see CameraManager
  * @see CameraCharacteristics
  **/
-public abstract class CameraMetadata {
+public abstract class CameraMetadata<TKey> {
+
+    private static final String TAG = "CameraMetadataAb";
+    private static final boolean VERBOSE = false;
 
     /**
      * Set a camera metadata field to a value. The field definitions can be
@@ -74,8 +76,15 @@
      *
      * @param key The metadata field to read.
      * @return The value of that key, or {@code null} if the field is not set.
+     *
+     * @hide
      */
-    public abstract <T> T get(Key<T> key);
+     protected abstract <T> T getProtected(TKey key);
+
+     /**
+      * @hide
+      */
+     protected abstract Class<TKey> getKeyClass();
 
     /**
      * Returns a list of the keys contained in this map.
@@ -83,14 +92,16 @@
      * <p>The list returned is not modifiable, so any attempts to modify it will throw
      * a {@code UnsupportedOperationException}.</p>
      *
-     * <p>All values retrieved by a key from this list with {@link #get} are guaranteed to be
+     * <p>All values retrieved by a key from this list with {@code #get} are guaranteed to be
      * non-{@code null}. Each key is only listed once in the list. The order of the keys
      * is undefined.</p>
      *
      * @return List of the keys contained in this map.
      */
-    public List<Key<?>> getKeys() {
-        return Collections.unmodifiableList(getKeysStatic(this.getClass(), this));
+    @SuppressWarnings("unchecked")
+    public List<TKey> getKeys() {
+        Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
+        return Collections.unmodifiableList(getKeysStatic(thisClass, getKeyClass(), this));
     }
 
     /**
@@ -101,24 +112,31 @@
      * Optionally, if {@code instance} is not null, then filter out any keys with null values.
      * </p>
      */
-    /*package*/ static ArrayList<Key<?>> getKeysStatic(Class<? extends CameraMetadata> type,
-            CameraMetadata instance) {
-        ArrayList<Key<?>> keyList = new ArrayList<Key<?>>();
+     /*package*/ @SuppressWarnings("unchecked")
+    static <TKey> ArrayList<TKey> getKeysStatic(
+             Class<?> type, Class<TKey> keyClass,
+             CameraMetadata<TKey> instance) {
+
+        if (VERBOSE) Log.v(TAG, "getKeysStatic for " + type);
+
+        ArrayList<TKey> keyList = new ArrayList<TKey>();
 
         Field[] fields = type.getDeclaredFields();
         for (Field field : fields) {
             // Filter for Keys that are public
-            if (field.getType().isAssignableFrom(Key.class) &&
+            if (field.getType().isAssignableFrom(keyClass) &&
                     (field.getModifiers() & Modifier.PUBLIC) != 0) {
-                Key<?> key;
+
+                TKey key;
                 try {
-                    key = (Key<?>) field.get(instance);
+                    key = (TKey) field.get(instance);
                 } catch (IllegalAccessException e) {
                     throw new AssertionError("Can't get IllegalAccessException", e);
                 } catch (IllegalArgumentException e) {
                     throw new AssertionError("Can't get IllegalArgumentException", e);
                 }
-                if (instance == null || instance.get(key) != null) {
+
+                if (instance == null || instance.getProtected(key) != null) {
                     keyList.add(key);
                 }
             }
@@ -127,113 +145,6 @@
         return keyList;
     }
 
-    // TODO: make final or abstract
-    public static class Key<T> {
-
-        private boolean mHasTag;
-        private int mTag;
-        private final Class<T> mType;
-        private final TypeReference<T> mTypeReference;
-        private final String mName;
-
-        /**
-         * @hide
-         */
-        public Key(String name, Class<T> type) {
-            if (name == null) {
-                throw new NullPointerException("Key needs a valid name");
-            } else if (type == null) {
-                throw new NullPointerException("Type needs to be non-null");
-            }
-            mName = name;
-            mType = type;
-            mTypeReference = TypeReference.createSpecializedTypeReference(type);
-        }
-
-        /**
-         * @hide
-         */
-        @SuppressWarnings("unchecked")
-        public Key(String name, TypeReference<T> typeReference) {
-            if (name == null) {
-                throw new NullPointerException("Key needs a valid name");
-            } else if (typeReference == null) {
-                throw new NullPointerException("TypeReference needs to be non-null");
-            }
-            mName = name;
-            mType = (Class<T>)typeReference.getRawType();
-            mTypeReference = typeReference;
-        }
-
-        public final String getName() {
-            return mName;
-        }
-
-        @Override
-        public final int hashCode() {
-            return mName.hashCode() ^ mTypeReference.hashCode();
-        }
-
-        @Override
-        public final boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-
-            if (!(o instanceof Key)) {
-                return false;
-            }
-
-            Key<?> lhs = (Key<?>)o;
-            return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
-        }
-
-        /**
-         * <p>
-         * Get the tag corresponding to this key. This enables insertion into the
-         * native metadata.
-         * </p>
-         *
-         * <p>This value is looked up the first time, and cached subsequently.</p>
-         *
-         * @return The tag numeric value corresponding to the string
-         *
-         * @hide
-         */
-        public final int getTag() {
-            if (!mHasTag) {
-                mTag = CameraMetadataNative.getTag(mName);
-                mHasTag = true;
-            }
-            return mTag;
-        }
-
-        /**
-         * Get the raw class backing the type {@code T} for this key.
-         *
-         * <p>The distinction is only important if {@code T} is a generic, e.g.
-         * {@code Range<Integer>} since the nested type will be erased.</p>
-         *
-         * @hide
-         */
-        public final Class<T> getType() {
-            // TODO: remove this; other places should use #getTypeReference() instead
-            return mType;
-        }
-
-        /**
-         * Get the type reference backing the type {@code T} for this key.
-         *
-         * <p>The distinction is only important if {@code T} is a generic, e.g.
-         * {@code Range<Integer>} since the nested type will be retained.</p>
-         *
-         * @hide
-         */
-        public final TypeReference<T> getTypeReference() {
-            return mTypeReference;
-        }
-    }
-
     /*@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/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 8e0f2ae..a4aa296 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -16,7 +16,9 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.CameraCharacteristics.Key;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Rational;
@@ -25,6 +27,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Objects;
 
 
@@ -58,7 +61,98 @@
  * @see CameraDevice#setRepeatingRequest
  * @see CameraDevice#createCaptureRequest
  */
-public final class CaptureRequest extends CameraMetadata implements Parcelable {
+public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
+        implements Parcelable {
+
+    /**
+     * A {@code Key} is used to do capture request field lookups with
+     * {@link CaptureResult#get} or to set fields with
+     * {@link CaptureRequest.Builder#set(Key, Object)}.
+     *
+     * <p>For example, to set the crop rectangle for the next capture:
+     * <code><pre>
+     * Rect cropRectangle = new Rect(0, 0, 640, 480);
+     * captureRequestBuilder.set(SCALER_CROP_REGION, cropRectangle);
+     * </pre></code>
+     * </p>
+     *
+     * <p>To enumerate over all possible keys for {@link CaptureResult}, see
+     * {@link CameraCharacteristics#getAvailableCaptureResultKeys}.</p>
+     *
+     * @see CaptureResult#get
+     * @see CameraCharacteristics#getAvailableCaptureResultKeys
+     */
+    public final static class Key<T> {
+        private final CameraMetadataNative.Key<T> mKey;
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, Class<T> type) {
+            mKey = new CameraMetadataNative.Key<T>(name, type);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, TypeReference<T> typeReference) {
+            mKey = new CameraMetadataNative.Key<T>(name, typeReference);
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public String getName() {
+            return mKey.getName();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mKey.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public final boolean equals(Object o) {
+            return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+        }
+
+        /**
+         * Visible for CameraMetadataNative implementation only; do not use.
+         *
+         * TODO: Make this private or remove it altogether.
+         *
+         * @hide
+         */
+        public CameraMetadataNative.Key<T> getNativeKey() {
+            return mKey;
+        }
+
+        @SuppressWarnings({ "unchecked" })
+        /*package*/ Key(CameraMetadataNative.Key<?> nativeKey) {
+            mKey = (CameraMetadataNative.Key<T>) nativeKey;
+        }
+    }
 
     private final HashSet<Surface> mSurfaceSet;
     private final CameraMetadataNative mSettings;
@@ -93,17 +187,58 @@
      * Used by the Builder to create a mutable CaptureRequest.
      */
     private CaptureRequest(CameraMetadataNative settings) {
-        mSettings = settings;
+        mSettings = CameraMetadataNative.move(settings);
         mSurfaceSet = new HashSet<Surface>();
     }
 
-    @SuppressWarnings("unchecked")
-    @Override
+    /**
+     * Get a capture request field value.
+     *
+     * <p>The field definitions can be found in {@link CaptureRequest}.</p>
+     *
+     * <p>Querying the value for the same key more than once will return a value
+     * which is equal to the previous queried value.</p>
+     *
+     * @throws IllegalArgumentException if the key was not valid
+     *
+     * @param key The result 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 mSettings.get(key);
     }
 
     /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T> T getProtected(Key<?> key) {
+        return (T) mSettings.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<Key<?>> getKeyClass() {
+        Object thisClass = Key.class;
+        return (Class<Key<?>>)thisClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Key<?>> getKeys() {
+        // Force the javadoc for this function to show up on the CaptureRequest page
+        return super.getKeys();
+    }
+
+    /**
      * Retrieve the tag for this request, if any.
      *
      * <p>This tag is not used for anything by the camera device, but can be
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index e0ddd57..38ac264 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -17,9 +17,12 @@
 package android.hardware.camera2;
 
 import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.params.Face;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
 import android.util.Rational;
 
+import java.util.List;
+
 /**
  * <p>The results of a single image capture from the image sensor.</p>
  *
@@ -36,7 +39,98 @@
  * <p>{@link CameraCharacteristics} objects are immutable.</p>
  *
  */
-public final class CaptureResult extends CameraMetadata {
+public final class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
+
+    private static final String TAG = "CaptureResult";
+    private static final boolean VERBOSE = false;
+
+    /**
+     * A {@code Key} is used to do capture result field lookups with
+     * {@link CaptureResult#get}.
+     *
+     * <p>For example, to get the timestamp corresponding to the exposure of the first row:
+     * <code><pre>
+     * long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP);
+     * </pre></code>
+     * </p>
+     *
+     * <p>To enumerate over all possible keys for {@link CaptureResult}, see
+     * {@link CameraCharacteristics#getAvailableCaptureResultKeys}.</p>
+     *
+     * @see CaptureResult#get
+     * @see CameraCharacteristics#getAvailableCaptureResultKeys
+     */
+    public final static class Key<T> {
+        private final CameraMetadataNative.Key<T> mKey;
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, Class<T> type) {
+            mKey = new CameraMetadataNative.Key<T>(name, type);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, TypeReference<T> typeReference) {
+            mKey = new CameraMetadataNative.Key<T>(name, typeReference);
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public String getName() {
+            return mKey.getName();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mKey.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public final boolean equals(Object o) {
+            return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+        }
+
+        /**
+         * Visible for CameraMetadataNative implementation only; do not use.
+         *
+         * TODO: Make this private or remove it altogether.
+         *
+         * @hide
+         */
+        public CameraMetadataNative.Key<T> getNativeKey() {
+            return mKey;
+        }
+
+        @SuppressWarnings({ "unchecked" })
+        /*package*/ Key(CameraMetadataNative.Key<?> nativeKey) {
+            mKey = (CameraMetadataNative.Key<T>) nativeKey;
+        }
+    }
 
     private final CameraMetadataNative mResults;
     private final CaptureRequest mRequest;
@@ -55,7 +149,10 @@
             throw new IllegalArgumentException("parent was null");
         }
 
-        mResults = results;
+        mResults = CameraMetadataNative.move(results);
+        if (mResults.isEmpty()) {
+            throw new AssertionError("Results must not be empty");
+        }
         mRequest = parent;
         mSequenceId = sequenceId;
     }
@@ -68,9 +165,85 @@
         return new CameraMetadataNative(mResults);
     }
 
-    @Override
+    /**
+     * Creates a request-less result.
+     *
+     * <p><strong>For testing only.</strong></p>
+     * @hide
+     */
+    public CaptureResult(CameraMetadataNative results, int sequenceId) {
+        if (results == null) {
+            throw new IllegalArgumentException("results was null");
+        }
+
+        mResults = CameraMetadataNative.move(results);
+        if (mResults.isEmpty()) {
+            throw new AssertionError("Results must not be empty");
+        }
+
+        mRequest = null;
+        mSequenceId = sequenceId;
+    }
+
+    /**
+     * Get a capture result field value.
+     *
+     * <p>The field definitions can be found in {@link CaptureResult}.</p>
+     *
+     * <p>Querying the value for the same key more than once will return a value
+     * which is equal to the previous queried value.</p>
+     *
+     * @throws IllegalArgumentException if the key was not valid
+     *
+     * @param key The result 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 mResults.get(key);
+        T value = mResults.get(key);
+        if (VERBOSE) Log.v(TAG, "#get for Key = " + key.getName() + ", returned value = " + value);
+        return value;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T> T getProtected(Key<?> key) {
+        return (T) mResults.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<Key<?>> getKeyClass() {
+        Object thisClass = Key.class;
+        return (Class<Key<?>>)thisClass;
+    }
+
+    /**
+     * Dumps the native metadata contents to logcat.
+     *
+     * <p>Visibility for testing/debugging only. The results will not
+     * include any synthesized keys, as they are invisible to the native layer.</p>
+     *
+     * @hide
+     */
+    public void dumpToLog() {
+        mResults.dumpToLog();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Key<?>> getKeys() {
+        // Force the javadoc for this function to show up on the CaptureResult page
+        return super.getKeys();
     }
 
     /**
@@ -110,6 +283,7 @@
      * @return int frame number
      */
     public int getFrameNumber() {
+        // TODO: @hide REQUEST_FRAME_COUNT
         return get(REQUEST_FRAME_COUNT);
     }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 27cfd38..ab2c49a 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -20,7 +20,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.marshal.Marshaler;
 import android.hardware.camera2.marshal.MarshalQueryable;
@@ -46,10 +46,14 @@
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.TypeReference;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
@@ -58,7 +62,147 @@
  * Implementation of camera metadata marshal/unmarshal across Binder to
  * the camera service
  */
-public class CameraMetadataNative extends CameraMetadata implements Parcelable {
+public class CameraMetadataNative implements Parcelable {
+
+    public static class Key<T> {
+        private boolean mHasTag;
+        private int mTag;
+        private final Class<T> mType;
+        private final TypeReference<T> mTypeReference;
+        private final String mName;
+
+        /**
+         * Visible for testing only.
+         *
+         * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
+         * for application code or vendor-extended keys.</p>
+         */
+        public Key(String name, Class<T> type) {
+            if (name == null) {
+                throw new NullPointerException("Key needs a valid name");
+            } else if (type == null) {
+                throw new NullPointerException("Type needs to be non-null");
+            }
+            mName = name;
+            mType = type;
+            mTypeReference = TypeReference.createSpecializedTypeReference(type);
+        }
+
+        /**
+         * Visible for testing only.
+         *
+         * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
+         * for application code or vendor-extended keys.</p>
+         */
+        @SuppressWarnings("unchecked")
+        public Key(String name, TypeReference<T> typeReference) {
+            if (name == null) {
+                throw new NullPointerException("Key needs a valid name");
+            } else if (typeReference == null) {
+                throw new NullPointerException("TypeReference needs to be non-null");
+            }
+            mName = name;
+            mType = (Class<T>)typeReference.getRawType();
+            mTypeReference = typeReference;
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public final String getName() {
+            return mName;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mName.hashCode() ^ mTypeReference.hashCode();
+        }
+
+        /**
+         * Compare this key against other native keys, request keys, result keys, and
+         * characteristics keys.
+         *
+         * <p>Two keys are considered equal if their name and type reference are equal.</p>
+         *
+         * <p>Note that the equality against non-native keys is one-way. A native key may be equal
+         * to a result key; but that same result key will not be equal to a native key.</p>
+         */
+        @SuppressWarnings("rawtypes")
+        @Override
+        public final boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            Key<?> lhs;
+
+            if (o instanceof CaptureResult.Key) {
+                lhs = ((CaptureResult.Key)o).getNativeKey();
+            } else if (o instanceof CaptureRequest.Key) {
+                lhs = ((CaptureRequest.Key)o).getNativeKey();
+            } else if (o instanceof CameraCharacteristics.Key) {
+                lhs = ((CameraCharacteristics.Key)o).getNativeKey();
+            } else if ((o instanceof Key)) {
+                lhs = (Key<?>)o;
+            } else {
+                return false;
+            }
+
+            return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
+        }
+
+        /**
+         * <p>
+         * Get the tag corresponding to this key. This enables insertion into the
+         * native metadata.
+         * </p>
+         *
+         * <p>This value is looked up the first time, and cached subsequently.</p>
+         *
+         * @return The tag numeric value corresponding to the string
+         */
+        public final int getTag() {
+            if (!mHasTag) {
+                mTag = CameraMetadataNative.getTag(mName);
+                mHasTag = true;
+            }
+            return mTag;
+        }
+
+        /**
+         * Get the raw class backing the type {@code T} for this key.
+         *
+         * <p>The distinction is only important if {@code T} is a generic, e.g.
+         * {@code Range<Integer>} since the nested type will be erased.</p>
+         */
+        public final Class<T> getType() {
+            // TODO: remove this; other places should use #getTypeReference() instead
+            return mType;
+        }
+
+        /**
+         * Get the type reference backing the type {@code T} for this key.
+         *
+         * <p>The distinction is only important if {@code T} is a generic, e.g.
+         * {@code Range<Integer>} since the nested type will be retained.</p>
+         */
+        public final TypeReference<T> getTypeReference() {
+            return mTypeReference;
+        }
+    }
 
     private static final String TAG = "CameraMetadataJV";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -84,6 +228,20 @@
         }
     }
 
+    /**
+     * Move the contents from {@code other} into a new camera metadata instance.</p>
+     *
+     * <p>After this call, {@code other} will become empty.</p>
+     *
+     * @param other the previous metadata instance which will get pilfered
+     * @return a new metadata instance with the values from {@code other} moved into it
+     */
+    public static CameraMetadataNative move(CameraMetadataNative other) {
+        CameraMetadataNative newObject = new CameraMetadataNative();
+        newObject.swap(other);
+        return newObject;
+    }
+
     public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
             new Parcelable.Creator<CameraMetadataNative>() {
         @Override
@@ -109,8 +267,36 @@
         nativeWriteToParcel(dest);
     }
 
-    @Override
+    /**
+     * @hide
+     */
+    public <T> T get(CameraCharacteristics.Key<T> key) {
+        return get(key.getNativeKey());
+    }
+
+    /**
+     * @hide
+     */
+    public <T> T get(CaptureResult.Key<T> key) {
+        return get(key.getNativeKey());
+    }
+
+    /**
+     * @hide
+     */
+    public <T> T get(CaptureRequest.Key<T> key) {
+        return get(key.getNativeKey());
+    }
+
+    /**
+     * Look-up a metadata field value by its key.
+     *
+     * @param key a non-{@code null} key instance
+     * @return the field corresponding to the {@code key}, or {@code null} if no value was set
+     */
     public <T> T get(Key<T> key) {
+        Preconditions.checkNotNull(key, "key must not be null");
+
         T value = getOverride(key);
         if (value != null) {
             return value;
@@ -152,6 +338,18 @@
         setBase(key, value);
     }
 
+    public <T> void set(CaptureRequest.Key<T> key, T value) {
+        set(key.getNativeKey(), value);
+    }
+
+    public <T> void set(CaptureResult.Key<T> key, T value) {
+        set(key.getNativeKey(), value);
+    }
+
+    public <T> void set(CameraCharacteristics.Key<T> key, T value) {
+        set(key.getNativeKey(), value);
+    }
+
     // Keep up-to-date with camera_metadata.h
     /**
      * @hide
@@ -188,6 +386,18 @@
         mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
     }
 
+    private <T> T getBase(CameraCharacteristics.Key<T> key) {
+        return getBase(key.getNativeKey());
+    }
+
+    private <T> T getBase(CaptureResult.Key<T> key) {
+        return getBase(key.getNativeKey());
+    }
+
+    private <T> T getBase(CaptureRequest.Key<T> key) {
+        return getBase(key.getNativeKey());
+    }
+
     private <T> T getBase(Key<T> key) {
         int tag = key.getTag();
         byte[] values = readValues(tag);
@@ -342,6 +552,18 @@
         return new StreamConfigurationMap(configurations, minFrameDurations, stallDurations);
     }
 
+    private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
+        setBase(key.getNativeKey(), value);
+    }
+
+    private <T> void setBase(CaptureResult.Key<T> key, T value) {
+        setBase(key.getNativeKey(), value);
+    }
+
+    private <T> void setBase(CaptureRequest.Key<T> key, T value) {
+        setBase(key.getNativeKey(), value);
+    }
+
     private <T> void setBase(Key<T> key, T value) {
         int tag = key.getTag();
 
@@ -440,6 +662,7 @@
 
     private native synchronized byte[] nativeReadValues(int tag);
     private native synchronized void nativeWriteValues(int tag, byte[] src);
+    private native synchronized void nativeDump() throws IOException; // dump to ALOGD
 
     private static native int nativeGetTagFromKey(String keyName)
             throws IllegalArgumentException;
@@ -531,6 +754,22 @@
         return nativeReadValues(tag);
     }
 
+    /**
+     * Dumps the native metadata contents to logcat.
+     *
+     * <p>Visibility for testing/debugging only. The results will not
+     * include any synthesized keys, as they are invisible to the native layer.</p>
+     *
+     * @hide
+     */
+    public void dumpToLog() {
+        try {
+            nativeDump();
+        } catch (IOException e) {
+            Log.wtf(TAG, "Dump logging failed", e);
+        }
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -599,5 +838,4 @@
         nativeClassInit();
         registerAllMarshalers();
     }
-
 }
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 0d2df80..7935329 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -39,6 +39,9 @@
 #include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 
+#include <sys/types.h> // for socketpair
+#include <sys/socket.h> // for socketpair
+
 #if defined(LOG_NNDEBUG)
 #if !LOG_NNDEBUG
 #define ALOGVV ALOGV
@@ -351,6 +354,119 @@
     }
 }
 
+struct DumpMetadataParams {
+    int writeFd;
+    const CameraMetadata* metadata;
+};
+
+static void* CameraMetadata_writeMetadataThread(void* arg) {
+    DumpMetadataParams* p = static_cast<DumpMetadataParams*>(arg);
+
+    /*
+     * Write the dumped data, and close the writing side FD.
+     */
+    p->metadata->dump(p->writeFd, /*verbosity*/2);
+
+    if (close(p->writeFd) < 0) {
+        ALOGE("%s: Failed to close writeFd (errno = %#x, message = '%s')",
+                __FUNCTION__, errno, strerror(errno));
+    }
+
+    return NULL;
+}
+
+static void CameraMetadata_dump(JNIEnv *env, jobject thiz) {
+    ALOGV("%s", __FUNCTION__);
+    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+    if (metadata == NULL) {
+        return;
+    }
+
+    /*
+     * Create a socket pair for local streaming read/writes.
+     *
+     * The metadata will be dumped into the write side,
+     * and then read back out (and logged) via the read side.
+     */
+
+    int writeFd, readFd;
+    {
+
+        int sv[2];
+        if (socketpair(AF_LOCAL, SOCK_STREAM, /*protocol*/0, &sv[0]) < 0) {
+            jniThrowExceptionFmt(env, "java/io/IOException",
+                    "Failed to create socketpair (errno = %#x, message = '%s')",
+                    errno, strerror(errno));
+            return;
+        }
+        writeFd = sv[0];
+        readFd = sv[1];
+    }
+
+    /*
+     * Create a thread for doing the writing.
+     *
+     * The reading and writing must be concurrent, otherwise
+     * the write will block forever once it exhausts the capped
+     * buffer size (from getsockopt).
+     */
+    pthread_t writeThread;
+    DumpMetadataParams params = {
+        writeFd,
+        metadata
+    };
+
+    {
+        int threadRet = pthread_create(&writeThread, /*attr*/NULL,
+                CameraMetadata_writeMetadataThread, (void*)&params);
+
+        if (threadRet != 0) {
+            close(writeFd);
+
+            jniThrowExceptionFmt(env, "java/io/IOException",
+                    "Failed to create thread for writing (errno = %#x, message = '%s')",
+                    threadRet, strerror(threadRet));
+        }
+    }
+
+    /*
+     * Read out a byte until stream is complete. Write completed lines
+     * to ALOG.
+     */
+    {
+        char out[] = {'\0', '\0'}; // large enough to append as a string
+        String8 logLine;
+
+        // Read one byte at a time! Very slow but avoids complicated \n scanning.
+        ssize_t res;
+        while ((res = TEMP_FAILURE_RETRY(read(readFd, &out[0], /*count*/1))) > 0) {
+            if (out[0] == '\n') {
+                ALOGD("%s", logLine.string());
+                logLine.clear();
+            } else {
+                logLine.append(out);
+            }
+        }
+
+        if (res < 0) {
+            jniThrowExceptionFmt(env, "java/io/IOException",
+                    "Failed to read from fd (errno = %#x, message = '%s')",
+                    errno, strerror(errno));
+            //return;
+        } else if (!logLine.isEmpty()) {
+            ALOGD("%s", logLine.string());
+        }
+    }
+
+    int res;
+
+    // Join until thread finishes. Ensures params/metadata is valid until then.
+    if ((res = pthread_join(writeThread, /*retval*/NULL)) != 0) {
+        ALOGE("%s: Failed to join thread (errno = %#x, message = '%s')",
+                __FUNCTION__, res, strerror(res));
+    }
+}
+
 static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
     ALOGV("%s", __FUNCTION__);
     CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
@@ -436,6 +552,9 @@
   { "nativeWriteValues",
     "(I[B)V",
     (void *)CameraMetadata_writeValues },
+  { "nativeDump",
+    "()V",
+    (void *)CameraMetadata_dump },
 // Parcelable interface
   { "nativeReadFromParcel",
     "(Landroid/os/Parcel;)V",
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 a77b647..a3caba2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -17,6 +17,7 @@
 package com.android.mediaframeworktest.unit;
 
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
 import android.util.Range;
 import android.util.Rational;
 import android.util.SizeF;
@@ -26,6 +27,8 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Size;
 import android.hardware.camera2.impl.CameraMetadataNative;
@@ -46,6 +49,7 @@
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.List;
 
 /**
  * <pre>
@@ -56,6 +60,10 @@
  */
 public class CameraMetadataTest extends junit.framework.TestCase {
 
+    private static final boolean VERBOSE = false;
+    private static final String TAG = "CameraMetadataTest";
+
+
     CameraMetadataNative mMetadata;
 
     // Sections
@@ -940,7 +948,7 @@
         };
         int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");
 
-        Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS;
+        Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey();
 
         validateArrayMetadataReadWriteOverride(formatKey, availableFormats,
                 expectedIntValues, availableFormatTag);
@@ -1046,7 +1054,7 @@
                 0x20, 320, 240, INPUT,   // RAW16
         };
         Key<StreamConfiguration[]> configKey =
-                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS.getNativeKey();
         mMetadata.writeValues(configKey.getTag(),
                 toByteArray(rawAvailableStreamConfigs));
 
@@ -1074,7 +1082,7 @@
                 0x21, 1920, 1080, 33333338  // BLOB
         };
         Key<StreamConfigurationDuration[]> durationKey =
-                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS.getNativeKey();
         mMetadata.writeValues(durationKey.getTag(),
                 toByteArray(rawAvailableMinDurations));
 
@@ -1100,7 +1108,7 @@
                 0x21, 1920, 1080, 33333338  // BLOB
         };
         Key<StreamConfigurationDuration[]> stallDurationKey =
-                CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS;
+                CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS.getNativeKey();
         mMetadata.writeValues(stallDurationKey.getTag(),
                 toByteArray(rawAvailableStallDurations));
 
@@ -1158,6 +1166,31 @@
         }
     }
 
+    @SmallTest
+    public void testCaptureResult() {
+        mMetadata.set(CaptureRequest.CONTROL_AE_MODE,
+                CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH);
+
+        if (VERBOSE) mMetadata.dumpToLog();
+
+        CaptureResult captureResult = new CaptureResult(mMetadata, /*sequenceId*/0);
+
+        List<CaptureResult.Key<?>> allKeys = captureResult.getKeys();
+        if (VERBOSE) Log.v(TAG, "testCaptureResult: key list size " + allKeys);
+        for (CaptureResult.Key<?> key : captureResult.getKeys()) {
+            if (VERBOSE) {
+                Log.v(TAG,
+                    "testCaptureResult: key " + key + " value" + captureResult.get(key));
+            }
+        }
+
+        assertTrue(allKeys.size() >= 1); // FIXME: android.statistics.faces counts as a key
+        assertTrue(allKeys.contains(CaptureResult.CONTROL_AE_MODE));
+
+        assertEquals(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH,
+                (int)captureResult.get(CaptureResult.CONTROL_AE_MODE));
+    }
+
     private static void checkStreamConfigurationMapByFormatSize(StreamConfigurationMap configMap,
             int format, int width, int height,
             boolean output) {