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");
      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.camera.one.v2;
     18 
     19 import android.content.Context;
     20 import android.graphics.ImageFormat;
     21 import android.graphics.Rect;
     22 import android.hardware.camera2.CameraAccessException;
     23 import android.hardware.camera2.CameraCaptureSession;
     24 import android.hardware.camera2.CameraCharacteristics;
     25 import android.hardware.camera2.CameraDevice;
     26 import android.hardware.camera2.CameraMetadata;
     27 import android.hardware.camera2.CaptureRequest;
     28 import android.hardware.camera2.CaptureResult;
     29 import android.hardware.camera2.TotalCaptureResult;
     30 import android.hardware.camera2.params.MeteringRectangle;
     31 import android.hardware.camera2.params.StreamConfigurationMap;
     32 import android.media.Image;
     33 import android.media.ImageReader;
     34 import android.net.Uri;
     35 import android.os.Handler;
     36 import android.os.HandlerThread;
     37 import android.os.SystemClock;
     38 import android.view.Surface;
     39 
     40 import com.android.camera.CaptureModuleUtil;
     41 import com.android.camera.Exif;
     42 import com.android.camera.app.MediaSaver.OnMediaSavedListener;
     43 import com.android.camera.debug.DebugPropertyHelper;
     44 import com.android.camera.debug.Log;
     45 import com.android.camera.debug.Log.Tag;
     46 import com.android.camera.exif.ExifInterface;
     47 import com.android.camera.exif.ExifTag;
     48 import com.android.camera.exif.Rational;
     49 import com.android.camera.one.AbstractOneCamera;
     50 import com.android.camera.one.OneCamera;
     51 import com.android.camera.one.Settings3A;
     52 import com.android.camera.session.CaptureSession;
     53 import com.android.camera.util.CameraUtil;
     54 import com.android.camera.util.CaptureDataSerializer;
     55 import com.android.camera.util.JpegUtilNative;
     56 import com.android.camera.util.Size;
     57 
     58 import java.io.File;
     59 import java.io.IOException;
     60 import java.nio.ByteBuffer;
     61 import java.util.ArrayList;
     62 import java.util.LinkedList;
     63 import java.util.List;
     64 
     65 /**
     66  * {@link OneCamera} implementation directly on top of the Camera2 API.
     67  */
     68 public class OneCameraImpl extends AbstractOneCamera {
     69 
     70     /** Captures that are requested but haven't completed yet. */
     71     private static class InFlightCapture {
     72         final PhotoCaptureParameters parameters;
     73         final CaptureSession session;
     74 
     75         public InFlightCapture(PhotoCaptureParameters parameters,
     76                 CaptureSession session) {
     77             this.parameters = parameters;
     78             this.session = session;
     79         }
     80     }
     81 
     82     private static final Tag TAG = new Tag("OneCameraImpl2");
     83 
     84     /** If true, will write data about each capture request to disk. */
     85     private static final boolean DEBUG_WRITE_CAPTURE_DATA = DebugPropertyHelper.writeCaptureData();
     86     /** If true, will log per-frame AF info. */
     87     private static final boolean DEBUG_FOCUS_LOG = DebugPropertyHelper.showFrameDebugLog();
     88 
     89     /** Default JPEG encoding quality. */
     90     private static final Byte JPEG_QUALITY = 90;
     91 
     92     /**
     93      * Set to ImageFormat.JPEG, to use the hardware encoder, or
     94      * ImageFormat.YUV_420_888 to use the software encoder. No other image
     95      * formats are supported.
     96      */
     97     private static final int sCaptureImageFormat = ImageFormat.YUV_420_888;
     98 
     99     /** Duration to hold after manual focus tap. */
    100     private static final int FOCUS_HOLD_MILLIS = Settings3A.getFocusHoldMillis();
    101     /** Zero weight 3A region, to reset regions per API. */
    102     MeteringRectangle[] ZERO_WEIGHT_3A_REGION = AutoFocusHelper.getZeroWeightRegion();
    103 
    104     /**
    105      * CaptureRequest tags.
    106      * <ul>
    107      * <li>{@link #PRESHOT_TRIGGERED_AF}</li>
    108      * <li>{@link #CAPTURE}</li>
    109      * </ul>
    110      */
    111     public static enum RequestTag {
    112         /** Request that is part of a pre shot trigger. */
    113         PRESHOT_TRIGGERED_AF,
    114         /** Capture request (purely for logging). */
    115         CAPTURE,
    116         /** Tap to focus (purely for logging). */
    117         TAP_TO_FOCUS
    118     }
    119 
    120     /** Current CONTROL_AF_MODE request to Camera2 API. */
    121     private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
    122     /** Last OneCamera.AutoFocusState reported. */
    123     private AutoFocusState mLastResultAFState = AutoFocusState.INACTIVE;
    124     /** Flag to take a picture when the lens is stopped. */
    125     private boolean mTakePictureWhenLensIsStopped = false;
    126     /** Takes a (delayed) picture with appropriate parameters. */
    127     private Runnable mTakePictureRunnable;
    128     /** Keep PictureCallback for last requested capture. */
    129     private PictureCallback mLastPictureCallback = null;
    130     /** Last time takePicture() was called in uptimeMillis. */
    131     private long mTakePictureStartMillis;
    132     /** Runnable that returns to CONTROL_AF_MODE = AF_CONTINUOUS_PICTURE. */
    133     private final Runnable mReturnToContinuousAFRunnable = new Runnable() {
    134         @Override
    135         public void run() {
    136             mAFRegions = ZERO_WEIGHT_3A_REGION;
    137             mAERegions = ZERO_WEIGHT_3A_REGION;
    138             mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
    139             repeatingPreview(null);
    140         }
    141     };
    142 
    143     /** Current zoom value. 1.0 is no zoom. */
    144     private float mZoomValue = 1f;
    145     /** Current crop region: set from mZoomValue. */
    146     private Rect mCropRegion;
    147     /** Current AF and AE regions */
    148     private MeteringRectangle[] mAFRegions = ZERO_WEIGHT_3A_REGION;
    149     private MeteringRectangle[] mAERegions = ZERO_WEIGHT_3A_REGION;
    150     /** Last frame for which CONTROL_AF_STATE was received. */
    151     private long mLastControlAfStateFrameNumber = 0;
    152 
    153     /**
    154      * Common listener for preview frame metadata.
    155      */
    156     private final CameraCaptureSession.CaptureCallback mAutoFocusStateListener = new
    157             CameraCaptureSession.CaptureCallback() {
    158                 @Override
    159                 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
    160                                              long timestamp, long frameNumber) {
    161                     if (request.getTag() == RequestTag.CAPTURE && mLastPictureCallback != null) {
    162                         mLastPictureCallback.onQuickExpose();
    163                     }
    164                 }
    165 
    166                 // AF state information is sometimes available 1 frame before
    167                 // onCaptureCompleted(), so we take advantage of that.
    168                 @Override
    169                 public void onCaptureProgressed(CameraCaptureSession session,
    170                         CaptureRequest request,
    171                         CaptureResult partialResult) {
    172                     autofocusStateChangeDispatcher(partialResult);
    173                     super.onCaptureProgressed(session, request, partialResult);
    174                 }
    175 
    176                 @Override
    177                 public void onCaptureCompleted(CameraCaptureSession session,
    178                         CaptureRequest request,
    179                         TotalCaptureResult result) {
    180                     autofocusStateChangeDispatcher(result);
    181                     // This checks for a HAL implementation error where TotalCaptureResult
    182                     // is missing CONTROL_AF_STATE.  This should not happen.
    183                     if (result.get(CaptureResult.CONTROL_AF_STATE) == null) {
    184                         AutoFocusHelper.checkControlAfState(result);
    185                     }
    186                     if (DEBUG_FOCUS_LOG) {
    187                         AutoFocusHelper.logExtraFocusInfo(result);
    188                     }
    189                     super.onCaptureCompleted(session, request, result);
    190                 }
    191             };
    192     /** Thread on which the camera operations are running. */
    193     private final HandlerThread mCameraThread;
    194     /** Handler of the {@link #mCameraThread}. */
    195     private final Handler mCameraHandler;
    196     /** The characteristics of this camera. */
    197     private final CameraCharacteristics mCharacteristics;
    198     /** The underlying Camera2 API camera device. */
    199     private final CameraDevice mDevice;
    200 
    201     /**
    202      * The aspect ratio (width/height) of the full resolution for this camera.
    203      * Usually the native aspect ratio of this camera.
    204      */
    205     private final float mFullSizeAspectRatio;
    206     /** The Camera2 API capture session currently active. */
    207     private CameraCaptureSession mCaptureSession;
    208     /** The surface onto which to render the preview. */
    209     private Surface mPreviewSurface;
    210     /**
    211      * A queue of capture requests that have been requested but are not done
    212      * yet.
    213      */
    214     private final LinkedList<InFlightCapture> mCaptureQueue =
    215             new LinkedList<InFlightCapture>();
    216     /** Whether closing of this device has been requested. */
    217     private volatile boolean mIsClosed = false;
    218     /** A callback that is called when the device is fully closed. */
    219     private CloseCallback mCloseCallback = null;
    220 
    221     /** Receives the normal captured images. */
    222     private final ImageReader mCaptureImageReader;
    223     ImageReader.OnImageAvailableListener mCaptureImageListener =
    224             new ImageReader.OnImageAvailableListener() {
    225                 @Override
    226                 public void onImageAvailable(ImageReader reader) {
    227                     InFlightCapture capture = mCaptureQueue.remove();
    228 
    229                     // Since this is not an HDR+ session, we will just save the
    230                     // result.
    231                     capture.session.startEmpty();
    232                     byte[] imageBytes = acquireJpegBytesAndClose(reader);
    233                     // TODO: The savePicture call here seems to block UI thread.
    234                     savePicture(imageBytes, capture.parameters, capture.session);
    235                     broadcastReadyState(true);
    236                     capture.parameters.callback.onPictureTaken(capture.session);
    237                 }
    238             };
    239 
    240     /**
    241      * Instantiates a new camera based on Camera 2 API.
    242      *
    243      * @param device The underlying Camera 2 device.
    244      * @param characteristics The device's characteristics.
    245      * @param pictureSize the size of the final image to be taken.
    246      */
    247     OneCameraImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize) {
    248         mDevice = device;
    249         mCharacteristics = characteristics;
    250         mFullSizeAspectRatio = calculateFullSizeAspectRatio(characteristics);
    251 
    252         mCameraThread = new HandlerThread("OneCamera2");
    253         mCameraThread.start();
    254         mCameraHandler = new Handler(mCameraThread.getLooper());
    255 
    256         mCaptureImageReader = ImageReader.newInstance(pictureSize.getWidth(),
    257                 pictureSize.getHeight(),
    258                 sCaptureImageFormat, 2);
    259         mCaptureImageReader.setOnImageAvailableListener(mCaptureImageListener, mCameraHandler);
    260         Log.d(TAG, "New Camera2 based OneCameraImpl created.");
    261     }
    262 
    263     /**
    264      * Take picture, initiating an auto focus scan if needed.
    265      */
    266     @Override
    267     public void takePicture(final PhotoCaptureParameters params, final CaptureSession session) {
    268         // Do not do anything when a picture is already requested.
    269         if (mTakePictureWhenLensIsStopped) {
    270             return;
    271         }
    272 
    273         // Not ready until the picture comes back.
    274         broadcastReadyState(false);
    275 
    276         mTakePictureRunnable = new Runnable() {
    277             @Override
    278             public void run() {
    279                 takePictureNow(params, session);
    280             }
    281         };
    282         mLastPictureCallback = params.callback;
    283         mTakePictureStartMillis = SystemClock.uptimeMillis();
    284 
    285         // This class implements a very simple version of AF, which
    286         // only delays capture if the lens is scanning.
    287         if (mLastResultAFState == AutoFocusState.ACTIVE_SCAN) {
    288             Log.v(TAG, "Waiting until scan is done before taking shot.");
    289             mTakePictureWhenLensIsStopped = true;
    290         } else {
    291             // We could do CONTROL_AF_TRIGGER_START and wait until lens locks,
    292             // but this would slow down the capture.
    293             takePictureNow(params, session);
    294         }
    295     }
    296 
    297     /**
    298      * Take picture immediately. Parameters passed through from takePicture().
    299      */
    300     public void takePictureNow(PhotoCaptureParameters params, CaptureSession session) {
    301         long dt = SystemClock.uptimeMillis() - mTakePictureStartMillis;
    302         Log.v(TAG, "Taking shot with extra AF delay of " + dt + " ms.");
    303         // This will throw a RuntimeException, if parameters are not sane.
    304         params.checkSanity();
    305         try {
    306             // JPEG capture.
    307             CaptureRequest.Builder builder = mDevice
    308                     .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
    309             builder.setTag(RequestTag.CAPTURE);
    310             addBaselineCaptureKeysToRequest(builder);
    311 
    312             if (sCaptureImageFormat == ImageFormat.JPEG) {
    313                 builder.set(CaptureRequest.JPEG_QUALITY, JPEG_QUALITY);
    314                 builder.set(CaptureRequest.JPEG_ORIENTATION,
    315                         CameraUtil.getJpegRotation(params.orientation, mCharacteristics));
    316             }
    317 
    318             builder.addTarget(mPreviewSurface);
    319             builder.addTarget(mCaptureImageReader.getSurface());
    320             CaptureRequest request = builder.build();
    321 
    322             if (DEBUG_WRITE_CAPTURE_DATA) {
    323                 final String debugDataDir = makeDebugDir(params.debugDataFolder,
    324                         "normal_capture_debug");
    325                 Log.i(TAG, "Writing capture data to: " + debugDataDir);
    326                 CaptureDataSerializer.toFile("Normal Capture", request, new File(debugDataDir,
    327                         "capture.txt"));
    328             }
    329 
    330             mCaptureSession.capture(request, mAutoFocusStateListener, mCameraHandler);
    331         } catch (CameraAccessException e) {
    332             Log.e(TAG, "Could not access camera for still image capture.");
    333             broadcastReadyState(true);
    334             params.callback.onPictureTakenFailed();
    335             return;
    336         }
    337         mCaptureQueue.add(new InFlightCapture(params, session));
    338     }
    339 
    340     @Override
    341     public void startPreview(Surface previewSurface, CaptureReadyCallback listener) {
    342         mPreviewSurface = previewSurface;
    343         setupAsync(mPreviewSurface, listener);
    344     }
    345 
    346     @Override
    347     public void setViewfinderSize(int width, int height) {
    348         throw new RuntimeException("Not implemented yet.");
    349     }
    350 
    351     @Override
    352     public boolean isFlashSupported(boolean enhanced) {
    353         throw new RuntimeException("Not implemented yet.");
    354     }
    355 
    356     @Override
    357     public boolean isSupportingEnhancedMode() {
    358         throw new RuntimeException("Not implemented yet.");
    359     }
    360 
    361     @Override
    362     public void close(CloseCallback closeCallback) {
    363         if (mIsClosed) {
    364             Log.w(TAG, "Camera is already closed.");
    365             return;
    366         }
    367         try {
    368             mCaptureSession.abortCaptures();
    369         } catch (CameraAccessException e) {
    370             Log.e(TAG, "Could not abort captures in progress.");
    371         }
    372         mIsClosed = true;
    373         mCloseCallback = closeCallback;
    374         mCameraThread.quitSafely();
    375         mDevice.close();
    376     }
    377 
    378     @Override
    379     public Size[] getSupportedSizes() {
    380         StreamConfigurationMap config = mCharacteristics
    381                 .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    382         return Size.convert(config.getOutputSizes(sCaptureImageFormat));
    383     }
    384 
    385     @Override
    386     public float getFullSizeAspectRatio() {
    387         return mFullSizeAspectRatio;
    388     }
    389 
    390     @Override
    391     public boolean isFrontFacing() {
    392         return mCharacteristics.get(CameraCharacteristics.LENS_FACING)
    393                 == CameraMetadata.LENS_FACING_FRONT;
    394     }
    395 
    396     @Override
    397     public boolean isBackFacing() {
    398         return mCharacteristics.get(CameraCharacteristics.LENS_FACING)
    399                 == CameraMetadata.LENS_FACING_BACK;
    400     }
    401 
    402     private void savePicture(byte[] jpegData, final PhotoCaptureParameters captureParams,
    403             CaptureSession session) {
    404         int heading = captureParams.heading;
    405         int width = 0;
    406         int height = 0;
    407         int rotation = 0;
    408         ExifInterface exif = null;
    409         try {
    410             exif = new ExifInterface();
    411             exif.readExif(jpegData);
    412 
    413             Integer w = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
    414             width = (w == null) ? width : w;
    415             Integer h = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
    416             height = (h == null) ? height : h;
    417 
    418             // Get image rotation from EXIF.
    419             rotation = Exif.getOrientation(exif);
    420 
    421             // Set GPS heading direction based on sensor, if location is on.
    422             if (heading >= 0) {
    423                 ExifTag directionRefTag = exif.buildTag(
    424                         ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
    425                         ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
    426                 ExifTag directionTag = exif.buildTag(
    427                         ExifInterface.TAG_GPS_IMG_DIRECTION,
    428                         new Rational(heading, 1));
    429                 exif.setTag(directionRefTag);
    430                 exif.setTag(directionTag);
    431             }
    432         } catch (IOException e) {
    433             Log.w(TAG, "Could not read exif from gcam jpeg", e);
    434             exif = null;
    435         }
    436         session.saveAndFinish(jpegData, width, height, rotation, exif, new OnMediaSavedListener() {
    437             @Override
    438             public void onMediaSaved(Uri uri) {
    439                 captureParams.callback.onPictureSaved(uri);
    440             }
    441         });
    442     }
    443 
    444     /**
    445      * Asynchronously sets up the capture session.
    446      *
    447      * @param previewSurface the surface onto which the preview should be
    448      *            rendered.
    449      * @param listener called when setup is completed.
    450      */
    451     private void setupAsync(final Surface previewSurface, final CaptureReadyCallback listener) {
    452         mCameraHandler.post(new Runnable() {
    453             @Override
    454             public void run() {
    455                 setup(previewSurface, listener);
    456             }
    457         });
    458     }
    459 
    460     /**
    461      * Configures and attempts to create a capture session.
    462      *
    463      * @param previewSurface the surface onto which the preview should be
    464      *            rendered.
    465      * @param listener called when the setup is completed.
    466      */
    467     private void setup(Surface previewSurface, final CaptureReadyCallback listener) {
    468         try {
    469             if (mCaptureSession != null) {
    470                 mCaptureSession.abortCaptures();
    471                 mCaptureSession = null;
    472             }
    473             List<Surface> outputSurfaces = new ArrayList<Surface>(2);
    474             outputSurfaces.add(previewSurface);
    475             outputSurfaces.add(mCaptureImageReader.getSurface());
    476 
    477             mDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
    478 
    479                 @Override
    480                 public void onConfigureFailed(CameraCaptureSession session) {
    481                     listener.onSetupFailed();
    482                 }
    483 
    484                 @Override
    485                 public void onConfigured(CameraCaptureSession session) {
    486                     mCaptureSession = session;
    487                     mAFRegions = ZERO_WEIGHT_3A_REGION;
    488                     mAERegions = ZERO_WEIGHT_3A_REGION;
    489                     mZoomValue = 1f;
    490                     mCropRegion = cropRegionForZoom(mZoomValue);
    491                     boolean success = repeatingPreview(null);
    492                     if (success) {
    493                         listener.onReadyForCapture();
    494                     } else {
    495                         listener.onSetupFailed();
    496                     }
    497                 }
    498 
    499                 @Override
    500                 public void onClosed(CameraCaptureSession session) {
    501                     super.onClosed(session);
    502                     if (mCloseCallback != null) {
    503                         mCloseCallback.onCameraClosed();
    504                     }
    505                 }
    506             }, mCameraHandler);
    507         } catch (CameraAccessException ex) {
    508             Log.e(TAG, "Could not set up capture session", ex);
    509             listener.onSetupFailed();
    510         }
    511     }
    512 
    513     /**
    514      * Adds current regions to CaptureRequest and base AF mode + AF_TRIGGER_IDLE.
    515      *
    516      * @param builder Build for the CaptureRequest
    517      */
    518     private void addBaselineCaptureKeysToRequest(CaptureRequest.Builder builder) {
    519         builder.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions);
    520         builder.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions);
    521         builder.set(CaptureRequest.SCALER_CROP_REGION, mCropRegion);
    522         builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode);
    523         builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
    524         // Enable face detection
    525         builder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
    526                 CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL);
    527         builder.set(CaptureRequest.CONTROL_SCENE_MODE,
    528                 CaptureRequest.CONTROL_SCENE_MODE_FACE_PRIORITY);
    529     }
    530 
    531     /**
    532      * Request preview capture stream with AF_MODE_CONTINUOUS_PICTURE.
    533      *
    534      * @return true if request was build and sent successfully.
    535      * @param tag
    536      */
    537     private boolean repeatingPreview(Object tag) {
    538         try {
    539             CaptureRequest.Builder builder = mDevice.
    540                     createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    541             builder.addTarget(mPreviewSurface);
    542             builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    543             addBaselineCaptureKeysToRequest(builder);
    544             mCaptureSession.setRepeatingRequest(builder.build(), mAutoFocusStateListener,
    545                     mCameraHandler);
    546             Log.v(TAG, String.format("Sent repeating Preview request, zoom = %.2f", mZoomValue));
    547             return true;
    548         } catch (CameraAccessException ex) {
    549             Log.e(TAG, "Could not access camera setting up preview.", ex);
    550             return false;
    551         }
    552     }
    553 
    554     /**
    555      * Request preview capture stream with auto focus trigger cycle.
    556      */
    557     private void sendAutoFocusTriggerCaptureRequest(Object tag) {
    558         try {
    559             // Step 1: Request single frame CONTROL_AF_TRIGGER_START.
    560             CaptureRequest.Builder builder;
    561             builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    562             builder.addTarget(mPreviewSurface);
    563             builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    564             mControlAFMode = CameraMetadata.CONTROL_AF_MODE_AUTO;
    565             addBaselineCaptureKeysToRequest(builder);
    566             builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
    567             builder.setTag(tag);
    568             mCaptureSession.capture(builder.build(), mAutoFocusStateListener, mCameraHandler);
    569 
    570             // Step 2: Call repeatingPreview to update mControlAFMode.
    571             repeatingPreview(tag);
    572             resumeContinuousAFAfterDelay(FOCUS_HOLD_MILLIS);
    573         } catch (CameraAccessException ex) {
    574             Log.e(TAG, "Could not execute preview request.", ex);
    575         }
    576     }
    577 
    578     /**
    579      * Resume AF_MODE_CONTINUOUS_PICTURE after FOCUS_HOLD_MILLIS.
    580      */
    581     private void resumeContinuousAFAfterDelay(int millis) {
    582         mCameraHandler.removeCallbacks(mReturnToContinuousAFRunnable);
    583         mCameraHandler.postDelayed(mReturnToContinuousAFRunnable, millis);
    584     }
    585 
    586     /**
    587      * This method takes appropriate action if camera2 AF state changes.
    588      * <ol>
    589      * <li>Reports changes in camera2 AF state to OneCamera.FocusStateListener.</li>
    590      * <li>Take picture after AF scan if mTakePictureWhenLensIsStopped true.</li>
    591      * </ol>
    592      */
    593     private void autofocusStateChangeDispatcher(CaptureResult result) {
    594         if (result.getFrameNumber() < mLastControlAfStateFrameNumber ||
    595                 result.get(CaptureResult.CONTROL_AF_STATE) == null) {
    596             return;
    597         }
    598         mLastControlAfStateFrameNumber = result.getFrameNumber();
    599 
    600         // Convert to OneCamera mode and state.
    601         AutoFocusState resultAFState = AutoFocusHelper.
    602                 stateFromCamera2State(result.get(CaptureResult.CONTROL_AF_STATE));
    603 
    604         // TODO: Consider using LENS_STATE.
    605         boolean lensIsStopped = resultAFState == AutoFocusState.ACTIVE_FOCUSED ||
    606                 resultAFState == AutoFocusState.ACTIVE_UNFOCUSED ||
    607                 resultAFState == AutoFocusState.PASSIVE_FOCUSED ||
    608                 resultAFState == AutoFocusState.PASSIVE_UNFOCUSED;
    609 
    610         if (mTakePictureWhenLensIsStopped && lensIsStopped) {
    611             // Take the shot.
    612             mCameraHandler.post(mTakePictureRunnable);
    613             mTakePictureWhenLensIsStopped = false;
    614         }
    615 
    616         // Report state change when AF state has changed.
    617         if (resultAFState != mLastResultAFState && mFocusStateListener != null) {
    618             mFocusStateListener.onFocusStatusUpdate(resultAFState, result.getFrameNumber());
    619         }
    620         mLastResultAFState = resultAFState;
    621     }
    622 
    623     @Override
    624     public void triggerFocusAndMeterAtPoint(float nx, float ny) {
    625         int sensorOrientation = mCharacteristics.get(
    626             CameraCharacteristics.SENSOR_ORIENTATION);
    627         mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
    628         mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
    629 
    630         sendAutoFocusTriggerCaptureRequest(RequestTag.TAP_TO_FOCUS);
    631     }
    632 
    633     @Override
    634     public float getMaxZoom() {
    635         return mCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
    636     }
    637 
    638     @Override
    639     public void setZoom(float zoom) {
    640         mZoomValue = zoom;
    641         mCropRegion = cropRegionForZoom(zoom);
    642         repeatingPreview(null);
    643     }
    644 
    645     @Override
    646     public Size pickPreviewSize(Size pictureSize, Context context) {
    647         float pictureAspectRatio = pictureSize.getWidth() / (float) pictureSize.getHeight();
    648         return CaptureModuleUtil.getOptimalPreviewSize(context, getSupportedSizes(),
    649                 pictureAspectRatio);
    650     }
    651 
    652     private Rect cropRegionForZoom(float zoom) {
    653         return AutoFocusHelper.cropRegionForZoom(mCharacteristics, zoom);
    654     }
    655 
    656     /**
    657      * Calculate the aspect ratio of the full size capture on this device.
    658      *
    659      * @param characteristics the characteristics of the camera device.
    660      * @return The aspect ration, in terms of width/height of the full capture
    661      *         size.
    662      */
    663     private static float calculateFullSizeAspectRatio(CameraCharacteristics characteristics) {
    664         Rect activeArraySize =
    665                 characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    666         return ((float)(activeArraySize.width())) / activeArraySize.height();
    667     }
    668 
    669     /**
    670      * Given an image reader, extracts the JPEG image bytes and then closes the
    671      * reader.
    672      *
    673      * @param reader the reader to read the JPEG data from.
    674      * @return The bytes of the JPEG image. Newly allocated.
    675      */
    676     private static byte[] acquireJpegBytesAndClose(ImageReader reader) {
    677         Image img = reader.acquireLatestImage();
    678 
    679         ByteBuffer buffer;
    680 
    681         if (img.getFormat() == ImageFormat.JPEG) {
    682             Image.Plane plane0 = img.getPlanes()[0];
    683             buffer = plane0.getBuffer();
    684         } else if (img.getFormat() == ImageFormat.YUV_420_888) {
    685             buffer = ByteBuffer.allocateDirect(img.getWidth() * img.getHeight() * 3);
    686 
    687             Log.v(TAG, "Compressing JPEG with software encoder.");
    688             int numBytes = JpegUtilNative.compressJpegFromYUV420Image(img, buffer, JPEG_QUALITY);
    689 
    690             if (numBytes < 0) {
    691                 throw new RuntimeException("Error compressing jpeg.");
    692             }
    693 
    694             buffer.limit(numBytes);
    695         } else {
    696             throw new RuntimeException("Unsupported image format.");
    697         }
    698 
    699         byte[] imageBytes = new byte[buffer.remaining()];
    700         buffer.get(imageBytes);
    701         buffer.rewind();
    702         img.close();
    703         return imageBytes;
    704     }
    705 }
    706