Home | History | Annotate | Download | only in device
      1 /*
      2  * Copyright (C) 2015 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.device;
     18 
     19 import android.annotation.TargetApi;
     20 import android.hardware.camera2.CameraAccessException;
     21 import android.hardware.camera2.CameraDevice;
     22 import android.hardware.camera2.CameraManager;
     23 import android.os.Build.VERSION_CODES;
     24 import android.os.Handler;
     25 
     26 import com.android.camera.async.HandlerFactory;
     27 import com.android.camera.async.Lifetime;
     28 import com.android.camera.debug.Log.Tag;
     29 import com.android.camera.debug.Logger;
     30 
     31 import java.util.concurrent.Executor;
     32 
     33 import javax.annotation.ParametersAreNonnullByDefault;
     34 
     35 /**
     36  * Set of device actions for opening and closing a single Camera2 device.
     37  */
     38 @TargetApi(VERSION_CODES.LOLLIPOP)
     39 @ParametersAreNonnullByDefault
     40 public class Camera2Actions implements SingleDeviceActions<CameraDevice> {
     41     private static final Tag TAG = new Tag("Camera2Act");
     42 
     43     private final CameraDeviceKey mId;
     44     private final CameraManager mCameraManager;
     45     private final HandlerFactory mHandlerFactory;
     46     private final Executor mBackgroundExecutor;
     47     private final Logger mLogger;
     48 
     49     public Camera2Actions(CameraDeviceKey id,
     50           CameraManager cameraManager,
     51           Executor backgroundExecutor,
     52           HandlerFactory handlerFactory,
     53           Logger.Factory logFactory) {
     54         mId = id;
     55         mCameraManager = cameraManager;
     56         mBackgroundExecutor = backgroundExecutor;
     57         mHandlerFactory = handlerFactory;
     58         mLogger = logFactory.create(TAG);
     59         mLogger.d("Created Camera2Request");
     60     }
     61 
     62     @Override
     63     public void executeOpen(SingleDeviceOpenListener<CameraDevice> openListener,
     64           Lifetime deviceLifetime) throws UnsupportedOperationException {
     65         mLogger.i("executeOpen(id: " + mId.getCameraId() + ")");
     66         mBackgroundExecutor.execute(new OpenCameraRunnable(mCameraManager,
     67               mId.getCameraId().getValue(),
     68               // TODO THIS IS BAD. If there are multiple requests to open,
     69               // we don't want to add the handler to the lifetime until after
     70               // the camera device is opened or the camera could be opened with
     71               // an invalid thread.
     72               mHandlerFactory.create(deviceLifetime, "Camera2 Lifetime"),
     73               openListener, mLogger));
     74     }
     75 
     76     @Override
     77     public void executeClose(SingleDeviceCloseListener closeListener, CameraDevice device)
     78           throws UnsupportedOperationException {
     79         mLogger.i("executeClose(" + device.getId() + ")");
     80         mBackgroundExecutor.execute(new CloseCameraRunnable(device, closeListener, mLogger));
     81     }
     82 
     83     /**
     84      * Internal runnable that executes a CameraManager openCamera call.
     85      */
     86     private static class OpenCameraRunnable implements Runnable {
     87         private final SingleDeviceOpenListener<CameraDevice> mOpenListener;
     88         private final String mCameraId;
     89         private final Handler mHandler;
     90         private final CameraManager mCameraManager;
     91         private final Logger mLogger;
     92 
     93         public OpenCameraRunnable(CameraManager cameraManager, String cameraId,
     94               Handler handler, SingleDeviceOpenListener<CameraDevice> openListener,
     95               Logger logger) {
     96             mCameraManager = cameraManager;
     97             mCameraId = cameraId;
     98             mHandler = handler;
     99             mOpenListener = openListener;
    100             mLogger = logger;
    101         }
    102 
    103         @Override
    104         public void run() {
    105             try {
    106                 mLogger.i("mCameraManager.openCamera(id: " + mCameraId + ")");
    107                 mCameraManager.openCamera(mCameraId, new OpenCameraStateCallback(mOpenListener,
    108                             mLogger), mHandler);
    109             } catch (CameraAccessException | SecurityException | IllegalArgumentException e) {
    110                 mLogger.e("There was a problem opening camera " + mCameraId, e);
    111                 mOpenListener.onDeviceOpenException(e);
    112             }
    113         }
    114     }
    115 
    116     /**
    117      * Internal runnable that executes a close on a cameraDevice.
    118      */
    119     private static class CloseCameraRunnable implements Runnable {
    120         private final SingleDeviceCloseListener mCloseListener;
    121         private final CameraDevice mCameraDevice;
    122         private final Logger mLogger;
    123 
    124         public CloseCameraRunnable(CameraDevice cameraDevice,
    125               SingleDeviceCloseListener closeListener,
    126               Logger logger) {
    127             mCameraDevice = cameraDevice;
    128             mCloseListener = closeListener;
    129             mLogger = logger;
    130         }
    131 
    132         @Override
    133         public void run() {
    134             try {
    135                 mLogger.i("mCameraDevice.close(id: " + mCameraDevice.getId() + ")");
    136                 mCameraDevice.close();
    137                 mCloseListener.onDeviceClosed();
    138             } catch (Exception e) {
    139                 mLogger.e("Closing the camera produced an exception!", e);
    140                 mCloseListener.onDeviceClosingException(e);
    141             }
    142         }
    143     }
    144 
    145     /**
    146      * Internal callback that provides a camera device to a future.
    147      */
    148     private static class OpenCameraStateCallback extends CameraDevice.StateCallback {
    149         private final SingleDeviceOpenListener<CameraDevice> mOpenListener;
    150         private final Logger mLogger;
    151         private boolean mHasBeenCalled = false;
    152 
    153         public OpenCameraStateCallback(SingleDeviceOpenListener<CameraDevice> openListener,
    154               Logger logger) {
    155             mOpenListener = openListener;
    156             mLogger = logger;
    157         }
    158 
    159         @Override
    160         public void onOpened(CameraDevice cameraDevice) {
    161             if (!called()) {
    162                 mLogger.i("onOpened(id: " + cameraDevice.getId() + ")");
    163                 mOpenListener.onDeviceOpened(cameraDevice);
    164             }
    165         }
    166 
    167         @Override
    168         public void onClosed(CameraDevice cameraDevice) {
    169             if (!called()) {
    170                 mLogger.w("onClosed(id: " + cameraDevice.getId() + ")");
    171                 mOpenListener.onDeviceOpenException(cameraDevice);
    172             }
    173         }
    174 
    175         @Override
    176         public void onDisconnected(CameraDevice cameraDevice) {
    177             if (!called()) {
    178                 mLogger.w("onDisconnected(id: " + cameraDevice.getId() + ")");
    179                 mOpenListener.onDeviceOpenException(cameraDevice);
    180             }
    181         }
    182 
    183         @Override
    184         public void onError(CameraDevice cameraDevice, int errorId) {
    185             if (!called()) {
    186                 mLogger.e("onError(id: " + cameraDevice.getId()
    187                       + ", errorId: " + errorId + ")");
    188                 mOpenListener.onDeviceOpenException(new CameraOpenException(errorId));
    189             }
    190         }
    191 
    192         private boolean called() {
    193             boolean result = mHasBeenCalled;
    194             if (!mHasBeenCalled) {
    195                 mHasBeenCalled = true;
    196             } else {
    197                 mLogger.v("Callback was re-executed.");
    198             }
    199 
    200             return result;
    201         }
    202     }
    203 }
    204