Home | History | Annotate | Download | only in its
      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 com.android.camera2.its;
     18 
     19 import android.hardware.camera2.CameraDevice;
     20 import android.hardware.camera2.CameraMetadata;
     21 import android.hardware.camera2.CaptureRequest;
     22 import android.hardware.camera2.Rational;
     23 import android.util.Log;
     24 
     25 import org.json.JSONArray;
     26 import org.json.JSONObject;
     27 
     28 import java.lang.reflect.Array;
     29 import java.lang.reflect.Field;
     30 import java.lang.reflect.GenericArrayType;
     31 import java.lang.reflect.Modifier;
     32 import java.lang.reflect.ParameterizedType;
     33 import java.lang.reflect.Type;
     34 import java.util.Arrays;
     35 import java.util.LinkedList;
     36 import java.util.List;
     37 
     38 /**
     39  * Class to deal with serializing and deserializing between JSON and Camera2 objects.
     40  */
     41 public class ItsSerializer {
     42     public static final String TAG = ItsSerializer.class.getSimpleName();
     43 
     44     private static class MetadataEntry {
     45         public MetadataEntry(String k, Object v) {
     46             key = k;
     47             value = v;
     48         }
     49         public String key;
     50         public Object value;
     51     }
     52 
     53     @SuppressWarnings("unchecked")
     54     private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md)
     55             throws ItsException {
     56         CameraMetadata.Key key = (CameraMetadata.Key)keyObj;
     57         try {
     58             if (md.get(key) == null) {
     59                 return new MetadataEntry(key.getName(), JSONObject.NULL);
     60             }
     61             if (keyType == Integer.class || keyType == Long.class    || keyType == Byte.class ||
     62                 keyType == Float.class   || keyType == Boolean.class || keyType == String.class) {
     63                 return new MetadataEntry(key.getName(), md.get(key));
     64             } else if (keyType == Rational.class) {
     65                 CameraMetadata.Key<Rational> key2 = (CameraMetadata.Key<Rational>)keyObj;
     66                 JSONObject ratObj = new JSONObject();
     67                 ratObj.put("numerator", md.get(key2).getNumerator());
     68                 ratObj.put("denominator", md.get(key2).getDenominator());
     69                 return new MetadataEntry(key.getName(), ratObj);
     70             } else if (keyType == android.hardware.camera2.Size.class) {
     71                 CameraMetadata.Key<android.hardware.camera2.Size> key2 =
     72                         (CameraMetadata.Key<android.hardware.camera2.Size>)keyObj;
     73                 JSONObject sizeObj = new JSONObject();
     74                 sizeObj.put("width", md.get(key2).getWidth());
     75                 sizeObj.put("height", md.get(key2).getHeight());
     76                 return new MetadataEntry(key.getName(), sizeObj);
     77             } else if (keyType == android.graphics.Rect.class) {
     78                 CameraMetadata.Key<android.graphics.Rect> key2 =
     79                         (CameraMetadata.Key<android.graphics.Rect>)keyObj;
     80                 JSONObject rectObj = new JSONObject();
     81                 rectObj.put("left", md.get(key2).left);
     82                 rectObj.put("right", md.get(key2).right);
     83                 rectObj.put("top", md.get(key2).top);
     84                 rectObj.put("bottom", md.get(key2).bottom);
     85                 return new MetadataEntry(key.getName(), rectObj);
     86             } else {
     87                 throw new ItsException(
     88                         "Unsupported key type in metadata serializer: " + keyType);
     89             }
     90         } catch (org.json.JSONException e) {
     91             throw new ItsException("JSON error for key: " + key.getName() + ": ", e);
     92         }
     93     }
     94 
     95     @SuppressWarnings("unchecked")
     96     private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md)
     97             throws ItsException {
     98         CameraMetadata.Key key = (CameraMetadata.Key)keyObj;
     99         try {
    100             if (md.get(key) == null) {
    101                 return new MetadataEntry(key.getName(), JSONObject.NULL);
    102             }
    103             Type elmtType = ((GenericArrayType)keyType).getGenericComponentType();
    104             if (elmtType == int.class  || elmtType == float.class || elmtType == byte.class ||
    105                 elmtType == long.class || elmtType == double.class) {
    106                 return new MetadataEntry(key.getName(), new JSONArray(md.get(key)));
    107             } else if (elmtType == Rational.class) {
    108                 CameraMetadata.Key<Rational[]> key2 = (CameraMetadata.Key<Rational[]>)keyObj;
    109                 JSONArray jsonArray = new JSONArray();
    110                 for (int i = 0; i < Array.getLength(md.get(key)); i++) {
    111                     JSONObject ratObj = new JSONObject();
    112                     ratObj.put("numerator", md.get(key2)[i].getNumerator());
    113                     ratObj.put("denominator", md.get(key2)[i].getDenominator());
    114                     jsonArray.put(ratObj);
    115                 }
    116                 return new MetadataEntry(key.getName(), jsonArray);
    117             } else if (elmtType == android.hardware.camera2.Size.class) {
    118                 CameraMetadata.Key<android.hardware.camera2.Size[]> key2 =
    119                         (CameraMetadata.Key<android.hardware.camera2.Size[]>)keyObj;
    120                 JSONArray jsonArray = new JSONArray();
    121                 for (int i = 0; i < Array.getLength(md.get(key)); i++) {
    122                     JSONObject sizeObj = new JSONObject();
    123                     sizeObj.put("width", md.get(key2)[i].getWidth());
    124                     sizeObj.put("height", md.get(key2)[i].getHeight());
    125                     jsonArray.put(sizeObj);
    126                 }
    127                 return new MetadataEntry(key.getName(), jsonArray);
    128             } else if (elmtType == android.graphics.Rect.class) {
    129                 CameraMetadata.Key<android.graphics.Rect[]> key2 =
    130                         (CameraMetadata.Key<android.graphics.Rect[]>)keyObj;
    131                 JSONArray jsonArray = new JSONArray();
    132                 for (int i = 0; i < Array.getLength(md.get(key)); i++) {
    133                     JSONObject rectObj = new JSONObject();
    134                     rectObj.put("left", md.get(key2)[i].left);
    135                     rectObj.put("right", md.get(key2)[i].right);
    136                     rectObj.put("top", md.get(key2)[i].top);
    137                     rectObj.put("bottom", md.get(key2)[i].bottom);
    138                     jsonArray.put(rectObj);
    139                 }
    140                 return new MetadataEntry(key.getName(), jsonArray);
    141             } else if (elmtType == android.hardware.camera2.Face.class) {
    142                 CameraMetadata.Key<android.hardware.camera2.Face[]> key2 =
    143                         (CameraMetadata.Key<android.hardware.camera2.Face[]>)keyObj;
    144 
    145                 // TODO: Serialize an array of faces to JSON.
    146                 // Will also need to deserialize JSON faces in the appropriate method.
    147                 if (Array.getLength(md.get(key)) != 0) {
    148                     throw new ItsException("Serialization of faces not implemented yet");
    149                 }
    150                 return new MetadataEntry(key.getName(), new JSONArray());
    151 
    152             } else {
    153                 throw new ItsException("Unsupported array type: " + elmtType);
    154             }
    155         } catch (org.json.JSONException e) {
    156             throw new ItsException("JSON error for key: " + key.getName() + ": ", e);
    157         }
    158     }
    159 
    160     @SuppressWarnings("unchecked")
    161     public static JSONObject serialize(CameraMetadata md)
    162             throws ItsException {
    163         JSONObject jsonObj = new JSONObject();
    164         Field[] allFields = md.getClass().getDeclaredFields();
    165         for (Field field : allFields) {
    166             if (Modifier.isPublic(field.getModifiers()) &&
    167                     Modifier.isStatic(field.getModifiers()) &&
    168                     field.getType() == CameraMetadata.Key.class &&
    169                     field.getGenericType() instanceof ParameterizedType) {
    170                 ParameterizedType paramType = (ParameterizedType)field.getGenericType();
    171                 Type[] argTypes = paramType.getActualTypeArguments();
    172                 if (argTypes.length > 0) {
    173                     try {
    174                         Type keyType = argTypes[0];
    175                         Object keyObj = field.get(md);
    176                         MetadataEntry entry;
    177                         if (keyType instanceof GenericArrayType) {
    178                             entry = serializeArrayEntry(keyType, keyObj, md);
    179                         } else {
    180                             entry = serializeEntry(keyType, keyObj, md);
    181                         }
    182                         if (entry != null) {
    183                             jsonObj.put(entry.key, entry.value);
    184                         }
    185                     } catch (IllegalAccessException e) {
    186                         throw new ItsException(
    187                                 "Access error for field: " + field + ": ", e);
    188                     } catch (org.json.JSONException e) {
    189                         throw new ItsException(
    190                                 "JSON error for field: " + field + ": ", e);
    191                     } catch (IllegalArgumentException e) {
    192                         // TODO: Remove this HACK once all keys are implemented (especially
    193                         // android.statistics.faces).
    194                         // If the key isn't plumbed all the way down, can get an exception by
    195                         // trying to get it. Swallow the exception.
    196                     }
    197                 }
    198             }
    199         }
    200         return jsonObj;
    201     }
    202 
    203     @SuppressWarnings("unchecked")
    204     public static CaptureRequest.Builder deserialize(CaptureRequest.Builder mdDefault,
    205             JSONObject jsonReq) throws ItsException {
    206         try {
    207             Log.i(TAG, "Parsing JSON capture request ...");
    208 
    209             // Iterate over the CameraMetadata reflected fields.
    210             CaptureRequest.Builder md = mdDefault;
    211             Field[] allFields = CaptureRequest.class.getDeclaredFields();
    212             for (Field field : allFields) {
    213                 if (Modifier.isPublic(field.getModifiers()) &&
    214                         Modifier.isStatic(field.getModifiers()) &&
    215                         field.getType() == CameraMetadata.Key.class &&
    216                         field.getGenericType() instanceof ParameterizedType) {
    217                     ParameterizedType paramType = (ParameterizedType)field.getGenericType();
    218                     Type[] argTypes = paramType.getActualTypeArguments();
    219                     try {
    220                         if (argTypes.length > 0) {
    221                             CameraMetadata.Key key = (CameraMetadata.Key)field.get(md);
    222                             String keyName = key.getName();
    223                             Type keyType = argTypes[0];
    224 
    225                             // For each reflected CameraMetadata entry, look inside the JSON object
    226                             // to see if it is being set. If it is found, remove the key from the
    227                             // JSON object. After this process, there should be no keys left in the
    228                             // JSON (otherwise an invalid key was specified).
    229 
    230                             if (jsonReq.has(keyName) && !jsonReq.isNull(keyName)) {
    231                                 if (keyType instanceof GenericArrayType) {
    232                                     Type elmtType =
    233                                             ((GenericArrayType)keyType).getGenericComponentType();
    234                                     JSONArray ja = jsonReq.getJSONArray(keyName);
    235                                     Object val[] = new Object[ja.length()];
    236                                     for (int i = 0; i < ja.length(); i++) {
    237                                         if (elmtType == int.class) {
    238                                             Array.set(val, i, ja.getInt(i));
    239                                         } else if (elmtType == byte.class) {
    240                                             Array.set(val, i, (byte)ja.getInt(i));
    241                                         } else if (elmtType == float.class) {
    242                                             Array.set(val, i, (float)ja.getDouble(i));
    243                                         } else if (elmtType == long.class) {
    244                                             Array.set(val, i, ja.getLong(i));
    245                                         } else if (elmtType == double.class) {
    246                                             Array.set(val, i, ja.getDouble(i));
    247                                         } else if (elmtType == boolean.class) {
    248                                             Array.set(val, i, ja.getBoolean(i));
    249                                         } else if (elmtType == String.class) {
    250                                             Array.set(val, i, ja.getString(i));
    251                                         } else if (elmtType == android.hardware.camera2.Size.class){
    252                                             JSONObject obj = ja.getJSONObject(i);
    253                                             Array.set(val, i, new android.hardware.camera2.Size(
    254                                                     obj.getInt("width"), obj.getInt("height")));
    255                                         } else if (elmtType == android.graphics.Rect.class) {
    256                                             JSONObject obj = ja.getJSONObject(i);
    257                                             Array.set(val, i, new android.graphics.Rect(
    258                                                     obj.getInt("left"), obj.getInt("top"),
    259                                                     obj.getInt("bottom"), obj.getInt("right")));
    260                                         } else if (elmtType == Rational.class) {
    261                                             JSONObject obj = ja.getJSONObject(i);
    262                                             Array.set(val, i, new Rational(
    263                                                     obj.getInt("numerator"),
    264                                                     obj.getInt("denominator")));
    265                                         } else {
    266                                             throw new ItsException(
    267                                                     "Failed to parse key from JSON: " + keyName);
    268                                         }
    269                                     }
    270                                     if (val != null) {
    271                                         Log.i(TAG, "Set: "+keyName+" -> "+Arrays.toString(val));
    272                                         md.set(key, val);
    273                                         jsonReq.remove(keyName);
    274                                     }
    275                                 } else {
    276                                     Object val = null;
    277                                     if (keyType == Integer.class) {
    278                                         val = jsonReq.getInt(keyName);
    279                                     } else if (keyType == Byte.class) {
    280                                         val = (byte)jsonReq.getInt(keyName);
    281                                     } else if (keyType == Double.class) {
    282                                         val = jsonReq.getDouble(keyName);
    283                                     } else if (keyType == Long.class) {
    284                                         val = jsonReq.getLong(keyName);
    285                                     } else if (keyType == Float.class) {
    286                                         val = (float)jsonReq.getDouble(keyName);
    287                                     } else if (keyType == Boolean.class) {
    288                                         val = jsonReq.getBoolean(keyName);
    289                                     } else if (keyType == String.class) {
    290                                         val = jsonReq.getString(keyName);
    291                                     } else if (keyType == android.hardware.camera2.Size.class) {
    292                                         JSONObject obj = jsonReq.getJSONObject(keyName);
    293                                         val = new android.hardware.camera2.Size(
    294                                                 obj.getInt("width"), obj.getInt("height"));
    295                                     } else if (keyType == android.graphics.Rect.class) {
    296                                         JSONObject obj = jsonReq.getJSONObject(keyName);
    297                                         val = new android.graphics.Rect(
    298                                                 obj.getInt("left"), obj.getInt("top"),
    299                                                 obj.getInt("right"), obj.getInt("bottom"));
    300                                     } else if (keyType == Rational.class) {
    301                                         JSONObject obj = jsonReq.getJSONObject(keyName);
    302                                         val = new Rational(obj.getInt("numerator"),
    303                                                            obj.getInt("denominator"));
    304                                     } else {
    305                                         throw new ItsException(
    306                                                 "Failed to parse key from JSON: " + keyName);
    307                                     }
    308                                     if (val != null) {
    309                                         Log.i(TAG, "Set: " + keyName + " -> " + val);
    310                                         md.set(key ,val);
    311                                         jsonReq.remove(keyName);
    312                                     }
    313                                 }
    314                             }
    315                         }
    316                     } catch (IllegalArgumentException e) {
    317                         // TODO: Remove this HACK once all keys are implemented (especially
    318                         // android.statistics.faces).
    319                         // If the key isn't plumbed all the way down, can get an exception by
    320                         // trying to get it. Swallow the exception.
    321                     }
    322                 }
    323             }
    324 
    325             // Ensure that there were no invalid keys in the JSON request object.
    326             if (jsonReq.length() != 0) {
    327                 throw new ItsException("Invalid JSON key(s): " + jsonReq.toString());
    328             }
    329 
    330             Log.i(TAG, "Parsing JSON capture request completed");
    331             return md;
    332         } catch (java.lang.IllegalAccessException e) {
    333             throw new ItsException("Access error: ", e);
    334         } catch (org.json.JSONException e) {
    335             throw new ItsException("JSON error: ", e);
    336         }
    337     }
    338 
    339     @SuppressWarnings("unchecked")
    340     public static List<CaptureRequest.Builder> deserializeRequestList(
    341             CameraDevice device, JSONObject jsonObjTop)
    342             throws ItsException {
    343         try {
    344             List<CaptureRequest.Builder> requests = null;
    345             if (jsonObjTop.has("captureRequest")) {
    346                 JSONObject jsonReq = jsonObjTop.getJSONObject("captureRequest");
    347                 CaptureRequest.Builder templateReq = device.createCaptureRequest(
    348                         CameraDevice.TEMPLATE_STILL_CAPTURE);
    349                 requests = new LinkedList<CaptureRequest.Builder>();
    350                 requests.add(deserialize(templateReq, jsonReq));
    351             } else if (jsonObjTop.has("captureRequestList")) {
    352                 JSONArray jsonReqs = jsonObjTop.getJSONArray("captureRequestList");
    353                 requests = new LinkedList<CaptureRequest.Builder>();
    354                 for (int i = 0; i < jsonReqs.length(); i++) {
    355                     CaptureRequest.Builder templateReq = device.createCaptureRequest(
    356                             CameraDevice.TEMPLATE_STILL_CAPTURE);
    357                     requests.add(
    358                         deserialize(templateReq, jsonReqs.getJSONObject(i)));
    359                 }
    360             }
    361             return requests;
    362         } catch (org.json.JSONException e) {
    363             throw new ItsException("JSON error: ", e);
    364         } catch (android.hardware.camera2.CameraAccessException e) {
    365             throw new ItsException("Access error: ", e);
    366         }
    367     }
    368 }
    369