Home | History | Annotate | Download | only in v2
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 
     15 package com.android.camera.one.v2;
     16 
     17 import android.annotation.TargetApi;
     18 import android.content.Context;
     19 import android.graphics.ImageFormat;
     20 import android.graphics.Rect;
     21 import android.hardware.camera2.CameraAccessException;
     22 import android.hardware.camera2.CameraCaptureSession;
     23 import android.hardware.camera2.CameraCharacteristics;
     24 import android.hardware.camera2.CameraDevice;
     25 import android.hardware.camera2.CameraMetadata;
     26 import android.hardware.camera2.CaptureRequest;
     27 import android.hardware.camera2.CaptureResult;
     28 import android.hardware.camera2.CaptureResult.Key;
     29 import android.hardware.camera2.TotalCaptureResult;
     30 import android.hardware.camera2.params.MeteringRectangle;
     31 import android.hardware.camera2.params.StreamConfigurationMap;
     32 import android.location.Location;
     33 import android.media.CameraProfile;
     34 import android.media.Image;
     35 import android.media.ImageReader;
     36 import android.media.MediaActionSound;
     37 import android.net.Uri;
     38 import android.os.Build;
     39 import android.os.Handler;
     40 import android.os.HandlerThread;
     41 import android.os.SystemClock;
     42 import android.support.v4.util.Pools;
     43 import android.view.Surface;
     44 
     45 import com.android.camera.CaptureModuleUtil;
     46 import com.android.camera.debug.Log;
     47 import com.android.camera.debug.Log.Tag;
     48 import com.android.camera.exif.ExifInterface;
     49 import com.android.camera.exif.ExifTag;
     50 import com.android.camera.exif.Rational;
     51 import com.android.camera.one.AbstractOneCamera;
     52 import com.android.camera.one.CameraDirectionProvider;
     53 import com.android.camera.one.OneCamera;
     54 import com.android.camera.one.OneCamera.PhotoCaptureParameters.Flash;
     55 import com.android.camera.one.Settings3A;
     56 import com.android.camera.one.v2.ImageCaptureManager.ImageCaptureListener;
     57 import com.android.camera.one.v2.ImageCaptureManager.MetadataChangeListener;
     58 import com.android.camera.one.v2.camera2proxy.AndroidCaptureResultProxy;
     59 import com.android.camera.one.v2.camera2proxy.AndroidImageProxy;
     60 import com.android.camera.one.v2.camera2proxy.CaptureResultProxy;
     61 import com.android.camera.processing.imagebackend.TaskImageContainer;
     62 import com.android.camera.session.CaptureSession;
     63 import com.android.camera.ui.focus.LensRangeCalculator;
     64 import com.android.camera.ui.motion.LinearScale;
     65 import com.android.camera.util.CameraUtil;
     66 import com.android.camera.util.ExifUtil;
     67 import com.android.camera.util.JpegUtilNative;
     68 import com.android.camera.util.ListenerCombiner;
     69 import com.android.camera.util.Size;
     70 import com.google.common.base.Optional;
     71 import com.google.common.util.concurrent.FutureCallback;
     72 import com.google.common.util.concurrent.Futures;
     73 import com.google.common.util.concurrent.ListenableFuture;
     74 
     75 import java.nio.ByteBuffer;
     76 import java.security.InvalidParameterException;
     77 import java.util.ArrayList;
     78 import java.util.Collections;
     79 import java.util.HashSet;
     80 import java.util.List;
     81 import java.util.Set;
     82 import java.util.concurrent.LinkedBlockingQueue;
     83 import java.util.concurrent.ThreadPoolExecutor;
     84 import java.util.concurrent.TimeUnit;
     85 
     86 /**
     87  * {@link OneCamera} implementation directly on top of the Camera2 API with zero
     88  * shutter lag.<br>
     89  * TODO: Determine what the maximum number of full YUV capture frames is.
     90  */
     91 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     92 @Deprecated
     93 public class OneCameraZslImpl extends AbstractOneCamera {
     94     private static final Tag TAG = new Tag("OneCameraZslImpl2");
     95 
     96     /** Default JPEG encoding quality. */
     97     private static final int JPEG_QUALITY =
     98             CameraProfile.getJpegEncodingQualityParameter(CameraProfile.QUALITY_HIGH);
     99     /**
    100      * The maximum number of images to store in the full-size ZSL ring buffer.
    101      * <br>
    102      * TODO: Determine this number dynamically based on available memory and the
    103      * size of frames.
    104      */
    105     private static final int MAX_CAPTURE_IMAGES = 12;
    106     /**
    107      * True if zero-shutter-lag images should be captured. Some devices produce
    108      * lower-quality images for the high-frequency stream, so we may wish to
    109      * disable ZSL in that case.
    110      */
    111     private static final boolean ZSL_ENABLED = true;
    112 
    113     /**
    114      * Tags which may be used in CaptureRequests.
    115      */
    116     private static enum RequestTag {
    117         /**
    118          * Indicates that the request was explicitly sent for a single
    119          * high-quality still capture. Unlike other requests, such as the
    120          * repeating (ZSL) stream and AF/AE triggers, requests with this tag
    121          * should always be saved.
    122          */
    123         EXPLICIT_CAPTURE
    124     }
    125 
    126     /**
    127      * Set to ImageFormat.JPEG to use the hardware encoder, or
    128      * ImageFormat.YUV_420_888 to use the software encoder. No other image
    129      * formats are supported.
    130      */
    131     private static final int sCaptureImageFormat = ImageFormat.YUV_420_888;
    132     /**
    133      * Token for callbacks posted to {@link #mCameraHandler} to resume
    134      * continuous AF.
    135      */
    136     private static final String FOCUS_RESUME_CALLBACK_TOKEN = "RESUME_CONTINUOUS_AF";
    137 
    138     /** Zero weight 3A region, to reset regions per API. */
    139     /*package*/ MeteringRectangle[] ZERO_WEIGHT_3A_REGION = AutoFocusHelper.getZeroWeightRegion();
    140 
    141     /**
    142      * Thread on which high-priority camera operations, such as grabbing preview
    143      * frames for the viewfinder, are running.
    144      */
    145     private final HandlerThread mCameraThread;
    146     /** Handler of the {@link #mCameraThread}. */
    147     private final Handler mCameraHandler;
    148 
    149     /** Thread on which low-priority camera listeners are running. */
    150     private final HandlerThread mCameraListenerThread;
    151     private final Handler mCameraListenerHandler;
    152 
    153     /** The characteristics of this camera. */
    154     private final CameraCharacteristics mCharacteristics;
    155     /** Converts focus distance units into ratio values */
    156     private final LinearScale mLensRange;
    157     /** The underlying Camera2 API camera device. */
    158     private final CameraDevice mDevice;
    159     private final CameraDirectionProvider mDirection;
    160 
    161     /**
    162      * The aspect ratio (width/height) of the full resolution for this camera.
    163      * Usually the native aspect ratio of this camera.
    164      */
    165     private final float mFullSizeAspectRatio;
    166     /** The Camera2 API capture session currently active. */
    167     private CameraCaptureSession mCaptureSession;
    168     /** The surface onto which to render the preview. */
    169     private Surface mPreviewSurface;
    170     /** Whether closing of this device has been requested. */
    171     private volatile boolean mIsClosed = false;
    172 
    173     /** Receives the normal captured images. */
    174     private final ImageReader mCaptureImageReader;
    175 
    176     /**
    177      * Maintains a buffer of images and their associated {@link CaptureResult}s.
    178      */
    179     private ImageCaptureManager mCaptureManager;
    180 
    181     /**
    182      * The sensor timestamps (which may not be relative to the system time) of
    183      * the most recently captured images.
    184      */
    185     private final Set<Long> mCapturedImageTimestamps = Collections.synchronizedSet(
    186             new HashSet<Long>());
    187 
    188     /** Thread pool for performing slow jpeg encoding and saving tasks. */
    189     private final ThreadPoolExecutor mImageSaverThreadPool;
    190 
    191     /** Pool of native byte buffers on which to store jpeg-encoded images. */
    192     private final Pools.SynchronizedPool<ByteBuffer> mJpegByteBufferPool =
    193             new Pools.SynchronizedPool<ByteBuffer>(64);
    194 
    195     /** Current zoom value. 1.0 is no zoom. */
    196     private float mZoomValue = 1f;
    197     /** Current crop region: set from mZoomValue. */
    198     private Rect mCropRegion;
    199     /** Current AE, AF, and AWB regions */
    200     private MeteringRectangle[] mAFRegions = ZERO_WEIGHT_3A_REGION;
    201     private MeteringRectangle[] mAERegions = ZERO_WEIGHT_3A_REGION;
    202 
    203     private MediaActionSound mMediaActionSound = new MediaActionSound();
    204 
    205     /**
    206      * Ready state (typically displayed by the UI shutter-button) depends on two
    207      * things:<br>
    208      * <ol>
    209      * <li>{@link #mCaptureManager} must be ready.</li>
    210      * <li>We must not be in the process of capturing a single, high-quality,
    211      * image.</li>
    212      * </ol>
    213      * See {@link ListenerCombiner} and {@link #mReadyStateManager} for
    214      * details of how this is managed.
    215      */
    216     private static enum ReadyStateRequirement {
    217         CAPTURE_MANAGER_READY, CAPTURE_NOT_IN_PROGRESS
    218     }
    219 
    220     /**
    221      * Handles the thread-safe logic of dispatching whenever the logical AND of
    222      * these constraints changes.
    223      */
    224     private final ListenerCombiner<ReadyStateRequirement>
    225             mReadyStateManager = new ListenerCombiner<ReadyStateRequirement>(
    226                     ReadyStateRequirement.class, new ListenerCombiner.StateChangeListener() {
    227                             @Override
    228                         public void onStateChange(boolean state) {
    229                             broadcastReadyState(state);
    230                         }
    231                     });
    232 
    233     /**
    234      * An {@link ImageCaptureListener} which will compress and save an image to
    235      * disk.
    236      */
    237     private class ImageCaptureTask implements ImageCaptureListener {
    238         private final PhotoCaptureParameters mParams;
    239         private final CaptureSession mSession;
    240 
    241         public ImageCaptureTask(PhotoCaptureParameters parameters, CaptureSession session) {
    242             mParams = parameters;
    243             mSession = session;
    244         }
    245 
    246         @Override
    247         public void onImageCaptured(Image image, TotalCaptureResult captureResult) {
    248             long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP);
    249 
    250             // We should only capture the image if it hasn't been captured
    251             // before. Synchronization is necessary since
    252             // mCapturedImageTimestamps is read & modified elsewhere.
    253             synchronized (mCapturedImageTimestamps) {
    254                 if (!mCapturedImageTimestamps.contains(timestamp)) {
    255                     mCapturedImageTimestamps.add(timestamp);
    256                 } else {
    257                     // There was a more recent (or identical) image which has
    258                     // begun being saved, so abort.
    259                     return;
    260                 }
    261 
    262                 // Clear out old timestamps from the set.
    263                 // We must keep old timestamps in the set a little longer (a
    264                 // factor of 2 seems adequate) to ensure they are cleared out of
    265                 // the ring buffer before their timestamp is removed from the
    266                 // set.
    267                 long maxTimestamps = MAX_CAPTURE_IMAGES * 2;
    268                 if (mCapturedImageTimestamps.size() > maxTimestamps) {
    269                     ArrayList<Long> timestamps = new ArrayList<Long>(mCapturedImageTimestamps);
    270                     Collections.sort(timestamps);
    271                     for (int i = 0; i < timestamps.size()
    272                             && mCapturedImageTimestamps.size() > maxTimestamps; i++) {
    273                         mCapturedImageTimestamps.remove(timestamps.get(i));
    274                     }
    275                 }
    276             }
    277 
    278             mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS, true);
    279 
    280             savePicture(image, mParams, mSession, captureResult);
    281             mParams.callback.onPictureTaken(mSession);
    282             Log.v(TAG, "Image saved.  Frame number = " + captureResult.getFrameNumber());
    283         }
    284     }
    285 
    286     /**
    287      * Instantiates a new camera based on Camera 2 API.
    288      *
    289      * @param device The underlying Camera 2 device.
    290      * @param characteristics The device's characteristics.
    291      * @param pictureSize the size of the final image to be taken.
    292      */
    293     OneCameraZslImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize) {
    294         Log.v(TAG, "Creating new OneCameraZslImpl");
    295 
    296         mDevice = device;
    297         mCharacteristics = characteristics;
    298         mLensRange = LensRangeCalculator
    299               .getDiopterToRatioCalculator(characteristics);
    300         mDirection = new CameraDirectionProvider(mCharacteristics);
    301         mFullSizeAspectRatio = calculateFullSizeAspectRatio(characteristics);
    302 
    303         mCameraThread = new HandlerThread("OneCamera2");
    304         // If this thread stalls, it will delay viewfinder frames.
    305         mCameraThread.setPriority(Thread.MAX_PRIORITY);
    306         mCameraThread.start();
    307         mCameraHandler = new Handler(mCameraThread.getLooper());
    308 
    309         mCameraListenerThread = new HandlerThread("OneCamera2-Listener");
    310         mCameraListenerThread.start();
    311         mCameraListenerHandler = new Handler(mCameraListenerThread.getLooper());
    312 
    313         // TODO: Encoding on multiple cores results in preview jank due to
    314         // excessive GC.
    315         int numEncodingCores = CameraUtil.getNumCpuCores();
    316         mImageSaverThreadPool = new ThreadPoolExecutor(numEncodingCores, numEncodingCores, 10,
    317                 TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    318 
    319         mCaptureManager =
    320                 new ImageCaptureManager(MAX_CAPTURE_IMAGES, mCameraListenerHandler,
    321                         mImageSaverThreadPool);
    322         mCaptureManager.setCaptureReadyListener(new ImageCaptureManager.CaptureReadyListener() {
    323                 @Override
    324             public void onReadyStateChange(boolean capturePossible) {
    325                 mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_MANAGER_READY,
    326                         capturePossible);
    327             }
    328         });
    329 
    330         // Listen for changes to auto focus state and dispatch to
    331         // mFocusStateListener.
    332         mCaptureManager.addMetadataChangeListener(CaptureResult.CONTROL_AF_STATE,
    333                 new ImageCaptureManager.MetadataChangeListener() {
    334                 @Override
    335                     public void onImageMetadataChange(Key<?> key, Object oldValue, Object newValue,
    336                             CaptureResult result) {
    337                         FocusStateListener listener = mFocusStateListener;
    338                         if (listener != null) {
    339                             listener.onFocusStatusUpdate(
    340                                     AutoFocusHelper.stateFromCamera2State(
    341                                             result.get(CaptureResult.CONTROL_AF_STATE)),
    342                                 result.getFrameNumber());
    343                         }
    344                     }
    345                 });
    346 
    347         // Allocate the image reader to store all images received from the
    348         // camera.
    349         if (pictureSize == null) {
    350             // TODO The default should be selected by the caller, and
    351             // pictureSize should never be null.
    352             pictureSize = getDefaultPictureSize();
    353         }
    354         mCaptureImageReader = ImageReader.newInstance(pictureSize.getWidth(),
    355                 pictureSize.getHeight(),
    356                 sCaptureImageFormat, MAX_CAPTURE_IMAGES);
    357 
    358         mCaptureImageReader.setOnImageAvailableListener(mCaptureManager, mCameraHandler);
    359         mMediaActionSound.load(MediaActionSound.SHUTTER_CLICK);
    360     }
    361 
    362     @Override
    363     public void setFocusDistanceListener(FocusDistanceListener focusDistanceListener) {
    364         if(mFocusDistanceListener == null) {
    365             mCaptureManager.addMetadataChangeListener(CaptureResult.LENS_FOCUS_DISTANCE,
    366                   new ImageCaptureManager.MetadataChangeListener() {
    367                       @Override
    368                       public void onImageMetadataChange(Key<?> key, Object oldValue,
    369                             Object newValue,
    370                             CaptureResult result) {
    371                           Integer state = result.get(CaptureResult.LENS_STATE);
    372 
    373                           // Forward changes if we have a new value and the camera
    374                           // A) Doesn't support lens state or B) lens state is
    375                           // reported and it is reported as moving.
    376                           if (newValue != null &&
    377                                 (state == null || state == CameraMetadata.LENS_STATE_MOVING)) {
    378                               mFocusDistanceListener.onFocusDistance((float) newValue, mLensRange);
    379                           }
    380                       }
    381                   });
    382         }
    383         mFocusDistanceListener = focusDistanceListener;
    384     }
    385 
    386     /**
    387      * @return The largest supported picture size.
    388      */
    389     public Size getDefaultPictureSize() {
    390         StreamConfigurationMap configs =
    391                 mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    392         android.util.Size[] supportedSizes = configs.getOutputSizes(sCaptureImageFormat);
    393 
    394         // Find the largest supported size.
    395         android.util.Size largestSupportedSize = supportedSizes[0];
    396         long largestSupportedSizePixels =
    397                 largestSupportedSize.getWidth() * largestSupportedSize.getHeight();
    398         for (int i = 0; i < supportedSizes.length; i++) {
    399             long numPixels = supportedSizes[i].getWidth() * supportedSizes[i].getHeight();
    400             if (numPixels > largestSupportedSizePixels) {
    401                 largestSupportedSize = supportedSizes[i];
    402                 largestSupportedSizePixels = numPixels;
    403             }
    404         }
    405 
    406         return new Size(largestSupportedSize.getWidth(), largestSupportedSize.getHeight());
    407     }
    408 
    409     private void onShutterInvokeUI(final PhotoCaptureParameters params) {
    410         // Tell CaptureModule shutter has occurred so it can flash the screen.
    411         params.callback.onQuickExpose();
    412         // Play shutter click sound.
    413         mMediaActionSound.play(MediaActionSound.SHUTTER_CLICK);
    414     }
    415 
    416     /**
    417      * Take a picture.
    418      */
    419     @Override
    420     public void takePicture(final PhotoCaptureParameters params, final CaptureSession session) {
    421         mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS, false);
    422 
    423         boolean useZSL = ZSL_ENABLED;
    424 
    425         // We will only capture images from the zsl ring-buffer which satisfy
    426         // this constraint.
    427         ArrayList<ImageCaptureManager.CapturedImageConstraint> zslConstraints =
    428                 new ArrayList<ImageCaptureManager.CapturedImageConstraint>();
    429         zslConstraints.add(new ImageCaptureManager.CapturedImageConstraint() {
    430                 @Override
    431             public boolean satisfiesConstraint(TotalCaptureResult captureResult) {
    432                 Long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP);
    433                 Integer lensState = captureResult.get(CaptureResult.LENS_STATE);
    434                 Integer flashState = captureResult.get(CaptureResult.FLASH_STATE);
    435                 Integer flashMode = captureResult.get(CaptureResult.FLASH_MODE);
    436                 Integer aeState = captureResult.get(CaptureResult.CONTROL_AE_STATE);
    437                 Integer afState = captureResult.get(CaptureResult.CONTROL_AF_STATE);
    438                 Integer awbState = captureResult.get(CaptureResult.CONTROL_AWB_STATE);
    439 
    440                 if (lensState == null) {
    441                     lensState = CaptureResult.LENS_STATE_STATIONARY;
    442                 }
    443                 if (flashState == null) {
    444                     flashState = CaptureResult.FLASH_STATE_UNAVAILABLE;
    445                 }
    446                 if (flashMode == null) {
    447                     flashMode = CaptureResult.FLASH_MODE_OFF;
    448                 }
    449                 if (aeState == null) {
    450                     aeState = CaptureResult.CONTROL_AE_STATE_INACTIVE;
    451                 }
    452                 if (afState == null) {
    453                     afState = CaptureResult.CONTROL_AF_STATE_INACTIVE;
    454                 }
    455                 if (awbState == null) {
    456                     awbState = CaptureResult.CONTROL_AWB_STATE_INACTIVE;
    457                 }
    458 
    459                 synchronized (mCapturedImageTimestamps) {
    460                     if (mCapturedImageTimestamps.contains(timestamp)) {
    461                         // Don't save frames which we've already saved.
    462                         return false;
    463                     }
    464                 }
    465 
    466                 if (lensState == CaptureResult.LENS_STATE_MOVING) {
    467                     // If we know the lens was moving, don't use this image.
    468                     return false;
    469                 }
    470 
    471                 if (aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING
    472                         || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
    473                     return false;
    474                 }
    475 
    476                 if (afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN
    477                         || afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN) {
    478                     return false;
    479                 }
    480 
    481                 if (awbState == CaptureResult.CONTROL_AWB_STATE_SEARCHING) {
    482                     return false;
    483                 }
    484 
    485                 return true;
    486             }
    487         });
    488         // This constraint lets us capture images which have been explicitly
    489         // requested. See {@link RequestTag.EXPLICIT_CAPTURE}.
    490         ArrayList<ImageCaptureManager.CapturedImageConstraint> singleCaptureConstraint =
    491                 new ArrayList<ImageCaptureManager.CapturedImageConstraint>();
    492         singleCaptureConstraint.add(new ImageCaptureManager.CapturedImageConstraint() {
    493                 @Override
    494             public boolean satisfiesConstraint(TotalCaptureResult captureResult) {
    495                 Object tag = captureResult.getRequest().getTag();
    496                 return tag == RequestTag.EXPLICIT_CAPTURE;
    497             }
    498         });
    499 
    500         // If we can use ZSL, try to save a previously-captured frame, if an
    501         // acceptable one exists in the buffer.
    502         if (useZSL) {
    503             boolean capturedPreviousFrame = mCaptureManager.tryCaptureExistingImage(
    504                     new ImageCaptureTask(params, session), zslConstraints);
    505             if (capturedPreviousFrame) {
    506                 Log.v(TAG, "Saving previous frame");
    507                 onShutterInvokeUI(params);
    508             } else {
    509                 Log.v(TAG, "No good image Available.  Capturing next available good image.");
    510                 // If there was no good frame available in the ring buffer
    511                 // already, capture the next good image.
    512                 // TODO Disable the shutter button until this image is captured.
    513 
    514                 Flash flashMode = Flash.OFF;
    515 
    516                 if (flashMode == Flash.ON || flashMode == Flash.AUTO) {
    517                     // We must issue a request for a single capture using the
    518                     // flash, including an AE precapture trigger.
    519 
    520                     // The following sets up a sequence of events which will
    521                     // occur in reverse order to the associated method
    522                     // calls:
    523                     // 1. Send a request to trigger the Auto Exposure Precapture
    524                     // 2. Wait for the AE_STATE to leave the PRECAPTURE state,
    525                     // and then send a request for a single image, with the
    526                     // appropriate flash settings.
    527                     // 3. Capture the next appropriate image, which should be
    528                     // the one we requested in (2).
    529 
    530                     mCaptureManager.captureNextImage(new ImageCaptureTask(params, session),
    531                             singleCaptureConstraint);
    532 
    533                     mCaptureManager.addMetadataChangeListener(CaptureResult.CONTROL_AE_STATE,
    534                             new MetadataChangeListener() {
    535                             @Override
    536                                 public void onImageMetadataChange(Key<?> key, Object oldValue,
    537                                         Object newValue,
    538                                         CaptureResult result) {
    539                                     Log.v(TAG, "AE State Changed");
    540                                     if (oldValue.equals(Integer.valueOf(
    541                                             CaptureResult.CONTROL_AE_STATE_PRECAPTURE))) {
    542                                         mCaptureManager.removeMetadataChangeListener(key, this);
    543                                         sendSingleRequest(params);
    544                                         // TODO: Delay this until
    545                                         // onCaptureStarted().
    546                                         onShutterInvokeUI(params);
    547                                     }
    548                                 }
    549                             });
    550 
    551                     sendAutoExposureTriggerRequest(flashMode);
    552                 } else {
    553                     // We may get here if, for example, the auto focus is in the
    554                     // middle of a scan.
    555                     // If the flash is off, we should just wait for the next
    556                     // image that arrives. This will have minimal delay since we
    557                     // do not need to send a new capture request.
    558                     mCaptureManager.captureNextImage(new ImageCaptureTask(params, session),
    559                             zslConstraints);
    560                 }
    561             }
    562         } else {
    563             // TODO If we can't save a previous frame, create a new capture
    564             // request to do what we need (e.g. flash) and call
    565             // captureNextImage().
    566             throw new UnsupportedOperationException("Non-ZSL capture not yet supported");
    567         }
    568     }
    569 
    570     @Override
    571     public void startPreview(Surface previewSurface, CaptureReadyCallback listener) {
    572         mPreviewSurface = previewSurface;
    573         setupAsync(mPreviewSurface, listener);
    574     }
    575 
    576     @Override
    577     public void close() {
    578         if (mIsClosed) {
    579             Log.w(TAG, "Camera is already closed.");
    580             return;
    581         }
    582         try {
    583             mCaptureSession.stopRepeating();
    584         } catch (CameraAccessException e) {
    585             Log.e(TAG, "Could not abort captures in progress.");
    586         }
    587         mIsClosed = true;
    588         mCameraThread.quitSafely();
    589         mDevice.close();
    590         mCaptureManager.close();
    591         mCaptureImageReader.close();
    592     }
    593 
    594     public Size[] getSupportedPreviewSizes() {
    595         StreamConfigurationMap config =
    596                 mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    597         return Size.convert(config.getOutputSizes(sCaptureImageFormat));
    598     }
    599 
    600     public float getFullSizeAspectRatio() {
    601         return mFullSizeAspectRatio;
    602     }
    603 
    604     @Override
    605     public Facing getDirection() {
    606         return mDirection.getDirection();
    607    }
    608 
    609 
    610     private void savePicture(Image image, final PhotoCaptureParameters captureParams,
    611             CaptureSession session, CaptureResult result) {
    612         int heading = captureParams.heading;
    613         int degrees = CameraUtil.getJpegRotation(captureParams.orientation, mCharacteristics);
    614 
    615         ExifInterface exif = new ExifInterface();
    616         // TODO: Add more exif tags here.
    617 
    618         Size size = getImageSizeForOrientation(image.getWidth(), image.getHeight(),
    619                 degrees);
    620 
    621         exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_X_DIMENSION, size.getWidth()));
    622         exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_Y_DIMENSION, size.getHeight()));
    623 
    624         exif.setTag(
    625                 exif.buildTag(ExifInterface.TAG_ORIENTATION, ExifInterface.Orientation.TOP_LEFT));
    626 
    627         // Set GPS heading direction based on sensor, if location is on.
    628         if (heading >= 0) {
    629             ExifTag directionRefTag = exif.buildTag(ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
    630                     ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
    631             ExifTag directionTag =
    632                     exif.buildTag(ExifInterface.TAG_GPS_IMG_DIRECTION, new Rational(heading, 1));
    633             exif.setTag(directionRefTag);
    634             exif.setTag(directionTag);
    635         }
    636         new ExifUtil(exif).populateExif(Optional.<TaskImageContainer.TaskImage>absent(),
    637                 Optional.of((CaptureResultProxy) new AndroidCaptureResultProxy(result)),
    638                 Optional.<Location>absent());
    639         ListenableFuture<Optional<Uri>> futureUri = session.saveAndFinish(
    640                 acquireJpegBytes(image, degrees),
    641                 size.getWidth(), size.getHeight(), 0, exif);
    642         Futures.addCallback(futureUri, new FutureCallback<Optional<Uri>>() {
    643             @Override
    644             public void onSuccess(Optional<Uri> uriOptional) {
    645                 captureParams.callback.onPictureSaved(uriOptional.orNull());
    646             }
    647 
    648             @Override
    649             public void onFailure(Throwable throwable) {
    650                 captureParams.callback.onPictureSaved(null);
    651             }
    652         });
    653     }
    654 
    655     /**
    656      * Asynchronously sets up the capture session.
    657      *
    658      * @param previewSurface the surface onto which the preview should be
    659      *            rendered.
    660      * @param listener called when setup is completed.
    661      */
    662     private void setupAsync(final Surface previewSurface, final CaptureReadyCallback listener) {
    663         mCameraHandler.post(new Runnable() {
    664                 @Override
    665             public void run() {
    666                 setup(previewSurface, listener);
    667             }
    668         });
    669     }
    670 
    671     /**
    672      * Configures and attempts to create a capture session.
    673      *
    674      * @param previewSurface the surface onto which the preview should be
    675      *            rendered.
    676      * @param listener called when the setup is completed.
    677      */
    678     private void setup(Surface previewSurface, final CaptureReadyCallback listener) {
    679         try {
    680             if (mCaptureSession != null) {
    681                 mCaptureSession.abortCaptures();
    682                 mCaptureSession = null;
    683             }
    684             List<Surface> outputSurfaces = new ArrayList<Surface>(2);
    685             outputSurfaces.add(previewSurface);
    686             outputSurfaces.add(mCaptureImageReader.getSurface());
    687 
    688             mDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
    689                     @Override
    690                 public void onConfigureFailed(CameraCaptureSession session) {
    691                     listener.onSetupFailed();
    692                 }
    693 
    694                     @Override
    695                 public void onConfigured(CameraCaptureSession session) {
    696                     mCaptureSession = session;
    697                     mAFRegions = ZERO_WEIGHT_3A_REGION;
    698                     mAERegions = ZERO_WEIGHT_3A_REGION;
    699                     mZoomValue = 1f;
    700                     mCropRegion = cropRegionForZoom(mZoomValue);
    701                     boolean success = sendRepeatingCaptureRequest();
    702                     if (success) {
    703                         mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS,
    704                                 true);
    705                         mReadyStateManager.notifyListeners();
    706                         listener.onReadyForCapture();
    707                     } else {
    708                         listener.onSetupFailed();
    709                     }
    710                 }
    711 
    712                     @Override
    713                 public void onClosed(CameraCaptureSession session) {
    714                     super.onClosed(session);
    715                 }
    716             }, mCameraHandler);
    717         } catch (CameraAccessException ex) {
    718             Log.e(TAG, "Could not set up capture session", ex);
    719             listener.onSetupFailed();
    720         }
    721     }
    722 
    723     private void addRegionsToCaptureRequestBuilder(CaptureRequest.Builder builder) {
    724         builder.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions);
    725         builder.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions);
    726         builder.set(CaptureRequest.SCALER_CROP_REGION, mCropRegion);
    727     }
    728 
    729     private void addFlashToCaptureRequestBuilder(CaptureRequest.Builder builder, Flash flashMode) {
    730         switch (flashMode) {
    731             case ON:
    732                 builder.set(CaptureRequest.CONTROL_AE_MODE,
    733                         CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
    734                 builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE);
    735                 break;
    736             case OFF:
    737                 builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
    738                 builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
    739                 break;
    740             case AUTO:
    741                 builder.set(CaptureRequest.CONTROL_AE_MODE,
    742                         CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
    743                 break;
    744         }
    745     }
    746 
    747     /**
    748      * Request a stream of images.
    749      *
    750      * @return true if successful, false if there was an error submitting the
    751      *         capture request.
    752      */
    753     private boolean sendRepeatingCaptureRequest() {
    754         Log.v(TAG, "sendRepeatingCaptureRequest()");
    755         try {
    756             CaptureRequest.Builder builder;
    757             if (ZSL_ENABLED) {
    758                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
    759             } else {
    760                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    761             }
    762 
    763             builder.addTarget(mPreviewSurface);
    764 
    765             if (ZSL_ENABLED) {
    766                 builder.addTarget(mCaptureImageReader.getSurface());
    767             }
    768 
    769             builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
    770 
    771             builder.set(CaptureRequest.CONTROL_AF_MODE,
    772                     CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    773             builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
    774 
    775             builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
    776             builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
    777 
    778             addRegionsToCaptureRequestBuilder(builder);
    779 
    780             mCaptureSession.setRepeatingRequest(builder.build(), mCaptureManager, mCameraHandler);
    781             return true;
    782         } catch (CameraAccessException e) {
    783             if (ZSL_ENABLED) {
    784                 Log.v(TAG, "Could not execute zero-shutter-lag repeating request.", e);
    785             } else {
    786                 Log.v(TAG, "Could not execute preview request.", e);
    787             }
    788             return false;
    789         }
    790     }
    791 
    792     /**
    793      * Request a single image.
    794      *
    795      * @return true if successful, false if there was an error submitting the
    796      *         capture request.
    797      */
    798     private boolean sendSingleRequest(OneCamera.PhotoCaptureParameters params) {
    799         Log.v(TAG, "sendSingleRequest()");
    800         try {
    801             CaptureRequest.Builder builder;
    802             builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
    803 
    804             builder.addTarget(mPreviewSurface);
    805 
    806             // Always add this surface for single image capture requests.
    807             builder.addTarget(mCaptureImageReader.getSurface());
    808 
    809             builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
    810 
    811             Flash flashMode = Flash.OFF;
    812             addFlashToCaptureRequestBuilder(builder, flashMode);
    813             addRegionsToCaptureRequestBuilder(builder);
    814 
    815             builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
    816             builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
    817 
    818             // Tag this as a special request which should be saved.
    819             builder.setTag(RequestTag.EXPLICIT_CAPTURE);
    820 
    821             if (sCaptureImageFormat == ImageFormat.JPEG) {
    822                 builder.set(CaptureRequest.JPEG_QUALITY, (byte) (JPEG_QUALITY));
    823                 builder.set(CaptureRequest.JPEG_ORIENTATION,
    824                         CameraUtil.getJpegRotation(params.orientation, mCharacteristics));
    825             }
    826 
    827             mCaptureSession.capture(builder.build(), mCaptureManager, mCameraHandler);
    828             return true;
    829         } catch (CameraAccessException e) {
    830             Log.v(TAG, "Could not execute single still capture request.", e);
    831             return false;
    832         }
    833     }
    834 
    835     private boolean sendRepeatingBurstCaptureRequest() {
    836         Log.v(TAG, "sendRepeatingBurstCaptureRequest()");
    837         try {
    838             CaptureRequest.Builder builder;
    839             builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
    840             builder.addTarget(mPreviewSurface);
    841 
    842             if (ZSL_ENABLED) {
    843                 builder.addTarget(mCaptureImageReader.getSurface());
    844             }
    845 
    846             builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
    847             builder.set(CaptureRequest.CONTROL_AF_MODE,
    848                     CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
    849             builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
    850 
    851             builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
    852             builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
    853 
    854             addRegionsToCaptureRequestBuilder(builder);
    855 
    856             mCaptureSession.setRepeatingRequest(builder.build(), mCaptureManager, mCameraHandler);
    857             return true;
    858         } catch (CameraAccessException e) {
    859             Log.v(TAG, "Could not send repeating burst capture request.", e);
    860             return false;
    861         }
    862     }
    863 
    864     private boolean sendAutoExposureTriggerRequest(Flash flashMode) {
    865         Log.v(TAG, "sendAutoExposureTriggerRequest()");
    866         try {
    867             CaptureRequest.Builder builder;
    868             if (ZSL_ENABLED) {
    869                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
    870             } else {
    871                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    872             }
    873 
    874             builder.addTarget(mPreviewSurface);
    875 
    876             if (ZSL_ENABLED) {
    877                 builder.addTarget(mCaptureImageReader.getSurface());
    878             }
    879 
    880             builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
    881 
    882             builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
    883                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
    884 
    885             addRegionsToCaptureRequestBuilder(builder);
    886             addFlashToCaptureRequestBuilder(builder, flashMode);
    887 
    888             mCaptureSession.capture(builder.build(), mCaptureManager, mCameraHandler);
    889 
    890             return true;
    891         } catch (CameraAccessException e) {
    892             Log.v(TAG, "Could not execute auto exposure trigger request.", e);
    893             return false;
    894         }
    895     }
    896 
    897     /**
    898      */
    899     private boolean sendAutoFocusTriggerRequest() {
    900         Log.v(TAG, "sendAutoFocusTriggerRequest()");
    901         try {
    902             CaptureRequest.Builder builder;
    903             if (ZSL_ENABLED) {
    904                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
    905             } else {
    906                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    907             }
    908 
    909             builder.addTarget(mPreviewSurface);
    910 
    911             if (ZSL_ENABLED) {
    912                 builder.addTarget(mCaptureImageReader.getSurface());
    913             }
    914 
    915             builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
    916 
    917             addRegionsToCaptureRequestBuilder(builder);
    918 
    919             builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
    920             builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
    921 
    922             mCaptureSession.capture(builder.build(), mCaptureManager, mCameraHandler);
    923 
    924             return true;
    925         } catch (CameraAccessException e) {
    926             Log.v(TAG, "Could not execute auto focus trigger request.", e);
    927             return false;
    928         }
    929     }
    930 
    931     /**
    932      * Like {@link #sendRepeatingCaptureRequest()}, but with the focus held
    933      * constant.
    934      *
    935      * @return true if successful, false if there was an error submitting the
    936      *         capture request.
    937      */
    938     private boolean sendAutoFocusHoldRequest() {
    939         Log.v(TAG, "sendAutoFocusHoldRequest()");
    940         try {
    941             CaptureRequest.Builder builder;
    942             if (ZSL_ENABLED) {
    943                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
    944             } else {
    945                 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    946             }
    947 
    948             builder.addTarget(mPreviewSurface);
    949 
    950             if (ZSL_ENABLED) {
    951                 builder.addTarget(mCaptureImageReader.getSurface());
    952             }
    953 
    954             builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    955 
    956             builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
    957             builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
    958 
    959             addRegionsToCaptureRequestBuilder(builder);
    960             // TODO: This should fire the torch, if appropriate.
    961 
    962             mCaptureSession.setRepeatingRequest(builder.build(), mCaptureManager, mCameraHandler);
    963 
    964             return true;
    965         } catch (CameraAccessException e) {
    966             Log.v(TAG, "Could not execute auto focus hold request.", e);
    967             return false;
    968         }
    969     }
    970 
    971     /**
    972      * Calculate the aspect ratio of the full size capture on this device.
    973      *
    974      * @param characteristics the characteristics of the camera device.
    975      * @return The aspect ration, in terms of width/height of the full capture
    976      *         size.
    977      */
    978     private static float calculateFullSizeAspectRatio(CameraCharacteristics characteristics) {
    979         Rect activeArraySize =
    980                 characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    981         return ((float) activeArraySize.width()) / activeArraySize.height();
    982     }
    983 
    984     /**
    985      * @param originalWidth the width of the original image captured from the
    986      *            camera
    987      * @param originalHeight the height of the original image captured from the
    988      *            camera
    989      * @param orientation the rotation to apply, in degrees.
    990      * @return The size of the final rotated image
    991      */
    992     private Size getImageSizeForOrientation(int originalWidth, int originalHeight,
    993             int orientation) {
    994         if (orientation == 0 || orientation == 180) {
    995             return new Size(originalWidth, originalHeight);
    996         } else if (orientation == 90 || orientation == 270) {
    997             return new Size(originalHeight, originalWidth);
    998         } else {
    999             throw new InvalidParameterException("Orientation not supported.");
   1000         }
   1001     }
   1002 
   1003     /**
   1004      * Given an image reader, extracts the JPEG image bytes and then closes the
   1005      * reader.
   1006      *
   1007      * @param img the image from which to extract jpeg bytes or compress to
   1008      *            jpeg.
   1009      * @param degrees the angle to rotate the image clockwise, in degrees. Rotation is
   1010      *            only applied to YUV images.
   1011      * @return The bytes of the JPEG image. Newly allocated.
   1012      */
   1013     private byte[] acquireJpegBytes(Image img, int degrees) {
   1014         ByteBuffer buffer;
   1015 
   1016         if (img.getFormat() == ImageFormat.JPEG) {
   1017             Image.Plane plane0 = img.getPlanes()[0];
   1018             buffer = plane0.getBuffer();
   1019 
   1020             byte[] imageBytes = new byte[buffer.remaining()];
   1021             buffer.get(imageBytes);
   1022             buffer.rewind();
   1023             return imageBytes;
   1024         } else if (img.getFormat() == ImageFormat.YUV_420_888) {
   1025             buffer = mJpegByteBufferPool.acquire();
   1026             if (buffer == null) {
   1027                 buffer = ByteBuffer.allocateDirect(img.getWidth() * img.getHeight() * 3);
   1028             }
   1029 
   1030             int numBytes = JpegUtilNative.compressJpegFromYUV420Image(
   1031                     new AndroidImageProxy(img), buffer, JPEG_QUALITY,
   1032                     degrees);
   1033 
   1034             if (numBytes < 0) {
   1035                 throw new RuntimeException("Error compressing jpeg.");
   1036             }
   1037 
   1038             buffer.limit(numBytes);
   1039 
   1040             byte[] imageBytes = new byte[buffer.remaining()];
   1041             buffer.get(imageBytes);
   1042 
   1043             buffer.clear();
   1044             mJpegByteBufferPool.release(buffer);
   1045 
   1046             return imageBytes;
   1047         } else {
   1048             throw new RuntimeException("Unsupported image format.");
   1049         }
   1050     }
   1051 
   1052     private void startAFCycle() {
   1053         // Clean up any existing AF cycle's pending callbacks.
   1054         mCameraHandler.removeCallbacksAndMessages(FOCUS_RESUME_CALLBACK_TOKEN);
   1055 
   1056         // Send a single CONTROL_AF_TRIGGER_START capture request.
   1057         sendAutoFocusTriggerRequest();
   1058 
   1059         // Immediately send a request for a regular preview stream, but with
   1060         // CONTROL_AF_MODE_AUTO set so that the focus remains constant after the
   1061         // AF cycle completes.
   1062         sendAutoFocusHoldRequest();
   1063 
   1064         // Waits Settings3A.getFocusHoldMillis() milliseconds before sending
   1065         // a request for a regular preview stream to resume.
   1066         mCameraHandler.postAtTime(new Runnable() {
   1067                 @Override
   1068             public void run() {
   1069                 mAERegions = ZERO_WEIGHT_3A_REGION;
   1070                 mAFRegions = ZERO_WEIGHT_3A_REGION;
   1071                 sendRepeatingCaptureRequest();
   1072             }
   1073         }, FOCUS_RESUME_CALLBACK_TOKEN,
   1074                 SystemClock.uptimeMillis() + Settings3A.getFocusHoldMillis());
   1075     }
   1076 
   1077     /**
   1078      * @see com.android.camera.one.OneCamera#triggerFocusAndMeterAtPoint(float,
   1079      *      float)
   1080      */
   1081     @Override
   1082     public void triggerFocusAndMeterAtPoint(float nx, float ny) {
   1083         int sensorOrientation = mCharacteristics.get(
   1084             CameraCharacteristics.SENSOR_ORIENTATION);
   1085         mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
   1086         mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
   1087 
   1088         startAFCycle();
   1089     }
   1090 
   1091     @Override
   1092     public Size pickPreviewSize(Size pictureSize, Context context) {
   1093         if (pictureSize == null) {
   1094             // TODO The default should be selected by the caller, and
   1095             // pictureSize should never be null.
   1096             pictureSize = getDefaultPictureSize();
   1097         }
   1098         float pictureAspectRatio = pictureSize.getWidth() / (float) pictureSize.getHeight();
   1099         return CaptureModuleUtil.getOptimalPreviewSize(getSupportedPreviewSizes(),
   1100               pictureAspectRatio);
   1101     }
   1102 
   1103     @Override
   1104     public float getMaxZoom() {
   1105         return mCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
   1106     }
   1107 
   1108     @Override
   1109     public void setZoom(float zoom) {
   1110         mZoomValue = zoom;
   1111         mCropRegion = cropRegionForZoom(zoom);
   1112         sendRepeatingCaptureRequest();
   1113     }
   1114 
   1115     private Rect cropRegionForZoom(float zoom) {
   1116         return AutoFocusHelper.cropRegionForZoom(mCharacteristics, zoom);
   1117     }
   1118 }
   1119