1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.impl;
18 
19 import android.graphics.ImageFormat;
20 import android.graphics.Point;
21 import android.graphics.Rect;
22 import android.hardware.camera2.CameraCharacteristics;
23 import android.hardware.camera2.CaptureRequest;
24 import android.hardware.camera2.CaptureResult;
25 import android.hardware.camera2.marshal.Marshaler;
26 import android.hardware.camera2.marshal.MarshalQueryable;
27 import android.hardware.camera2.marshal.MarshalRegistry;
28 import android.hardware.camera2.marshal.impl.MarshalQueryableArray;
29 import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
30 import android.hardware.camera2.marshal.impl.MarshalQueryableBlackLevelPattern;
31 import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform;
32 import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
33 import android.hardware.camera2.marshal.impl.MarshalQueryableHighSpeedVideoConfiguration;
34 import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle;
35 import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger;
36 import android.hardware.camera2.marshal.impl.MarshalQueryablePair;
37 import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
38 import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
39 import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
40 import android.hardware.camera2.marshal.impl.MarshalQueryableRect;
41 import android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap;
42 import android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector;
43 import android.hardware.camera2.marshal.impl.MarshalQueryableSize;
44 import android.hardware.camera2.marshal.impl.MarshalQueryableSizeF;
45 import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration;
46 import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
47 import android.hardware.camera2.marshal.impl.MarshalQueryableString;
48 import android.hardware.camera2.params.Face;
49 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
50 import android.hardware.camera2.params.LensShadingMap;
51 import android.hardware.camera2.params.ReprocessFormatsMap;
52 import android.hardware.camera2.params.StreamConfiguration;
53 import android.hardware.camera2.params.StreamConfigurationDuration;
54 import android.hardware.camera2.params.StreamConfigurationMap;
55 import android.hardware.camera2.params.TonemapCurve;
56 import android.hardware.camera2.utils.TypeReference;
57 import android.location.Location;
58 import android.location.LocationManager;
59 import android.os.Parcelable;
60 import android.os.Parcel;
61 import android.os.ServiceSpecificException;
62 import android.util.Log;
63 import android.util.Size;
64 
65 import com.android.internal.util.Preconditions;
66 
67 import java.io.IOException;
68 import java.nio.ByteBuffer;
69 import java.nio.ByteOrder;
70 import java.util.ArrayList;
71 import java.util.HashMap;
72 
73 /**
74  * Implementation of camera metadata marshal/unmarshal across Binder to
75  * the camera service
76  */
77 public class CameraMetadataNative implements Parcelable {
78 
79     public static class Key<T> {
80         private boolean mHasTag;
81         private int mTag;
82         private final Class<T> mType;
83         private final TypeReference<T> mTypeReference;
84         private final String mName;
85         private final int mHash;
86         /**
87          * Visible for testing only.
88          *
89          * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
90          * for application code or vendor-extended keys.</p>
91          */
Key(String name, Class<T> type)92         public Key(String name, Class<T> type) {
93             if (name == null) {
94                 throw new NullPointerException("Key needs a valid name");
95             } else if (type == null) {
96                 throw new NullPointerException("Type needs to be non-null");
97             }
98             mName = name;
99             mType = type;
100             mTypeReference = TypeReference.createSpecializedTypeReference(type);
101             mHash = mName.hashCode() ^ mTypeReference.hashCode();
102         }
103 
104         /**
105          * Visible for testing only.
106          *
107          * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
108          * for application code or vendor-extended keys.</p>
109          */
110         @SuppressWarnings("unchecked")
Key(String name, TypeReference<T> typeReference)111         public Key(String name, TypeReference<T> typeReference) {
112             if (name == null) {
113                 throw new NullPointerException("Key needs a valid name");
114             } else if (typeReference == null) {
115                 throw new NullPointerException("TypeReference needs to be non-null");
116             }
117             mName = name;
118             mType = (Class<T>)typeReference.getRawType();
119             mTypeReference = typeReference;
120             mHash = mName.hashCode() ^ mTypeReference.hashCode();
121         }
122 
123         /**
124          * Return a camelCase, period separated name formatted like:
125          * {@code "root.section[.subsections].name"}.
126          *
127          * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
128          * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
129          *
130          * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
131          * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
132          * specific key might look like {@code "com.google.nexus.data.private"}.</p>
133          *
134          * @return String representation of the key name
135          */
getName()136         public final String getName() {
137             return mName;
138         }
139 
140         /**
141          * {@inheritDoc}
142          */
143         @Override
hashCode()144         public final int hashCode() {
145             return mHash;
146         }
147 
148         /**
149          * Compare this key against other native keys, request keys, result keys, and
150          * characteristics keys.
151          *
152          * <p>Two keys are considered equal if their name and type reference are equal.</p>
153          *
154          * <p>Note that the equality against non-native keys is one-way. A native key may be equal
155          * to a result key; but that same result key will not be equal to a native key.</p>
156          */
157         @SuppressWarnings("rawtypes")
158         @Override
equals(Object o)159         public final boolean equals(Object o) {
160             if (this == o) {
161                 return true;
162             }
163 
164             if (o == null || this.hashCode() != o.hashCode()) {
165                 return false;
166             }
167 
168             Key<?> lhs;
169 
170             if (o instanceof CaptureResult.Key) {
171                 lhs = ((CaptureResult.Key)o).getNativeKey();
172             } else if (o instanceof CaptureRequest.Key) {
173                 lhs = ((CaptureRequest.Key)o).getNativeKey();
174             } else if (o instanceof CameraCharacteristics.Key) {
175                 lhs = ((CameraCharacteristics.Key)o).getNativeKey();
176             } else if ((o instanceof Key)) {
177                 lhs = (Key<?>)o;
178             } else {
179                 return false;
180             }
181 
182             return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
183         }
184 
185         /**
186          * <p>
187          * Get the tag corresponding to this key. This enables insertion into the
188          * native metadata.
189          * </p>
190          *
191          * <p>This value is looked up the first time, and cached subsequently.</p>
192          *
193          * @return The tag numeric value corresponding to the string
194          */
getTag()195         public final int getTag() {
196             if (!mHasTag) {
197                 mTag = CameraMetadataNative.getTag(mName);
198                 mHasTag = true;
199             }
200             return mTag;
201         }
202 
203         /**
204          * Get the raw class backing the type {@code T} for this key.
205          *
206          * <p>The distinction is only important if {@code T} is a generic, e.g.
207          * {@code Range<Integer>} since the nested type will be erased.</p>
208          */
getType()209         public final Class<T> getType() {
210             // TODO: remove this; other places should use #getTypeReference() instead
211             return mType;
212         }
213 
214         /**
215          * Get the type reference backing the type {@code T} for this key.
216          *
217          * <p>The distinction is only important if {@code T} is a generic, e.g.
218          * {@code Range<Integer>} since the nested type will be retained.</p>
219          */
getTypeReference()220         public final TypeReference<T> getTypeReference() {
221             return mTypeReference;
222         }
223     }
224 
225     private static final String TAG = "CameraMetadataJV";
226     private static final boolean DEBUG = false;
227 
228     // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h
229     public static final int NATIVE_JPEG_FORMAT = 0x21;
230 
231     private static final String CELLID_PROCESS = "CELLID";
232     private static final String GPS_PROCESS = "GPS";
233     private static final int FACE_LANDMARK_SIZE = 6;
234 
translateLocationProviderToProcess(final String provider)235     private static String translateLocationProviderToProcess(final String provider) {
236         if (provider == null) {
237             return null;
238         }
239         switch(provider) {
240             case LocationManager.GPS_PROVIDER:
241                 return GPS_PROCESS;
242             case LocationManager.NETWORK_PROVIDER:
243                 return CELLID_PROCESS;
244             default:
245                 return null;
246         }
247     }
248 
translateProcessToLocationProvider(final String process)249     private static String translateProcessToLocationProvider(final String process) {
250         if (process == null) {
251             return null;
252         }
253         switch(process) {
254             case GPS_PROCESS:
255                 return LocationManager.GPS_PROVIDER;
256             case CELLID_PROCESS:
257                 return LocationManager.NETWORK_PROVIDER;
258             default:
259                 return null;
260         }
261     }
262 
CameraMetadataNative()263     public CameraMetadataNative() {
264         super();
265         mMetadataPtr = nativeAllocate();
266         if (mMetadataPtr == 0) {
267             throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
268         }
269     }
270 
271     /**
272      * Copy constructor - clone metadata
273      */
CameraMetadataNative(CameraMetadataNative other)274     public CameraMetadataNative(CameraMetadataNative other) {
275         super();
276         mMetadataPtr = nativeAllocateCopy(other);
277         if (mMetadataPtr == 0) {
278             throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
279         }
280     }
281 
282     /**
283      * Move the contents from {@code other} into a new camera metadata instance.</p>
284      *
285      * <p>After this call, {@code other} will become empty.</p>
286      *
287      * @param other the previous metadata instance which will get pilfered
288      * @return a new metadata instance with the values from {@code other} moved into it
289      */
move(CameraMetadataNative other)290     public static CameraMetadataNative move(CameraMetadataNative other) {
291         CameraMetadataNative newObject = new CameraMetadataNative();
292         newObject.swap(other);
293         return newObject;
294     }
295 
296     public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
297             new Parcelable.Creator<CameraMetadataNative>() {
298         @Override
299         public CameraMetadataNative createFromParcel(Parcel in) {
300             CameraMetadataNative metadata = new CameraMetadataNative();
301             metadata.readFromParcel(in);
302             return metadata;
303         }
304 
305         @Override
306         public CameraMetadataNative[] newArray(int size) {
307             return new CameraMetadataNative[size];
308         }
309     };
310 
311     @Override
describeContents()312     public int describeContents() {
313         return 0;
314     }
315 
316     @Override
writeToParcel(Parcel dest, int flags)317     public void writeToParcel(Parcel dest, int flags) {
318         nativeWriteToParcel(dest);
319     }
320 
321     /**
322      * @hide
323      */
get(CameraCharacteristics.Key<T> key)324     public <T> T get(CameraCharacteristics.Key<T> key) {
325         return get(key.getNativeKey());
326     }
327 
328     /**
329      * @hide
330      */
get(CaptureResult.Key<T> key)331     public <T> T get(CaptureResult.Key<T> key) {
332         return get(key.getNativeKey());
333     }
334 
335     /**
336      * @hide
337      */
get(CaptureRequest.Key<T> key)338     public <T> T get(CaptureRequest.Key<T> key) {
339         return get(key.getNativeKey());
340     }
341 
342     /**
343      * Look-up a metadata field value by its key.
344      *
345      * @param key a non-{@code null} key instance
346      * @return the field corresponding to the {@code key}, or {@code null} if no value was set
347      */
get(Key<T> key)348     public <T> T get(Key<T> key) {
349         Preconditions.checkNotNull(key, "key must not be null");
350 
351         // Check if key has been overridden to use a wrapper class on the java side.
352         GetCommand g = sGetCommandMap.get(key);
353         if (g != null) {
354             return g.getValue(this, key);
355         }
356         return getBase(key);
357     }
358 
readFromParcel(Parcel in)359     public void readFromParcel(Parcel in) {
360         nativeReadFromParcel(in);
361     }
362 
363     /**
364      * Set the global client-side vendor tag descriptor to allow use of vendor
365      * tags in camera applications.
366      *
367      * @throws ServiceSpecificException
368      * @hide
369      */
setupGlobalVendorTagDescriptor()370     public static void setupGlobalVendorTagDescriptor() throws ServiceSpecificException {
371         int err = nativeSetupGlobalVendorTagDescriptor();
372         if (err != 0) {
373             throw new ServiceSpecificException(err, "Failure to set up global vendor tags");
374         }
375     }
376 
377     /**
378      * Set the global client-side vendor tag descriptor to allow use of vendor
379      * tags in camera applications.
380      *
381      * @return int An error code corresponding to one of the
382      * {@link ICameraService} error constants, or 0 on success.
383      */
nativeSetupGlobalVendorTagDescriptor()384     private static native int nativeSetupGlobalVendorTagDescriptor();
385 
386     /**
387      * Set a camera metadata field to a value. The field definitions can be
388      * found in {@link CameraCharacteristics}, {@link CaptureResult}, and
389      * {@link CaptureRequest}.
390      *
391      * @param key The metadata field to write.
392      * @param value The value to set the field to, which must be of a matching
393      * type to the key.
394      */
set(Key<T> key, T value)395     public <T> void set(Key<T> key, T value) {
396         SetCommand s = sSetCommandMap.get(key);
397         if (s != null) {
398             s.setValue(this, value);
399             return;
400         }
401 
402         setBase(key, value);
403     }
404 
set(CaptureRequest.Key<T> key, T value)405     public <T> void set(CaptureRequest.Key<T> key, T value) {
406         set(key.getNativeKey(), value);
407     }
408 
set(CaptureResult.Key<T> key, T value)409     public <T> void set(CaptureResult.Key<T> key, T value) {
410         set(key.getNativeKey(), value);
411     }
412 
set(CameraCharacteristics.Key<T> key, T value)413     public <T> void set(CameraCharacteristics.Key<T> key, T value) {
414         set(key.getNativeKey(), value);
415     }
416 
417     // Keep up-to-date with camera_metadata.h
418     /**
419      * @hide
420      */
421     public static final int TYPE_BYTE = 0;
422     /**
423      * @hide
424      */
425     public static final int TYPE_INT32 = 1;
426     /**
427      * @hide
428      */
429     public static final int TYPE_FLOAT = 2;
430     /**
431      * @hide
432      */
433     public static final int TYPE_INT64 = 3;
434     /**
435      * @hide
436      */
437     public static final int TYPE_DOUBLE = 4;
438     /**
439      * @hide
440      */
441     public static final int TYPE_RATIONAL = 5;
442     /**
443      * @hide
444      */
445     public static final int NUM_TYPES = 6;
446 
close()447     private void close() {
448         // this sets mMetadataPtr to 0
449         nativeClose();
450         mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
451     }
452 
getBase(CameraCharacteristics.Key<T> key)453     private <T> T getBase(CameraCharacteristics.Key<T> key) {
454         return getBase(key.getNativeKey());
455     }
456 
getBase(CaptureResult.Key<T> key)457     private <T> T getBase(CaptureResult.Key<T> key) {
458         return getBase(key.getNativeKey());
459     }
460 
getBase(CaptureRequest.Key<T> key)461     private <T> T getBase(CaptureRequest.Key<T> key) {
462         return getBase(key.getNativeKey());
463     }
464 
getBase(Key<T> key)465     private <T> T getBase(Key<T> key) {
466         int tag = key.getTag();
467         byte[] values = readValues(tag);
468         if (values == null) {
469             return null;
470         }
471 
472         Marshaler<T> marshaler = getMarshalerForKey(key);
473         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
474         return marshaler.unmarshal(buffer);
475     }
476 
477     // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
478     // metadata.
479     private static final HashMap<Key<?>, GetCommand> sGetCommandMap =
480             new HashMap<Key<?>, GetCommand>();
481     static {
482         sGetCommandMap.put(
GetCommand()483                 CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(), new GetCommand() {
484                     @Override
485                     @SuppressWarnings("unchecked")
486                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
487                         return (T) metadata.getAvailableFormats();
488                     }
489                 });
490         sGetCommandMap.put(
GetCommand()491                 CaptureResult.STATISTICS_FACES.getNativeKey(), new GetCommand() {
492                     @Override
493                     @SuppressWarnings("unchecked")
494                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
495                         return (T) metadata.getFaces();
496                     }
497                 });
498         sGetCommandMap.put(
GetCommand()499                 CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(), new GetCommand() {
500                     @Override
501                     @SuppressWarnings("unchecked")
502                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
503                         return (T) metadata.getFaceRectangles();
504                     }
505                 });
506         sGetCommandMap.put(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP.getNativeKey()507                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP.getNativeKey(),
508                         new GetCommand() {
509                     @Override
510                     @SuppressWarnings("unchecked")
511                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
512                         return (T) metadata.getStreamConfigurationMap();
513                     }
514                 });
515         sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey()516                 CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
517                     @Override
518                     @SuppressWarnings("unchecked")
519                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
520                         return (T) metadata.getMaxRegions(key);
521                     }
522                 });
523         sGetCommandMap.put(
GetCommand()524                 CameraCharacteristics.CONTROL_MAX_REGIONS_AWB.getNativeKey(), new GetCommand() {
525                     @Override
526                     @SuppressWarnings("unchecked")
527                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
528                         return (T) metadata.getMaxRegions(key);
529                     }
530                 });
531         sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AF.getNativeKey()532                 CameraCharacteristics.CONTROL_MAX_REGIONS_AF.getNativeKey(), new GetCommand() {
533                     @Override
534                     @SuppressWarnings("unchecked")
535                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
536                         return (T) metadata.getMaxRegions(key);
537                     }
538                 });
539         sGetCommandMap.put(
GetCommand()540                 CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW.getNativeKey(), new GetCommand() {
541                     @Override
542                     @SuppressWarnings("unchecked")
543                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
544                         return (T) metadata.getMaxNumOutputs(key);
545                     }
546                 });
547         sGetCommandMap.put(
GetCommand()548                 CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC.getNativeKey(), new GetCommand() {
549                     @Override
550                     @SuppressWarnings("unchecked")
551                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
552                         return (T) metadata.getMaxNumOutputs(key);
553                     }
554                 });
555         sGetCommandMap.put(
CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING.getNativeKey()556                 CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING.getNativeKey(),
557                         new GetCommand() {
558                     @Override
559                     @SuppressWarnings("unchecked")
560                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
561                         return (T) metadata.getMaxNumOutputs(key);
562                     }
563                 });
564         sGetCommandMap.put(
GetCommand()565                 CaptureRequest.TONEMAP_CURVE.getNativeKey(), new GetCommand() {
566                     @Override
567                     @SuppressWarnings("unchecked")
568                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
569                         return (T) metadata.getTonemapCurve();
570                     }
571                 });
572         sGetCommandMap.put(
GetCommand()573                 CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new GetCommand() {
574                     @Override
575                     @SuppressWarnings("unchecked")
576                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
577                         return (T) metadata.getGpsLocation();
578                     }
579                 });
580         sGetCommandMap.put(
CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey()581                 CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
582                         new GetCommand() {
583                     @Override
584                     @SuppressWarnings("unchecked")
585                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
586                         return (T) metadata.getLensShadingMap();
587                     }
588                 });
589     }
590 
getAvailableFormats()591     private int[] getAvailableFormats() {
592         int[] availableFormats = getBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
593         if (availableFormats != null) {
594             for (int i = 0; i < availableFormats.length; i++) {
595                 // JPEG has different value between native and managed side, need override.
596                 if (availableFormats[i] == NATIVE_JPEG_FORMAT) {
597                     availableFormats[i] = ImageFormat.JPEG;
598                 }
599             }
600         }
601 
602         return availableFormats;
603     }
604 
setFaces(Face[] faces)605     private boolean setFaces(Face[] faces) {
606         if (faces == null) {
607             return false;
608         }
609 
610         int numFaces = faces.length;
611 
612         // Detect if all faces are SIMPLE or not; count # of valid faces
613         boolean fullMode = true;
614         for (Face face : faces) {
615             if (face == null) {
616                 numFaces--;
617                 Log.w(TAG, "setFaces - null face detected, skipping");
618                 continue;
619             }
620 
621             if (face.getId() == Face.ID_UNSUPPORTED) {
622                 fullMode = false;
623             }
624         }
625 
626         Rect[] faceRectangles = new Rect[numFaces];
627         byte[] faceScores = new byte[numFaces];
628         int[] faceIds = null;
629         int[] faceLandmarks = null;
630 
631         if (fullMode) {
632             faceIds = new int[numFaces];
633             faceLandmarks = new int[numFaces * FACE_LANDMARK_SIZE];
634         }
635 
636         int i = 0;
637         for (Face face : faces) {
638             if (face == null) {
639                 continue;
640             }
641 
642             faceRectangles[i] = face.getBounds();
643             faceScores[i] = (byte)face.getScore();
644 
645             if (fullMode) {
646                 faceIds[i] = face.getId();
647 
648                 int j = 0;
649 
650                 faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().x;
651                 faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().y;
652                 faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().x;
653                 faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().y;
654                 faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().x;
655                 faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().y;
656             }
657 
658             i++;
659         }
660 
661         set(CaptureResult.STATISTICS_FACE_RECTANGLES, faceRectangles);
662         set(CaptureResult.STATISTICS_FACE_IDS, faceIds);
663         set(CaptureResult.STATISTICS_FACE_LANDMARKS, faceLandmarks);
664         set(CaptureResult.STATISTICS_FACE_SCORES, faceScores);
665 
666         return true;
667     }
668 
getFaces()669     private Face[] getFaces() {
670         Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
671         byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
672         Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
673         int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
674         int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
675 
676         if (areValuesAllNull(faceDetectMode, faceScores, faceRectangles, faceIds, faceLandmarks)) {
677             return null;
678         }
679 
680         if (faceDetectMode == null) {
681             Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
682             faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
683         } else {
684             if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
685                 return new Face[0];
686             }
687             if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE &&
688                     faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
689                 Log.w(TAG, "Unknown face detect mode: " + faceDetectMode);
690                 return new Face[0];
691             }
692         }
693 
694         // Face scores and rectangles are required by SIMPLE and FULL mode.
695         if (faceScores == null || faceRectangles == null) {
696             Log.w(TAG, "Expect face scores and rectangles to be non-null");
697             return new Face[0];
698         } else if (faceScores.length != faceRectangles.length) {
699             Log.w(TAG, String.format("Face score size(%d) doesn match face rectangle size(%d)!",
700                     faceScores.length, faceRectangles.length));
701         }
702 
703         // To be safe, make number of faces is the minimal of all face info metadata length.
704         int numFaces = Math.min(faceScores.length, faceRectangles.length);
705         // Face id and landmarks are only required by FULL mode.
706         if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
707             if (faceIds == null || faceLandmarks == null) {
708                 Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," +
709                         "fallback to SIMPLE mode");
710                 faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
711             } else {
712                 if (faceIds.length != numFaces ||
713                         faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) {
714                     Log.w(TAG, String.format("Face id size(%d), or face landmark size(%d) don't" +
715                             "match face number(%d)!",
716                             faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE, numFaces));
717                 }
718                 // To be safe, make number of faces is the minimal of all face info metadata length.
719                 numFaces = Math.min(numFaces, faceIds.length);
720                 numFaces = Math.min(numFaces, faceLandmarks.length / FACE_LANDMARK_SIZE);
721             }
722         }
723 
724         ArrayList<Face> faceList = new ArrayList<Face>();
725         if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
726             for (int i = 0; i < numFaces; i++) {
727                 if (faceScores[i] <= Face.SCORE_MAX &&
728                         faceScores[i] >= Face.SCORE_MIN) {
729                     faceList.add(new Face(faceRectangles[i], faceScores[i]));
730                 }
731             }
732         } else {
733             // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL
734             for (int i = 0; i < numFaces; i++) {
735                 if (faceScores[i] <= Face.SCORE_MAX &&
736                         faceScores[i] >= Face.SCORE_MIN &&
737                         faceIds[i] >= 0) {
738                     Point leftEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE],
739                             faceLandmarks[i*FACE_LANDMARK_SIZE+1]);
740                     Point rightEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+2],
741                             faceLandmarks[i*FACE_LANDMARK_SIZE+3]);
742                     Point mouth = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+4],
743                             faceLandmarks[i*FACE_LANDMARK_SIZE+5]);
744                     Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i],
745                             leftEye, rightEye, mouth);
746                     faceList.add(face);
747                 }
748             }
749         }
750         Face[] faces = new Face[faceList.size()];
751         faceList.toArray(faces);
752         return faces;
753     }
754 
755     // Face rectangles are defined as (left, top, right, bottom) instead of
756     // (left, top, width, height) at the native level, so the normal Rect
757     // conversion that does (l, t, w, h) -> (l, t, r, b) is unnecessary. Undo
758     // that conversion here for just the faces.
getFaceRectangles()759     private Rect[] getFaceRectangles() {
760         Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES);
761         if (faceRectangles == null) return null;
762 
763         Rect[] fixedFaceRectangles = new Rect[faceRectangles.length];
764         for (int i = 0; i < faceRectangles.length; i++) {
765             fixedFaceRectangles[i] = new Rect(
766                     faceRectangles[i].left,
767                     faceRectangles[i].top,
768                     faceRectangles[i].right - faceRectangles[i].left,
769                     faceRectangles[i].bottom - faceRectangles[i].top);
770         }
771         return fixedFaceRectangles;
772     }
773 
getLensShadingMap()774     private LensShadingMap getLensShadingMap() {
775         float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
776         Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
777 
778         // Do not warn if lsmArray is null while s is not. This is valid.
779         if (lsmArray == null) {
780             return null;
781         }
782 
783         if (s == null) {
784             Log.w(TAG, "getLensShadingMap - Lens shading map size was null.");
785             return null;
786         }
787 
788         LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth());
789         return map;
790     }
791 
getGpsLocation()792     private Location getGpsLocation() {
793         String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
794         double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
795         Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP);
796 
797         if (areValuesAllNull(processingMethod, coords, timeStamp)) {
798             return null;
799         }
800 
801         Location l = new Location(translateProcessToLocationProvider(processingMethod));
802         if (timeStamp != null) {
803             l.setTime(timeStamp);
804         } else {
805             Log.w(TAG, "getGpsLocation - No timestamp for GPS location.");
806         }
807 
808         if (coords != null) {
809             l.setLatitude(coords[0]);
810             l.setLongitude(coords[1]);
811             l.setAltitude(coords[2]);
812         } else {
813             Log.w(TAG, "getGpsLocation - No coordinates for GPS location");
814         }
815 
816         return l;
817     }
818 
setGpsLocation(Location l)819     private boolean setGpsLocation(Location l) {
820         if (l == null) {
821             return false;
822         }
823 
824         double[] coords = { l.getLatitude(), l.getLongitude(), l.getAltitude() };
825         String processMethod = translateLocationProviderToProcess(l.getProvider());
826         long timestamp = l.getTime();
827 
828         set(CaptureRequest.JPEG_GPS_TIMESTAMP, timestamp);
829         set(CaptureRequest.JPEG_GPS_COORDINATES, coords);
830 
831         if (processMethod == null) {
832             Log.w(TAG, "setGpsLocation - No process method, Location is not from a GPS or NETWORK" +
833                     "provider");
834         } else {
835             setBase(CaptureRequest.JPEG_GPS_PROCESSING_METHOD, processMethod);
836         }
837         return true;
838     }
839 
getStreamConfigurationMap()840     private StreamConfigurationMap getStreamConfigurationMap() {
841         StreamConfiguration[] configurations = getBase(
842                 CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
843         StreamConfigurationDuration[] minFrameDurations = getBase(
844                 CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
845         StreamConfigurationDuration[] stallDurations = getBase(
846                 CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS);
847         StreamConfiguration[] depthConfigurations = getBase(
848                 CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
849         StreamConfigurationDuration[] depthMinFrameDurations = getBase(
850                 CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
851         StreamConfigurationDuration[] depthStallDurations = getBase(
852                 CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
853         HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase(
854                 CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
855         ReprocessFormatsMap inputOutputFormatsMap = getBase(
856                 CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP);
857         int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
858         boolean listHighResolution = false;
859         for (int capability : capabilities) {
860             if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
861                 listHighResolution = true;
862                 break;
863             }
864         }
865         return new StreamConfigurationMap(
866                 configurations, minFrameDurations, stallDurations,
867                 depthConfigurations, depthMinFrameDurations, depthStallDurations,
868                 highSpeedVideoConfigurations, inputOutputFormatsMap,
869                 listHighResolution);
870     }
871 
getMaxRegions(Key<T> key)872     private <T> Integer getMaxRegions(Key<T> key) {
873         final int AE = 0;
874         final int AWB = 1;
875         final int AF = 2;
876 
877         // The order of the elements is: (AE, AWB, AF)
878         int[] maxRegions = getBase(CameraCharacteristics.CONTROL_MAX_REGIONS);
879 
880         if (maxRegions == null) {
881             return null;
882         }
883 
884         if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
885             return maxRegions[AE];
886         } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
887             return maxRegions[AWB];
888         } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
889             return maxRegions[AF];
890         } else {
891             throw new AssertionError("Invalid key " + key);
892         }
893     }
894 
getMaxNumOutputs(Key<T> key)895     private <T> Integer getMaxNumOutputs(Key<T> key) {
896         final int RAW = 0;
897         final int PROC = 1;
898         final int PROC_STALLING = 2;
899 
900         // The order of the elements is: (raw, proc+nonstalling, proc+stalling)
901         int[] maxNumOutputs = getBase(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS);
902 
903         if (maxNumOutputs == null) {
904             return null;
905         }
906 
907         if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
908             return maxNumOutputs[RAW];
909         } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
910             return maxNumOutputs[PROC];
911         } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
912             return maxNumOutputs[PROC_STALLING];
913         } else {
914             throw new AssertionError("Invalid key " + key);
915         }
916     }
917 
getTonemapCurve()918     private <T> TonemapCurve getTonemapCurve() {
919         float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED);
920         float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN);
921         float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE);
922 
923         if (areValuesAllNull(red, green, blue)) {
924             return null;
925         }
926 
927         if (red == null || green == null || blue == null) {
928             Log.w(TAG, "getTonemapCurve - missing tone curve components");
929             return null;
930         }
931         TonemapCurve tc = new TonemapCurve(red, green, blue);
932         return tc;
933     }
934 
setBase(CameraCharacteristics.Key<T> key, T value)935     private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
936         setBase(key.getNativeKey(), value);
937     }
938 
setBase(CaptureResult.Key<T> key, T value)939     private <T> void setBase(CaptureResult.Key<T> key, T value) {
940         setBase(key.getNativeKey(), value);
941     }
942 
setBase(CaptureRequest.Key<T> key, T value)943     private <T> void setBase(CaptureRequest.Key<T> key, T value) {
944         setBase(key.getNativeKey(), value);
945     }
946 
setBase(Key<T> key, T value)947     private <T> void setBase(Key<T> key, T value) {
948         int tag = key.getTag();
949 
950         if (value == null) {
951             // Erase the entry
952             writeValues(tag, /*src*/null);
953             return;
954         } // else update the entry to a new value
955 
956         Marshaler<T> marshaler = getMarshalerForKey(key);
957         int size = marshaler.calculateMarshalSize(value);
958 
959         // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
960         byte[] values = new byte[size];
961 
962         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
963         marshaler.marshal(value, buffer);
964 
965         writeValues(tag, values);
966     }
967 
968     // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
969     // metadata.
970     private static final HashMap<Key<?>, SetCommand> sSetCommandMap =
971             new HashMap<Key<?>, SetCommand>();
972     static {
CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey()973         sSetCommandMap.put(CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(),
974                 new SetCommand() {
975             @Override
976             public <T> void setValue(CameraMetadataNative metadata, T value) {
977                 metadata.setAvailableFormats((int[]) value);
978             }
979         });
CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey()980         sSetCommandMap.put(CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(),
981                 new SetCommand() {
982             @Override
983             public <T> void setValue(CameraMetadataNative metadata, T value) {
984                 metadata.setFaceRectangles((Rect[]) value);
985             }
986         });
CaptureResult.STATISTICS_FACES.getNativeKey()987         sSetCommandMap.put(CaptureResult.STATISTICS_FACES.getNativeKey(),
988                 new SetCommand() {
989             @Override
990             public <T> void setValue(CameraMetadataNative metadata, T value) {
991                 metadata.setFaces((Face[])value);
992             }
993         });
CaptureRequest.TONEMAP_CURVE.getNativeKey()994         sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() {
995             @Override
996             public <T> void setValue(CameraMetadataNative metadata, T value) {
997                 metadata.setTonemapCurve((TonemapCurve) value);
998             }
999         });
CaptureResult.JPEG_GPS_LOCATION.getNativeKey()1000         sSetCommandMap.put(CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new SetCommand() {
1001             @Override
1002             public <T> void setValue(CameraMetadataNative metadata, T value) {
1003                 metadata.setGpsLocation((Location) value);
1004             }
1005         });
1006     }
1007 
setAvailableFormats(int[] value)1008     private boolean setAvailableFormats(int[] value) {
1009         int[] availableFormat = value;
1010         if (value == null) {
1011             // Let setBase() to handle the null value case.
1012             return false;
1013         }
1014 
1015         int[] newValues = new int[availableFormat.length];
1016         for (int i = 0; i < availableFormat.length; i++) {
1017             newValues[i] = availableFormat[i];
1018             if (availableFormat[i] == ImageFormat.JPEG) {
1019                 newValues[i] = NATIVE_JPEG_FORMAT;
1020             }
1021         }
1022 
1023         setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues);
1024         return true;
1025     }
1026 
1027     /**
1028      * Convert Face Rectangles from managed side to native side as they have different definitions.
1029      * <p>
1030      * Managed side face rectangles are defined as: left, top, width, height.
1031      * Native side face rectangles are defined as: left, top, right, bottom.
1032      * The input face rectangle need to be converted to native side definition when set is called.
1033      * </p>
1034      *
1035      * @param faceRects Input face rectangles.
1036      * @return true if face rectangles can be set successfully. Otherwise, Let the caller
1037      *             (setBase) to handle it appropriately.
1038      */
setFaceRectangles(Rect[] faceRects)1039     private boolean setFaceRectangles(Rect[] faceRects) {
1040         if (faceRects == null) {
1041             return false;
1042         }
1043 
1044         Rect[] newFaceRects = new Rect[faceRects.length];
1045         for (int i = 0; i < newFaceRects.length; i++) {
1046             newFaceRects[i] = new Rect(
1047                     faceRects[i].left,
1048                     faceRects[i].top,
1049                     faceRects[i].right + faceRects[i].left,
1050                     faceRects[i].bottom + faceRects[i].top);
1051         }
1052 
1053         setBase(CaptureResult.STATISTICS_FACE_RECTANGLES, newFaceRects);
1054         return true;
1055     }
1056 
setTonemapCurve(TonemapCurve tc)1057     private <T> boolean setTonemapCurve(TonemapCurve tc) {
1058         if (tc == null) {
1059             return false;
1060         }
1061 
1062         float[][] curve = new float[3][];
1063         for (int i = TonemapCurve.CHANNEL_RED; i <= TonemapCurve.CHANNEL_BLUE; i++) {
1064             int pointCount = tc.getPointCount(i);
1065             curve[i] = new float[pointCount * TonemapCurve.POINT_SIZE];
1066             tc.copyColorCurve(i, curve[i], 0);
1067         }
1068         setBase(CaptureRequest.TONEMAP_CURVE_RED, curve[0]);
1069         setBase(CaptureRequest.TONEMAP_CURVE_GREEN, curve[1]);
1070         setBase(CaptureRequest.TONEMAP_CURVE_BLUE, curve[2]);
1071 
1072         return true;
1073     }
1074 
1075     private long mMetadataPtr; // native CameraMetadata*
1076 
nativeAllocate()1077     private native long nativeAllocate();
nativeAllocateCopy(CameraMetadataNative other)1078     private native long nativeAllocateCopy(CameraMetadataNative other)
1079             throws NullPointerException;
1080 
nativeWriteToParcel(Parcel dest)1081     private native synchronized void nativeWriteToParcel(Parcel dest);
nativeReadFromParcel(Parcel source)1082     private native synchronized void nativeReadFromParcel(Parcel source);
nativeSwap(CameraMetadataNative other)1083     private native synchronized void nativeSwap(CameraMetadataNative other)
1084             throws NullPointerException;
nativeClose()1085     private native synchronized void nativeClose();
nativeIsEmpty()1086     private native synchronized boolean nativeIsEmpty();
nativeGetEntryCount()1087     private native synchronized int nativeGetEntryCount();
1088 
nativeReadValues(int tag)1089     private native synchronized byte[] nativeReadValues(int tag);
nativeWriteValues(int tag, byte[] src)1090     private native synchronized void nativeWriteValues(int tag, byte[] src);
nativeDump()1091     private native synchronized void nativeDump() throws IOException; // dump to ALOGD
1092 
nativeGetAllVendorKeys(Class keyClass)1093     private static native ArrayList nativeGetAllVendorKeys(Class keyClass);
nativeGetTagFromKey(String keyName)1094     private static native int nativeGetTagFromKey(String keyName)
1095             throws IllegalArgumentException;
nativeGetTypeFromTag(int tag)1096     private static native int nativeGetTypeFromTag(int tag)
1097             throws IllegalArgumentException;
nativeClassInit()1098     private static native void nativeClassInit();
1099 
1100     /**
1101      * <p>Perform a 0-copy swap of the internal metadata with another object.</p>
1102      *
1103      * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
1104      *
1105      * @param other Metadata to swap with
1106      * @throws NullPointerException if other was null
1107      * @hide
1108      */
swap(CameraMetadataNative other)1109     public void swap(CameraMetadataNative other) {
1110         nativeSwap(other);
1111     }
1112 
1113     /**
1114      * @hide
1115      */
getEntryCount()1116     public int getEntryCount() {
1117         return nativeGetEntryCount();
1118     }
1119 
1120     /**
1121      * Does this metadata contain at least 1 entry?
1122      *
1123      * @hide
1124      */
isEmpty()1125     public boolean isEmpty() {
1126         return nativeIsEmpty();
1127     }
1128 
1129 
1130     /**
1131      * Return a list containing keys of the given key class for all defined vendor tags.
1132      *
1133      * @hide
1134      */
getAllVendorKeys(Class<K> keyClass)1135     public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
1136         if (keyClass == null) {
1137             throw new NullPointerException();
1138         }
1139         return (ArrayList<K>) nativeGetAllVendorKeys(keyClass);
1140     }
1141 
1142     /**
1143      * Convert a key string into the equivalent native tag.
1144      *
1145      * @throws IllegalArgumentException if the key was not recognized
1146      * @throws NullPointerException if the key was null
1147      *
1148      * @hide
1149      */
getTag(String key)1150     public static int getTag(String key) {
1151         return nativeGetTagFromKey(key);
1152     }
1153 
1154     /**
1155      * Get the underlying native type for a tag.
1156      *
1157      * @param tag An integer tag, see e.g. {@link #getTag}
1158      * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
1159      *
1160      * @hide
1161      */
getNativeType(int tag)1162     public static int getNativeType(int tag) {
1163         return nativeGetTypeFromTag(tag);
1164     }
1165 
1166     /**
1167      * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
1168      * the entry if src was null.</p>
1169      *
1170      * <p>An empty array can be passed in to update the entry to 0 elements.</p>
1171      *
1172      * @param tag An integer tag, see e.g. {@link #getTag}
1173      * @param src An array of bytes, or null to erase the entry
1174      *
1175      * @hide
1176      */
writeValues(int tag, byte[] src)1177     public void writeValues(int tag, byte[] src) {
1178         nativeWriteValues(tag, src);
1179     }
1180 
1181     /**
1182      * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
1183      * the data properly.</p>
1184      *
1185      * <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
1186      *
1187      * @param tag An integer tag, see e.g. {@link #getTag}
1188      *
1189      * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
1190      * @hide
1191      */
readValues(int tag)1192     public byte[] readValues(int tag) {
1193         // TODO: Optimization. Native code returns a ByteBuffer instead.
1194         return nativeReadValues(tag);
1195     }
1196 
1197     /**
1198      * Dumps the native metadata contents to logcat.
1199      *
1200      * <p>Visibility for testing/debugging only. The results will not
1201      * include any synthesized keys, as they are invisible to the native layer.</p>
1202      *
1203      * @hide
1204      */
dumpToLog()1205     public void dumpToLog() {
1206         try {
1207             nativeDump();
1208         } catch (IOException e) {
1209             Log.wtf(TAG, "Dump logging failed", e);
1210         }
1211     }
1212 
1213     @Override
finalize()1214     protected void finalize() throws Throwable {
1215         try {
1216             close();
1217         } finally {
1218             super.finalize();
1219         }
1220     }
1221 
1222     /**
1223      * Get the marshaler compatible with the {@code key} and type {@code T}.
1224      *
1225      * @throws UnsupportedOperationException
1226      *          if the native/managed type combination for {@code key} is not supported
1227      */
getMarshalerForKey(Key<T> key)1228     private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) {
1229         return MarshalRegistry.getMarshaler(key.getTypeReference(),
1230                 getNativeType(key.getTag()));
1231     }
1232 
1233     @SuppressWarnings({ "unchecked", "rawtypes" })
registerAllMarshalers()1234     private static void registerAllMarshalers() {
1235         if (DEBUG) {
1236             Log.v(TAG, "Shall register metadata marshalers");
1237         }
1238 
1239         MarshalQueryable[] queryList = new MarshalQueryable[] {
1240                 // marshalers for standard types
1241                 new MarshalQueryablePrimitive(),
1242                 new MarshalQueryableEnum(),
1243                 new MarshalQueryableArray(),
1244 
1245                 // pseudo standard types, that expand/narrow the native type into a managed type
1246                 new MarshalQueryableBoolean(),
1247                 new MarshalQueryableNativeByteToInteger(),
1248 
1249                 // marshalers for custom types
1250                 new MarshalQueryableRect(),
1251                 new MarshalQueryableSize(),
1252                 new MarshalQueryableSizeF(),
1253                 new MarshalQueryableString(),
1254                 new MarshalQueryableReprocessFormatsMap(),
1255                 new MarshalQueryableRange(),
1256                 new MarshalQueryablePair(),
1257                 new MarshalQueryableMeteringRectangle(),
1258                 new MarshalQueryableColorSpaceTransform(),
1259                 new MarshalQueryableStreamConfiguration(),
1260                 new MarshalQueryableStreamConfigurationDuration(),
1261                 new MarshalQueryableRggbChannelVector(),
1262                 new MarshalQueryableBlackLevelPattern(),
1263                 new MarshalQueryableHighSpeedVideoConfiguration(),
1264 
1265                 // generic parcelable marshaler (MUST BE LAST since it has lowest priority)
1266                 new MarshalQueryableParcelable(),
1267         };
1268 
1269         for (MarshalQueryable query : queryList) {
1270             MarshalRegistry.registerMarshalQueryable(query);
1271         }
1272         if (DEBUG) {
1273             Log.v(TAG, "Registered metadata marshalers");
1274         }
1275     }
1276 
1277     /** Check if input arguments are all {@code null}.
1278      *
1279      * @param objs Input arguments for null check
1280      * @return {@code true} if input arguments are all {@code null}, otherwise {@code false}
1281      */
areValuesAllNull(Object... objs)1282     private static boolean areValuesAllNull(Object... objs) {
1283         for (Object o : objs) {
1284             if (o != null) return false;
1285         }
1286         return true;
1287     }
1288 
1289     static {
1290         /*
1291          * We use a class initializer to allow the native code to cache some field offsets
1292          */
nativeClassInit()1293         nativeClassInit();
registerAllMarshalers()1294         registerAllMarshalers();
1295     }
1296 }
1297