• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ex.camera2.portability;
18 
19 import android.annotation.TargetApi;
20 import android.content.Context;
21 import android.graphics.ImageFormat;
22 import android.graphics.Matrix;
23 import android.graphics.Rect;
24 import android.graphics.RectF;
25 import android.graphics.SurfaceTexture;
26 import android.hardware.camera2.CameraAccessException;
27 import android.hardware.camera2.CameraCaptureSession;
28 import android.hardware.camera2.CameraCharacteristics;
29 import android.hardware.camera2.CameraDevice;
30 import android.hardware.camera2.CameraManager;
31 import android.hardware.camera2.CaptureFailure;
32 import android.hardware.camera2.CaptureRequest;
33 import android.hardware.camera2.CaptureResult;
34 import android.hardware.camera2.TotalCaptureResult;
35 import android.hardware.camera2.params.MeteringRectangle;
36 import android.media.Image;
37 import android.media.ImageReader;
38 import android.media.MediaActionSound;
39 import android.os.Build;
40 import android.os.Handler;
41 import android.os.HandlerThread;
42 import android.os.Looper;
43 import android.os.Message;
44 import android.view.Surface;
45 
46 import com.android.ex.camera2.portability.debug.Log;
47 import com.android.ex.camera2.utils.Camera2RequestSettingsSet;
48 
49 import java.nio.ByteBuffer;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashSet;
53 import java.util.List;
54 import java.util.Set;
55 
56 /**
57  * A class to implement {@link CameraAgent} of the Android camera2 framework.
58  */
59 class AndroidCamera2AgentImpl extends CameraAgent {
60     private static final Log.Tag TAG = new Log.Tag("AndCam2AgntImp");
61 
62     private final Camera2Handler mCameraHandler;
63     private final HandlerThread mCameraHandlerThread;
64     private final CameraStateHolder mCameraState;
65     private final DispatchThread mDispatchThread;
66     private final CameraManager mCameraManager;
67     private final MediaActionSound mNoisemaker;
68     private CameraExceptionHandler mExceptionHandler;
69 
70     /**
71      * Number of camera devices.  The length of {@code mCameraDevices} does not reveal this
72      * information because that list may contain since-invalidated indices.
73      */
74     private int mNumCameraDevices;
75 
76     /**
77      * Transformation between integral camera indices and the {@link java.lang.String} indices used
78      * by the underlying API.  Note that devices may disappear because they've been disconnected or
79      * have otherwise gone offline.  Because we need to keep the meanings of whatever indices we
80      * expose stable, we cannot simply remove them in such a case; instead, we insert {@code null}s
81      * to invalidate any such indices.  Whenever new devices appear, they are appended to the end of
82      * the list, and thereby assigned the lowest index that has never yet been used.
83      */
84     private final List<String> mCameraDevices;
85 
AndroidCamera2AgentImpl(Context context)86     AndroidCamera2AgentImpl(Context context) {
87         mCameraHandlerThread = new HandlerThread("Camera2 Handler Thread");
88         mCameraHandlerThread.start();
89         mCameraHandler = new Camera2Handler(mCameraHandlerThread.getLooper());
90         mExceptionHandler = new CameraExceptionHandler(mCameraHandler);
91         mCameraState = new AndroidCamera2StateHolder();
92         mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
93         mDispatchThread.start();
94         mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
95         mNoisemaker = new MediaActionSound();
96         mNoisemaker.load(MediaActionSound.SHUTTER_CLICK);
97 
98         mNumCameraDevices = 0;
99         mCameraDevices = new ArrayList<String>();
100         updateCameraDevices();
101     }
102 
103     /**
104      * Updates the camera device index assignments stored in {@link mCameraDevices}, without
105      * reappropriating any currently-assigned index.
106      * @return Whether the operation was successful
107      */
updateCameraDevices()108     private boolean updateCameraDevices() {
109         try {
110             String[] currentCameraDevices = mCameraManager.getCameraIdList();
111             Set<String> currentSet = new HashSet<String>(Arrays.asList(currentCameraDevices));
112 
113             // Invalidate the indices assigned to any camera devices that are no longer present
114             for (int index = 0; index < mCameraDevices.size(); ++index) {
115                 if (!currentSet.contains(mCameraDevices.get(index))) {
116                     mCameraDevices.set(index, null);
117                     --mNumCameraDevices;
118                 }
119             }
120 
121             // Assign fresh indices to any new camera devices
122             currentSet.removeAll(mCameraDevices); // The devices we didn't know about
123             for (String device : currentCameraDevices) {
124                 if (currentSet.contains(device)) {
125                     mCameraDevices.add(device);
126                     ++mNumCameraDevices;
127                 }
128             }
129 
130             return true;
131         } catch (CameraAccessException ex) {
132             Log.e(TAG, "Could not get device listing from camera subsystem", ex);
133             return false;
134         }
135     }
136 
137     // TODO: Implement
138     @Override
recycle()139     public void recycle() {}
140 
141     // TODO: Some indices may now be invalid; ensure everyone can handle that and update the docs
142     @Override
getCameraDeviceInfo()143     public CameraDeviceInfo getCameraDeviceInfo() {
144         updateCameraDevices();
145         return new AndroidCamera2DeviceInfo(mCameraManager, mCameraDevices.toArray(new String[0]),
146                 mNumCameraDevices);
147     }
148 
149     @Override
getCameraHandler()150     protected Handler getCameraHandler() {
151         return mCameraHandler;
152     }
153 
154     @Override
getDispatchThread()155     protected DispatchThread getDispatchThread() {
156         return mDispatchThread;
157     }
158 
159     @Override
getCameraState()160     protected CameraStateHolder getCameraState() {
161         return mCameraState;
162     }
163 
164     @Override
getCameraExceptionHandler()165     protected CameraExceptionHandler getCameraExceptionHandler() {
166         return mExceptionHandler;
167     }
168 
169     @Override
setCameraExceptionHandler(CameraExceptionHandler exceptionHandler)170     public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) {
171         mExceptionHandler = exceptionHandler;
172     }
173 
174     private static abstract class CaptureAvailableListener
175             extends CameraCaptureSession.CaptureCallback
176             implements ImageReader.OnImageAvailableListener {};
177 
178     private class Camera2Handler extends HistoryHandler {
179         // Caller-provided when leaving CAMERA_UNOPENED state:
180         private CameraOpenCallback mOpenCallback;
181         private int mCameraIndex;
182         private String mCameraId;
183         private int mCancelAfPending = 0;
184 
185         // Available in CAMERA_UNCONFIGURED state and above:
186         private CameraDevice mCamera;
187         private AndroidCamera2ProxyImpl mCameraProxy;
188         private Camera2RequestSettingsSet mPersistentSettings;
189         private Rect mActiveArray;
190         private boolean mLegacyDevice;
191 
192         // Available in CAMERA_CONFIGURED state and above:
193         private Size mPreviewSize;
194         private Size mPhotoSize;
195 
196         // Available in PREVIEW_READY state and above:
197         private SurfaceTexture mPreviewTexture;
198         private Surface mPreviewSurface;
199         private CameraCaptureSession mSession;
200         private ImageReader mCaptureReader;
201 
202         // Available from the beginning of PREVIEW_ACTIVE until the first preview frame arrives:
203         private CameraStartPreviewCallback mOneshotPreviewingCallback;
204 
205         // Available in FOCUS_LOCKED between AF trigger receipt and whenever the lens stops moving:
206         private CameraAFCallback mOneshotAfCallback;
207 
208         // Available when taking picture between AE trigger receipt and autoexposure convergence
209         private CaptureAvailableListener mOneshotCaptureCallback;
210 
211         // Available whenever setAutoFocusMoveCallback() was last invoked with a non-null argument:
212         private CameraAFMoveCallback mPassiveAfCallback;
213 
214         // Gets reset on every state change
215         private int mCurrentAeState = CaptureResult.CONTROL_AE_STATE_INACTIVE;
216 
Camera2Handler(Looper looper)217         Camera2Handler(Looper looper) {
218             super(looper);
219         }
220 
221         @Override
handleMessage(final Message msg)222         public void handleMessage(final Message msg) {
223             super.handleMessage(msg);
224             Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'");
225             int cameraAction = msg.what;
226             try {
227                 switch (cameraAction) {
228                     case CameraActions.OPEN_CAMERA:
229                     case CameraActions.RECONNECT: {
230                         CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
231                         int cameraIndex = msg.arg1;
232 
233                         if (mCameraState.getState() > AndroidCamera2StateHolder.CAMERA_UNOPENED) {
234                             openCallback.onDeviceOpenedAlready(cameraIndex,
235                                     generateHistoryString(cameraIndex));
236                             break;
237                         }
238 
239                         mOpenCallback = openCallback;
240                         mCameraIndex = cameraIndex;
241                         mCameraId = mCameraDevices.get(mCameraIndex);
242                         Log.i(TAG, String.format("Opening camera index %d (id %s) with camera2 API",
243                                 cameraIndex, mCameraId));
244 
245                         if (mCameraId == null) {
246                             mOpenCallback.onCameraDisabled(msg.arg1);
247                             break;
248                         }
249                         mCameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, this);
250 
251                         break;
252                     }
253 
254                     case CameraActions.RELEASE: {
255                         if (mCameraState.getState() == AndroidCamera2StateHolder.CAMERA_UNOPENED) {
256                             Log.w(TAG, "Ignoring release at inappropriate time");
257                             break;
258                         }
259 
260                         if (mSession != null) {
261                             closePreviewSession();
262                             mSession = null;
263                         }
264                         if (mCamera != null) {
265                             mCamera.close();
266                             mCamera = null;
267                         }
268                         mCameraProxy = null;
269                         mPersistentSettings = null;
270                         mActiveArray = null;
271                         if (mPreviewSurface != null) {
272                             mPreviewSurface.release();
273                             mPreviewSurface = null;
274                         }
275                         mPreviewTexture = null;
276                         if (mCaptureReader != null) {
277                             mCaptureReader.close();
278                             mCaptureReader = null;
279                         }
280                         mPreviewSize = null;
281                         mPhotoSize = null;
282                         mCameraIndex = 0;
283                         mCameraId = null;
284                         changeState(AndroidCamera2StateHolder.CAMERA_UNOPENED);
285                         break;
286                     }
287 
288                     /*case CameraActions.UNLOCK: {
289                         break;
290                     }
291 
292                     case CameraActions.LOCK: {
293                         break;
294                     }*/
295 
296                     case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: {
297                         setPreviewTexture((SurfaceTexture) msg.obj);
298                         break;
299                     }
300 
301                     case CameraActions.START_PREVIEW_ASYNC: {
302                         if (mCameraState.getState() !=
303                                         AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) {
304                             // TODO: Provide better feedback here?
305                             Log.w(TAG, "Refusing to start preview at inappropriate time");
306                             break;
307                         }
308 
309                         mOneshotPreviewingCallback = (CameraStartPreviewCallback) msg.obj;
310                         changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
311                         try {
312                             mSession.setRepeatingRequest(
313                                     mPersistentSettings.createRequest(mCamera,
314                                             CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface),
315                                     /*listener*/mCameraResultStateCallback, /*handler*/this);
316                         } catch(CameraAccessException ex) {
317                             Log.w(TAG, "Unable to start preview", ex);
318                             changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
319                         }
320                         break;
321                     }
322 
323                     // FIXME: We need to tear down the CameraCaptureSession here
324                     // (and unlock the CameraSettings object from our
325                     // CameraProxy) so that the preview/photo sizes can be
326                     // changed again while no preview is running.
327                     case CameraActions.STOP_PREVIEW: {
328                         if (mCameraState.getState() <
329                                         AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
330                             Log.w(TAG, "Refusing to stop preview at inappropriate time");
331                             break;
332                         }
333 
334                         mSession.stopRepeating();
335                         changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
336                         break;
337                     }
338 
339                     /*case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: {
340                         break;
341                     }
342 
343                     case CameraActions.ADD_CALLBACK_BUFFER: {
344                         break;
345                     }
346 
347                     case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: {
348                         break;
349                     }
350 
351                     case CameraActions.SET_PREVIEW_CALLBACK: {
352                         break;
353                     }
354 
355                     case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: {
356                         break;
357                     }
358 
359                     case CameraActions.SET_PARAMETERS: {
360                         break;
361                     }
362 
363                     case CameraActions.GET_PARAMETERS: {
364                         break;
365                     }
366 
367                     case CameraActions.REFRESH_PARAMETERS: {
368                         break;
369                     }*/
370 
371                     case CameraActions.APPLY_SETTINGS: {
372                         AndroidCamera2Settings settings = (AndroidCamera2Settings) msg.obj;
373                         applyToRequest(settings);
374                         break;
375                     }
376 
377                     case CameraActions.AUTO_FOCUS: {
378                         if (mCancelAfPending > 0) {
379                             Log.v(TAG, "handleMessage - Ignored AUTO_FOCUS because there was "
380                                     + mCancelAfPending + " pending CANCEL_AUTO_FOCUS messages");
381                             break; // ignore AF because a CANCEL_AF is queued after this
382                         }
383                         // We only support locking the focus while a preview is being displayed.
384                         // However, it can be requested multiple times in succession; the effect of
385                         // the subsequent invocations is determined by the focus mode defined in the
386                         // provided CameraSettings object. In passive (CONTINUOUS_*) mode, the
387                         // duplicate requests are no-ops and leave the lens locked at its current
388                         // position, but in active (AUTO) mode, they perform another scan and lock
389                         // once that is finished. In any manual focus mode, this call is a no-op,
390                         // and most notably, this is the only case where the callback isn't invoked.
391                         if (mCameraState.getState() <
392                                         AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
393                             Log.w(TAG, "Ignoring attempt to autofocus without preview");
394                             break;
395                         }
396 
397                         // The earliest we can reliably tell whether the autofocus has locked in
398                         // response to our latest request is when our one-time capture progresses.
399                         // However, it will probably take longer than that, so once that happens,
400                         // just start checking the repeating preview requests as they complete.
401                         final CameraAFCallback callback = (CameraAFCallback) msg.obj;
402                         CameraCaptureSession.CaptureCallback deferredCallbackSetter =
403                                 new CameraCaptureSession.CaptureCallback() {
404                             private boolean mAlreadyDispatched = false;
405 
406                             @Override
407                             public void onCaptureProgressed(CameraCaptureSession session,
408                                                             CaptureRequest request,
409                                                             CaptureResult result) {
410                                 checkAfState(result);
411                             }
412 
413                             @Override
414                             public void onCaptureCompleted(CameraCaptureSession session,
415                                                            CaptureRequest request,
416                                                            TotalCaptureResult result) {
417                                 checkAfState(result);
418                             }
419 
420                             private void checkAfState(CaptureResult result) {
421                                 if (result.get(CaptureResult.CONTROL_AF_STATE) != null &&
422                                         !mAlreadyDispatched) {
423                                     // Now our mCameraResultStateCallback will invoke the callback
424                                     // the first time it finds the focus motor to be locked.
425                                     mAlreadyDispatched = true;
426                                     mOneshotAfCallback = callback;
427                                     // This is an optimization: check the AF state of this frame
428                                     // instead of simply waiting for the next.
429                                     mCameraResultStateCallback.monitorControlStates(result);
430                                 }
431                             }
432 
433                             @Override
434                             public void onCaptureFailed(CameraCaptureSession session,
435                                                         CaptureRequest request,
436                                                         CaptureFailure failure) {
437                                 Log.e(TAG, "Focusing failed with reason " + failure.getReason());
438                                 callback.onAutoFocus(false, mCameraProxy);
439                             }};
440 
441                         // Send a one-time capture to trigger the camera driver to lock focus.
442                         changeState(AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
443                         Camera2RequestSettingsSet trigger =
444                                 new Camera2RequestSettingsSet(mPersistentSettings);
445                         trigger.set(CaptureRequest.CONTROL_AF_TRIGGER,
446                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
447                         try {
448                             mSession.capture(
449                                     trigger.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
450                                             mPreviewSurface),
451                                     /*listener*/deferredCallbackSetter, /*handler*/ this);
452                         } catch(CameraAccessException ex) {
453                             Log.e(TAG, "Unable to lock autofocus", ex);
454                             changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
455                         }
456                         break;
457                     }
458 
459                     case CameraActions.CANCEL_AUTO_FOCUS: {
460                         // Ignore all AFs that were already queued until we see
461                         // a CANCEL_AUTO_FOCUS_FINISH
462                         mCancelAfPending++;
463                         // Why would you want to unlock the lens if it isn't already locked?
464                         if (mCameraState.getState() <
465                                 AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
466                             Log.w(TAG, "Ignoring attempt to release focus lock without preview");
467                             break;
468                         }
469 
470                         // Send a one-time capture to trigger the camera driver to resume scanning.
471                         changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
472                         Camera2RequestSettingsSet cancel =
473                                 new Camera2RequestSettingsSet(mPersistentSettings);
474                         cancel.set(CaptureRequest.CONTROL_AF_TRIGGER,
475                                 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
476                         try {
477                             mSession.capture(
478                                     cancel.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
479                                             mPreviewSurface),
480                                     /*listener*/null, /*handler*/this);
481                         } catch(CameraAccessException ex) {
482                             Log.e(TAG, "Unable to cancel autofocus", ex);
483                             changeState(AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
484                         }
485                         break;
486                     }
487 
488                     case CameraActions.CANCEL_AUTO_FOCUS_FINISH: {
489                         // Stop ignoring AUTO_FOCUS messages unless there are additional
490                         // CANCEL_AUTO_FOCUSes that were added
491                         mCancelAfPending--;
492                         break;
493                     }
494 
495                     case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: {
496                         mPassiveAfCallback = (CameraAFMoveCallback) msg.obj;
497                         break;
498                     }
499 
500                     /*case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
501                         break;
502                     }
503 
504                     case CameraActions.SET_FACE_DETECTION_LISTENER: {
505                         break;
506                     }
507 
508                     case CameraActions.START_FACE_DETECTION: {
509                         break;
510                     }
511 
512                     case CameraActions.STOP_FACE_DETECTION: {
513                         break;
514                     }
515 
516                     case CameraActions.SET_ERROR_CALLBACK: {
517                         break;
518                     }
519 
520                     case CameraActions.ENABLE_SHUTTER_SOUND: {
521                         break;
522                     }*/
523 
524                     case CameraActions.SET_DISPLAY_ORIENTATION: {
525                         // Only set the JPEG capture orientation if requested to do so; otherwise,
526                         // capture in the sensor's physical orientation. (e.g., JPEG rotation is
527                         // necessary in auto-rotate mode.
528                         mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg2 > 0 ?
529                                 mCameraProxy.getCharacteristics().getJpegOrientation(msg.arg1) : 0);
530                         break;
531                     }
532 
533                     case CameraActions.SET_JPEG_ORIENTATION: {
534                         mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg1);
535                         break;
536                     }
537 
538                     case CameraActions.CAPTURE_PHOTO: {
539                         if (mCameraState.getState() <
540                                         AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
541                             Log.e(TAG, "Photos may only be taken when a preview is active");
542                             break;
543                         }
544                         if (mCameraState.getState() !=
545                                 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED) {
546                             Log.w(TAG, "Taking a (likely blurry) photo without the lens locked");
547                         }
548 
549                         final CaptureAvailableListener listener =
550                                 (CaptureAvailableListener) msg.obj;
551                         if (mLegacyDevice ||
552                                 (mCurrentAeState == CaptureResult.CONTROL_AE_STATE_CONVERGED &&
553                                 !mPersistentSettings.matches(CaptureRequest.CONTROL_AE_MODE,
554                                         CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH) &&
555                                 !mPersistentSettings.matches(CaptureRequest.FLASH_MODE,
556                                         CaptureRequest.FLASH_MODE_SINGLE)))
557                                 {
558                             // Legacy devices don't support the precapture state keys and instead
559                             // perform autoexposure convergence automatically upon capture.
560 
561                             // On other devices, as long as it has already converged, it determined
562                             // that flash was not required, and we're not going to invalidate the
563                             // current exposure levels by forcing the force on, we can save
564                             // significant capture time by not forcing a recalculation.
565                             Log.i(TAG, "Skipping pre-capture autoexposure convergence");
566                             mCaptureReader.setOnImageAvailableListener(listener, /*handler*/this);
567                             try {
568                                 mSession.capture(
569                                         mPersistentSettings.createRequest(mCamera,
570                                                 CameraDevice.TEMPLATE_STILL_CAPTURE,
571                                                 mCaptureReader.getSurface()),
572                                         listener, /*handler*/this);
573                             } catch (CameraAccessException ex) {
574                                 Log.e(TAG, "Unable to initiate immediate capture", ex);
575                             }
576                         } else {
577                             // We need to let AE converge before capturing. Once our one-time
578                             // trigger capture has made it into the pipeline, we'll start checking
579                             // for the completion of that convergence, capturing when that happens.
580                             Log.i(TAG, "Forcing pre-capture autoexposure convergence");
581                             CameraCaptureSession.CaptureCallback deferredCallbackSetter =
582                                     new CameraCaptureSession.CaptureCallback() {
583                                 private boolean mAlreadyDispatched = false;
584 
585                                 @Override
586                                 public void onCaptureProgressed(CameraCaptureSession session,
587                                                                 CaptureRequest request,
588                                                                 CaptureResult result) {
589                                     checkAeState(result);
590                                 }
591 
592                                 @Override
593                                 public void onCaptureCompleted(CameraCaptureSession session,
594                                                                CaptureRequest request,
595                                                                TotalCaptureResult result) {
596                                     checkAeState(result);
597                                 }
598 
599                                 private void checkAeState(CaptureResult result) {
600                                     if (result.get(CaptureResult.CONTROL_AE_STATE) != null &&
601                                             !mAlreadyDispatched) {
602                                         // Now our mCameraResultStateCallback will invoke the
603                                         // callback once the autoexposure routine has converged.
604                                         mAlreadyDispatched = true;
605                                         mOneshotCaptureCallback = listener;
606                                         // This is an optimization: check the AE state of this frame
607                                         // instead of simply waiting for the next.
608                                         mCameraResultStateCallback.monitorControlStates(result);
609                                     }
610                                 }
611 
612                                 @Override
613                                 public void onCaptureFailed(CameraCaptureSession session,
614                                                             CaptureRequest request,
615                                                             CaptureFailure failure) {
616                                     Log.e(TAG, "Autoexposure and capture failed with reason " +
617                                             failure.getReason());
618                                     // TODO: Make an error callback?
619                                 }};
620 
621                             // Set a one-time capture to trigger the camera driver's autoexposure:
622                             Camera2RequestSettingsSet expose =
623                                     new Camera2RequestSettingsSet(mPersistentSettings);
624                             expose.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
625                                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
626                             try {
627                                 mSession.capture(
628                                         expose.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
629                                                 mPreviewSurface),
630                                         /*listener*/deferredCallbackSetter, /*handler*/this);
631                             } catch (CameraAccessException ex) {
632                                 Log.e(TAG, "Unable to run autoexposure and perform capture", ex);
633                             }
634                         }
635                         break;
636                     }
637 
638                     default: {
639                         // TODO: Rephrase once everything has been implemented
640                         throw new RuntimeException("Unimplemented CameraProxy message=" + msg.what);
641                     }
642                 }
643             } catch (final Exception ex) {
644                 if (cameraAction != CameraActions.RELEASE && mCamera != null) {
645                     // TODO: Handle this better
646                     mCamera.close();
647                     mCamera = null;
648                 } else if (mCamera == null) {
649                     if (cameraAction == CameraActions.OPEN_CAMERA) {
650                         if (mOpenCallback != null) {
651                             mOpenCallback.onDeviceOpenFailure(mCameraIndex,
652                                     generateHistoryString(mCameraIndex));
653                         }
654                     } else {
655                         Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null");
656                     }
657                     return;
658                 }
659 
660                 if (ex instanceof RuntimeException) {
661                     String commandHistory = generateHistoryString(Integer.parseInt(mCameraId));
662                     mExceptionHandler.onCameraException((RuntimeException) ex, commandHistory,
663                             cameraAction, mCameraState.getState());
664                 }
665             } finally {
666                 WaitDoneBundle.unblockSyncWaiters(msg);
667             }
668         }
669 
buildSettings(AndroidCamera2Capabilities caps)670         public CameraSettings buildSettings(AndroidCamera2Capabilities caps) {
671             try {
672                 return new AndroidCamera2Settings(mCamera, CameraDevice.TEMPLATE_PREVIEW,
673                         mActiveArray, mPreviewSize, mPhotoSize);
674             } catch (CameraAccessException ex) {
675                 Log.e(TAG, "Unable to query camera device to build settings representation");
676                 return null;
677             }
678         }
679 
680         /**
681          * Simply propagates settings from provided {@link CameraSettings}
682          * object to our {@link CaptureRequest.Builder} for use in captures.
683          * <p>Most conversions to match the API 2 formats are performed by
684          * {@link AndroidCamera2Capabilities.IntegralStringifier}; otherwise
685          * any final adjustments are done here before updating the builder.</p>
686          *
687          * @param settings The new/updated settings
688          */
applyToRequest(AndroidCamera2Settings settings)689         private void applyToRequest(AndroidCamera2Settings settings) {
690             // TODO: If invoked when in PREVIEW_READY state, a new preview size will not take effect
691 
692             mPersistentSettings.union(settings.getRequestSettings());
693             mPreviewSize = settings.getCurrentPreviewSize();
694             mPhotoSize = settings.getCurrentPhotoSize();
695 
696             if (mCameraState.getState() >= AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
697                 // If we're already previewing, reflect most settings immediately
698                 try {
699                     mSession.setRepeatingRequest(
700                             mPersistentSettings.createRequest(mCamera,
701                                     CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface),
702                             /*listener*/mCameraResultStateCallback, /*handler*/this);
703                 } catch (CameraAccessException ex) {
704                     Log.e(TAG, "Failed to apply updated request settings", ex);
705                 }
706             } else if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) {
707                 // If we're already ready to preview, this doesn't regress our state
708                 changeState(AndroidCamera2StateHolder.CAMERA_CONFIGURED);
709             }
710         }
711 
setPreviewTexture(SurfaceTexture surfaceTexture)712         private void setPreviewTexture(SurfaceTexture surfaceTexture) {
713             // TODO: Must be called after providing a .*Settings populated with sizes
714             // TODO: We don't technically offer a selection of sizes tailored to SurfaceTextures!
715 
716             // TODO: Handle this error condition with a callback or exception
717             if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_CONFIGURED) {
718                 Log.w(TAG, "Ignoring texture setting at inappropriate time");
719                 return;
720             }
721 
722             // Avoid initializing another capture session unless we absolutely have to
723             if (surfaceTexture == mPreviewTexture) {
724                 Log.i(TAG, "Optimizing out redundant preview texture setting");
725                 return;
726             }
727 
728             if (mSession != null) {
729                 closePreviewSession();
730             }
731 
732             mPreviewTexture = surfaceTexture;
733             surfaceTexture.setDefaultBufferSize(mPreviewSize.width(), mPreviewSize.height());
734 
735             if (mPreviewSurface != null) {
736                 mPreviewSurface.release();
737             }
738             mPreviewSurface = new Surface(surfaceTexture);
739 
740             if (mCaptureReader != null) {
741                 mCaptureReader.close();
742             }
743             mCaptureReader = ImageReader.newInstance(
744                     mPhotoSize.width(), mPhotoSize.height(), ImageFormat.JPEG, 1);
745 
746             try {
747                 mCamera.createCaptureSession(
748                         Arrays.asList(mPreviewSurface, mCaptureReader.getSurface()),
749                         mCameraPreviewStateCallback, this);
750             } catch (CameraAccessException ex) {
751                 Log.e(TAG, "Failed to create camera capture session", ex);
752             }
753         }
754 
closePreviewSession()755         private void closePreviewSession() {
756             try {
757                 mSession.abortCaptures();
758                 mSession = null;
759             } catch (CameraAccessException ex) {
760                 Log.e(TAG, "Failed to close existing camera capture session", ex);
761             }
762             changeState(AndroidCamera2StateHolder.CAMERA_CONFIGURED);
763         }
764 
changeState(int newState)765         private void changeState(int newState) {
766             if (mCameraState.getState() != newState) {
767                 mCameraState.setState(newState);
768                 if (newState < AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
769                     mCurrentAeState = CaptureResult.CONTROL_AE_STATE_INACTIVE;
770                     mCameraResultStateCallback.resetState();
771                 }
772             }
773         }
774 
775         // This callback monitors our connection to and disconnection from camera devices.
776         private CameraDevice.StateCallback mCameraDeviceStateCallback =
777                 new CameraDevice.StateCallback() {
778             @Override
779             public void onOpened(CameraDevice camera) {
780                 mCamera = camera;
781                 if (mOpenCallback != null) {
782                     try {
783                         CameraCharacteristics props =
784                                 mCameraManager.getCameraCharacteristics(mCameraId);
785                         CameraDeviceInfo.Characteristics characteristics =
786                                 getCameraDeviceInfo().getCharacteristics(mCameraIndex);
787                         mCameraProxy = new AndroidCamera2ProxyImpl(AndroidCamera2AgentImpl.this,
788                                 mCameraIndex, mCamera, characteristics, props);
789                         mPersistentSettings = new Camera2RequestSettingsSet();
790                         mActiveArray =
791                                 props.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
792                         mLegacyDevice =
793                                 props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
794                                         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
795                         changeState(AndroidCamera2StateHolder.CAMERA_UNCONFIGURED);
796                         mOpenCallback.onCameraOpened(mCameraProxy);
797                     } catch (CameraAccessException ex) {
798                         mOpenCallback.onDeviceOpenFailure(mCameraIndex,
799                                 generateHistoryString(mCameraIndex));
800                     }
801                 }
802             }
803 
804             @Override
805             public void onDisconnected(CameraDevice camera) {
806                 Log.w(TAG, "Camera device '" + mCameraIndex + "' was disconnected");
807             }
808 
809             @Override
810             public void onError(CameraDevice camera, int error) {
811                 Log.e(TAG, "Camera device '" + mCameraIndex + "' encountered error code '" +
812                         error + '\'');
813                 if (mOpenCallback != null) {
814                     mOpenCallback.onDeviceOpenFailure(mCameraIndex,
815                             generateHistoryString(mCameraIndex));
816                 }
817             }};
818 
819         // This callback monitors our camera session (i.e. our transition into and out of preview).
820         private CameraCaptureSession.StateCallback mCameraPreviewStateCallback =
821                 new CameraCaptureSession.StateCallback() {
822             @Override
823             public void onConfigured(CameraCaptureSession session) {
824                 mSession = session;
825                 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
826             }
827 
828             @Override
829             public void onConfigureFailed(CameraCaptureSession session) {
830                 // TODO: Invoke a callback
831                 Log.e(TAG, "Failed to configure the camera for capture");
832             }
833 
834             @Override
835             public void onActive(CameraCaptureSession session) {
836                 if (mOneshotPreviewingCallback != null) {
837                     // The session is up and processing preview requests. Inform the caller.
838                     mOneshotPreviewingCallback.onPreviewStarted();
839                     mOneshotPreviewingCallback = null;
840                 }
841             }};
842 
843         private abstract class CameraResultStateCallback
844                 extends CameraCaptureSession.CaptureCallback {
monitorControlStates(CaptureResult result)845             public abstract void monitorControlStates(CaptureResult result);
846 
resetState()847             public abstract void resetState();
848         }
849 
850         // This callback monitors requested captures and notifies any relevant callbacks.
851         private CameraResultStateCallback mCameraResultStateCallback =
852                 new CameraResultStateCallback() {
853             private int mLastAfState = -1;
854             private long mLastAfFrameNumber = -1;
855             private long mLastAeFrameNumber = -1;
856 
857             @Override
858             public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
859                                             CaptureResult result) {
860                 monitorControlStates(result);
861             }
862 
863             @Override
864             public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
865                                            TotalCaptureResult result) {
866                 monitorControlStates(result);
867             }
868 
869             @Override
870             public void monitorControlStates(CaptureResult result) {
871                 Integer afStateMaybe = result.get(CaptureResult.CONTROL_AF_STATE);
872                 if (afStateMaybe != null) {
873                     int afState = afStateMaybe;
874                     // Since we handle both partial and total results for multiple frames here, we
875                     // might get the final callbacks for an earlier frame after receiving one or
876                     // more that correspond to the next one. To prevent our data from oscillating,
877                     // we never consider AF states that are older than the last one we've seen.
878                     if (result.getFrameNumber() > mLastAfFrameNumber) {
879                         boolean afStateChanged = afState != mLastAfState;
880                         mLastAfState = afState;
881                         mLastAfFrameNumber = result.getFrameNumber();
882 
883                         switch (afState) {
884                             case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN:
885                             case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED:
886                             case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: {
887                                 if (afStateChanged && mPassiveAfCallback != null) {
888                                     // A CameraAFMoveCallback is attached. If we just started to
889                                     // scan, the motor is moving; otherwise, it has settled.
890                                     mPassiveAfCallback.onAutoFocusMoving(
891                                             afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN,
892                                             mCameraProxy);
893                                 }
894                                 break;
895                             }
896 
897                             case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
898                             case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: {
899                                 // This check must be made regardless of whether the focus state has
900                                 // changed recently to avoid infinite waiting during autoFocus()
901                                 // when the algorithm has already either converged or failed to.
902                                 if (mOneshotAfCallback != null) {
903                                     // A call to autoFocus() was just made to request a focus lock.
904                                     // Notify the caller that the lens is now indefinitely fixed,
905                                     // and report whether the image we're stuck with is in focus.
906                                     mOneshotAfCallback.onAutoFocus(
907                                             afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED,
908                                             mCameraProxy);
909                                     mOneshotAfCallback = null;
910                                 }
911                                 break;
912                             }
913                         }
914                     }
915                 }
916 
917                 Integer aeStateMaybe = result.get(CaptureResult.CONTROL_AE_STATE);
918                 if (aeStateMaybe != null) {
919                     int aeState = aeStateMaybe;
920                     // Since we handle both partial and total results for multiple frames here, we
921                     // might get the final callbacks for an earlier frame after receiving one or
922                     // more that correspond to the next one. To prevent our data from oscillating,
923                     // we never consider AE states that are older than the last one we've seen.
924                     if (result.getFrameNumber() > mLastAeFrameNumber) {
925                         mCurrentAeState = aeStateMaybe;
926                         mLastAeFrameNumber = result.getFrameNumber();
927 
928                         switch (aeState) {
929                             case CaptureResult.CONTROL_AE_STATE_CONVERGED:
930                             case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED:
931                             case CaptureResult.CONTROL_AE_STATE_LOCKED: {
932                                 // This check must be made regardless of whether the exposure state
933                                 // has changed recently to avoid infinite waiting during
934                                 // takePicture() when the algorithm has already converged.
935                                 if (mOneshotCaptureCallback != null) {
936                                     // A call to takePicture() was just made, and autoexposure
937                                     // converged so it's time to initiate the capture!
938                                     mCaptureReader.setOnImageAvailableListener(
939                                             /*listener*/mOneshotCaptureCallback,
940                                             /*handler*/Camera2Handler.this);
941                                     try {
942                                         mSession.capture(
943                                                 mPersistentSettings.createRequest(mCamera,
944                                                         CameraDevice.TEMPLATE_STILL_CAPTURE,
945                                                         mCaptureReader.getSurface()),
946                                                 /*callback*/mOneshotCaptureCallback,
947                                                 /*handler*/Camera2Handler.this);
948                                     } catch (CameraAccessException ex) {
949                                         Log.e(TAG, "Unable to initiate capture", ex);
950                                     } finally {
951                                         mOneshotCaptureCallback = null;
952                                     }
953                                 }
954                                 break;
955                             }
956                         }
957                     }
958                 }
959             }
960 
961             @Override
962             public void resetState() {
963                 mLastAfState = -1;
964                 mLastAfFrameNumber = -1;
965                 mLastAeFrameNumber = -1;
966             }
967 
968             @Override
969             public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
970                                         CaptureFailure failure) {
971                 Log.e(TAG, "Capture attempt failed with reason " + failure.getReason());
972             }};
973     }
974 
975     private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy {
976         private final AndroidCamera2AgentImpl mCameraAgent;
977         private final int mCameraIndex;
978         private final CameraDevice mCamera;
979         private final CameraDeviceInfo.Characteristics mCharacteristics;
980         private final AndroidCamera2Capabilities mCapabilities;
981         private CameraSettings mLastSettings;
982         private boolean mShutterSoundEnabled;
983 
AndroidCamera2ProxyImpl( AndroidCamera2AgentImpl agent, int cameraIndex, CameraDevice camera, CameraDeviceInfo.Characteristics characteristics, CameraCharacteristics properties)984         public AndroidCamera2ProxyImpl(
985                 AndroidCamera2AgentImpl agent,
986                 int cameraIndex,
987                 CameraDevice camera,
988                 CameraDeviceInfo.Characteristics characteristics,
989                 CameraCharacteristics properties) {
990             mCameraAgent = agent;
991             mCameraIndex = cameraIndex;
992             mCamera = camera;
993             mCharacteristics = characteristics;
994             mCapabilities = new AndroidCamera2Capabilities(properties);
995             mLastSettings = null;
996             mShutterSoundEnabled = true;
997         }
998 
999         // TODO: Implement
1000         @Override
getCamera()1001         public android.hardware.Camera getCamera() { return null; }
1002 
1003         @Override
getCameraId()1004         public int getCameraId() {
1005             return mCameraIndex;
1006         }
1007 
1008         @Override
getCharacteristics()1009         public CameraDeviceInfo.Characteristics getCharacteristics() {
1010             return mCharacteristics;
1011         }
1012 
1013         @Override
getCapabilities()1014         public CameraCapabilities getCapabilities() {
1015             return mCapabilities;
1016         }
1017 
getAgent()1018         public CameraAgent getAgent() {
1019             return mCameraAgent;
1020         }
1021 
getSpecializedCapabilities()1022         private AndroidCamera2Capabilities getSpecializedCapabilities() {
1023             return mCapabilities;
1024         }
1025 
1026         // FIXME: Unlock the sizes in stopPreview(), as per the corresponding
1027         // explanation on the STOP_PREVIEW case in the handler.
1028         @Override
setPreviewTexture(SurfaceTexture surfaceTexture)1029         public void setPreviewTexture(SurfaceTexture surfaceTexture) {
1030             // Once the Surface has been selected, we configure the session and
1031             // are no longer able to change the sizes.
1032             getSettings().setSizesLocked(true);
1033             super.setPreviewTexture(surfaceTexture);
1034         }
1035 
1036         // FIXME: Unlock the sizes in stopPreview(), as per the corresponding
1037         // explanation on the STOP_PREVIEW case in the handler.
1038         @Override
setPreviewTextureSync(SurfaceTexture surfaceTexture)1039         public void setPreviewTextureSync(SurfaceTexture surfaceTexture) {
1040             // Once the Surface has been selected, we configure the session and
1041             // are no longer able to change the sizes.
1042             getSettings().setSizesLocked(true);
1043             super.setPreviewTexture(surfaceTexture);
1044         }
1045 
1046         // TODO: Implement
1047         @Override
setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb)1048         public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {}
1049 
1050         // TODO: Implement
1051         @Override
setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb)1052         public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {}
1053 
1054         // TODO: Implement
1055         @Override
setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb)1056         public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb)
1057                 {}
1058 
1059         // TODO: Implement
addCallbackBuffer(final byte[] callbackBuffer)1060         public void addCallbackBuffer(final byte[] callbackBuffer) {}
1061 
1062         @Override
autoFocus(final Handler handler, final CameraAFCallback cb)1063         public void autoFocus(final Handler handler, final CameraAFCallback cb) {
1064             try {
1065                 mDispatchThread.runJob(new Runnable() {
1066                     @Override
1067                     public void run() {
1068                         CameraAFCallback cbForward = null;
1069                         if (cb != null) {
1070                             cbForward = new CameraAFCallback() {
1071                                 @Override
1072                                 public void onAutoFocus(final boolean focused,
1073                                                         final CameraProxy camera) {
1074                                     handler.post(new Runnable() {
1075                                         @Override
1076                                         public void run() {
1077                                             cb.onAutoFocus(focused, camera);
1078                                         }
1079                                     });
1080                                 }
1081                             };
1082                         }
1083 
1084                         mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE |
1085                                 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
1086                         mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward)
1087                                 .sendToTarget();
1088                     }
1089                 });
1090             } catch (RuntimeException ex) {
1091                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
1092             }
1093         }
1094 
1095         @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1096         @Override
setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb)1097         public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) {
1098             try {
1099                 mDispatchThread.runJob(new Runnable() {
1100                     @Override
1101                     public void run() {
1102                         CameraAFMoveCallback cbForward = null;
1103                         if (cb != null) {
1104                             cbForward = new CameraAFMoveCallback() {
1105                                 @Override
1106                                 public void onAutoFocusMoving(final boolean moving,
1107                                                               final CameraProxy camera) {
1108                                     handler.post(new Runnable() {
1109                                         @Override
1110                                         public void run() {
1111                                             cb.onAutoFocusMoving(moving, camera);
1112                                         }
1113                                     });
1114                                 }
1115                             };
1116                         }
1117 
1118                         mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
1119                                 cbForward).sendToTarget();
1120                     }
1121                 });
1122             } catch (RuntimeException ex) {
1123                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
1124             }
1125         }
1126 
1127         @Override
takePicture(final Handler handler, final CameraShutterCallback shutter, CameraPictureCallback raw, CameraPictureCallback postview, final CameraPictureCallback jpeg)1128         public void takePicture(final Handler handler,
1129                                 final CameraShutterCallback shutter,
1130                                 CameraPictureCallback raw,
1131                                 CameraPictureCallback postview,
1132                                 final CameraPictureCallback jpeg) {
1133             // TODO: We never call raw or postview
1134             final CaptureAvailableListener picListener =
1135                     new CaptureAvailableListener() {
1136                 @Override
1137                 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
1138                                              long timestamp, long frameNumber) {
1139                     if (shutter != null) {
1140                         handler.post(new Runnable() {
1141                             @Override
1142                             public void run() {
1143                                 if (mShutterSoundEnabled) {
1144                                     mNoisemaker.play(MediaActionSound.SHUTTER_CLICK);
1145                                 }
1146                                 shutter.onShutter(AndroidCamera2ProxyImpl.this);
1147                             }});
1148                     }
1149                 }
1150 
1151                 @Override
1152                 public void onImageAvailable(ImageReader reader) {
1153                     try (Image image = reader.acquireNextImage()) {
1154                         if (jpeg != null) {
1155                             ByteBuffer buffer = image.getPlanes()[0].getBuffer();
1156                             final byte[] pixels = new byte[buffer.remaining()];
1157                             buffer.get(pixels);
1158                             handler.post(new Runnable() {
1159                                 @Override
1160                                 public void run() {
1161                                     jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this);
1162                                 }});
1163                         }
1164                     }
1165                 }};
1166             try {
1167                 mDispatchThread.runJob(new Runnable() {
1168                     @Override
1169                     public void run() {
1170                         // Wait until PREVIEW_ACTIVE or better
1171                         mCameraState.waitForStates(
1172                                 ~(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE - 1));
1173                         mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener)
1174                                 .sendToTarget();
1175                     }
1176                 });
1177             } catch (RuntimeException ex) {
1178                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
1179             }
1180         }
1181 
1182         // TODO: Implement
1183         @Override
setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener)1184         public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {}
1185 
1186         // TODO: Implement
1187         @Override
setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback)1188         public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback)
1189                 {}
1190 
1191         // TODO: Remove this method override once we handle this message
1192         @Override
startFaceDetection()1193         public void startFaceDetection() {}
1194 
1195         // TODO: Remove this method override once we handle this message
1196         @Override
stopFaceDetection()1197         public void stopFaceDetection() {}
1198 
1199         // TODO: Implement
1200         @Override
setParameters(android.hardware.Camera.Parameters params)1201         public void setParameters(android.hardware.Camera.Parameters params) {}
1202 
1203         // TODO: Implement
1204         @Override
getParameters()1205         public android.hardware.Camera.Parameters getParameters() { return null; }
1206 
1207         @Override
getSettings()1208         public CameraSettings getSettings() {
1209             if (mLastSettings == null) {
1210                 mLastSettings = mCameraHandler.buildSettings(mCapabilities);
1211             }
1212             return mLastSettings;
1213         }
1214 
1215         @Override
applySettings(CameraSettings settings)1216         public boolean applySettings(CameraSettings settings) {
1217             if (settings == null) {
1218                 Log.w(TAG, "null parameters in applySettings()");
1219                 return false;
1220             }
1221             if (!(settings instanceof AndroidCamera2Settings)) {
1222                 Log.e(TAG, "Provided settings not compatible with the backing framework API");
1223                 return false;
1224             }
1225 
1226             // Wait for any state that isn't OPENED
1227             if (applySettingsHelper(settings, ~AndroidCamera2StateHolder.CAMERA_UNOPENED)) {
1228                 mLastSettings = settings;
1229                 return true;
1230             }
1231             return false;
1232         }
1233 
1234         @Override
enableShutterSound(boolean enable)1235         public void enableShutterSound(boolean enable) {
1236             mShutterSoundEnabled = enable;
1237         }
1238 
1239         // TODO: Implement
1240         @Override
dumpDeviceSettings()1241         public String dumpDeviceSettings() { return null; }
1242 
1243         @Override
getCameraHandler()1244         public Handler getCameraHandler() {
1245             return AndroidCamera2AgentImpl.this.getCameraHandler();
1246         }
1247 
1248         @Override
getDispatchThread()1249         public DispatchThread getDispatchThread() {
1250             return AndroidCamera2AgentImpl.this.getDispatchThread();
1251         }
1252 
1253         @Override
getCameraState()1254         public CameraStateHolder getCameraState() {
1255             return mCameraState;
1256         }
1257     }
1258 
1259     /** A linear state machine: each state entails all the states below it. */
1260     private static class AndroidCamera2StateHolder extends CameraStateHolder {
1261         // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() ->
1262         //             autoFocus() -> takePicture()
1263         // States are mutually exclusive, but must be separate bits so that they can be used with
1264         // the StateHolder#waitForStates() and StateHolder#waitToAvoidStates() methods.
1265         // Do not set the state to be a combination of these values!
1266         /* Camera states */
1267         /** No camera device is opened. */
1268         public static final int CAMERA_UNOPENED = 1 << 0;
1269         /** A camera is opened, but no settings have been provided. */
1270         public static final int CAMERA_UNCONFIGURED = 1 << 1;
1271         /** The open camera has been configured by providing it with settings. */
1272         public static final int CAMERA_CONFIGURED = 1 << 2;
1273         /** A capture session is ready to stream a preview, but still has no repeating request. */
1274         public static final int CAMERA_PREVIEW_READY = 1 << 3;
1275         /** A preview is currently being streamed. */
1276         public static final int CAMERA_PREVIEW_ACTIVE = 1 << 4;
1277         /** The lens is locked on a particular region. */
1278         public static final int CAMERA_FOCUS_LOCKED = 1 << 5;
1279 
AndroidCamera2StateHolder()1280         public AndroidCamera2StateHolder() {
1281             this(CAMERA_UNOPENED);
1282         }
1283 
AndroidCamera2StateHolder(int state)1284         public AndroidCamera2StateHolder(int state) {
1285             super(state);
1286         }
1287     }
1288 
1289     private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo {
1290         private final CameraManager mCameraManager;
1291         private final String[] mCameraIds;
1292         private final int mNumberOfCameras;
1293         private final int mFirstBackCameraId;
1294         private final int mFirstFrontCameraId;
1295 
AndroidCamera2DeviceInfo(CameraManager cameraManager, String[] cameraIds, int numberOfCameras)1296         public AndroidCamera2DeviceInfo(CameraManager cameraManager,
1297                                         String[] cameraIds, int numberOfCameras) {
1298             mCameraManager = cameraManager;
1299             mCameraIds = cameraIds;
1300             mNumberOfCameras = numberOfCameras;
1301 
1302             int firstBackId = NO_DEVICE;
1303             int firstFrontId = NO_DEVICE;
1304             for (int id = 0; id < cameraIds.length; ++id) {
1305                 try {
1306                     int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id])
1307                             .get(CameraCharacteristics.LENS_FACING);
1308                     if (firstBackId == NO_DEVICE &&
1309                             lensDirection == CameraCharacteristics.LENS_FACING_BACK) {
1310                         firstBackId = id;
1311                     }
1312                     if (firstFrontId == NO_DEVICE &&
1313                             lensDirection == CameraCharacteristics.LENS_FACING_FRONT) {
1314                         firstFrontId = id;
1315                     }
1316                 } catch (CameraAccessException ex) {
1317                     Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex);
1318                 }
1319             }
1320             mFirstBackCameraId = firstBackId;
1321             mFirstFrontCameraId = firstFrontId;
1322         }
1323 
1324         @Override
getCharacteristics(int cameraId)1325         public Characteristics getCharacteristics(int cameraId) {
1326             String actualId = mCameraIds[cameraId];
1327             try {
1328                 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId);
1329                 return new AndroidCharacteristics2(info);
1330             } catch (CameraAccessException ex) {
1331                 return null;
1332             }
1333         }
1334 
1335         @Override
getNumberOfCameras()1336         public int getNumberOfCameras() {
1337             return mNumberOfCameras;
1338         }
1339 
1340         @Override
getFirstBackCameraId()1341         public int getFirstBackCameraId() {
1342             return mFirstBackCameraId;
1343         }
1344 
1345         @Override
getFirstFrontCameraId()1346         public int getFirstFrontCameraId() {
1347             return mFirstFrontCameraId;
1348         }
1349 
1350         private static class AndroidCharacteristics2 extends Characteristics {
1351             private CameraCharacteristics mCameraInfo;
1352 
AndroidCharacteristics2(CameraCharacteristics cameraInfo)1353             AndroidCharacteristics2(CameraCharacteristics cameraInfo) {
1354                 mCameraInfo = cameraInfo;
1355             }
1356 
1357             @Override
isFacingBack()1358             public boolean isFacingBack() {
1359                 return mCameraInfo.get(CameraCharacteristics.LENS_FACING)
1360                         .equals(CameraCharacteristics.LENS_FACING_BACK);
1361             }
1362 
1363             @Override
isFacingFront()1364             public boolean isFacingFront() {
1365                 return mCameraInfo.get(CameraCharacteristics.LENS_FACING)
1366                         .equals(CameraCharacteristics.LENS_FACING_FRONT);
1367             }
1368 
1369             @Override
getSensorOrientation()1370             public int getSensorOrientation() {
1371                 return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION);
1372             }
1373 
1374             @Override
getPreviewTransform(int currentDisplayOrientation, RectF surfaceDimensions, RectF desiredBounds)1375             public Matrix getPreviewTransform(int currentDisplayOrientation,
1376                                               RectF surfaceDimensions,
1377                                               RectF desiredBounds) {
1378                 if (!orientationIsValid(currentDisplayOrientation)) {
1379                     return new Matrix();
1380                 }
1381 
1382                 // The system transparently transforms the image to fill the surface
1383                 // when the device is in its natural orientation. We rotate the
1384                 // coordinates of the rectangle's corners to be relative to the
1385                 // original image, instead of to the current screen orientation.
1386                 float[] surfacePolygon = rotate(convertRectToPoly(surfaceDimensions),
1387                         2 * currentDisplayOrientation / 90);
1388                 float[] desiredPolygon = convertRectToPoly(desiredBounds);
1389 
1390                 Matrix transform = new Matrix();
1391                 // Use polygons instead of rectangles so that rotation will be
1392                 // calculated, since that is not done by the new camera API.
1393                 transform.setPolyToPoly(surfacePolygon, 0, desiredPolygon, 0, 4);
1394                 return transform;
1395             }
1396 
1397             @Override
canDisableShutterSound()1398             public boolean canDisableShutterSound() {
1399                 return true;
1400             }
1401 
convertRectToPoly(RectF rf)1402             private static float[] convertRectToPoly(RectF rf) {
1403                 return new float[] {rf.left, rf.top, rf.right, rf.top,
1404                         rf.right, rf.bottom, rf.left, rf.bottom};
1405             }
1406 
rotate(float[] arr, int times)1407             private static float[] rotate(float[] arr, int times) {
1408                 if (times < 0) {
1409                     times = times % arr.length + arr.length;
1410                 }
1411 
1412                 float[] res = new float[arr.length];
1413                 for (int offset = 0; offset < arr.length; ++offset) {
1414                     res[offset] = arr[(times + offset) % arr.length];
1415                 }
1416                 return res;
1417             }
1418         }
1419     }
1420 }
1421