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 
Camera2Actions(CameraDeviceKey id, CameraManager cameraManager, Executor backgroundExecutor, HandlerFactory handlerFactory, Logger.Factory logFactory)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
executeOpen(SingleDeviceOpenListener<CameraDevice> openListener, Lifetime deviceLifetime)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
executeClose(SingleDeviceCloseListener closeListener, CameraDevice device)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 
OpenCameraRunnable(CameraManager cameraManager, String cameraId, Handler handler, SingleDeviceOpenListener<CameraDevice> openListener, Logger logger)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
run()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 
CloseCameraRunnable(CameraDevice cameraDevice, SingleDeviceCloseListener closeListener, Logger logger)124         public CloseCameraRunnable(CameraDevice cameraDevice,
125               SingleDeviceCloseListener closeListener,
126               Logger logger) {
127             mCameraDevice = cameraDevice;
128             mCloseListener = closeListener;
129             mLogger = logger;
130         }
131 
132         @Override
run()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 
OpenCameraStateCallback(SingleDeviceOpenListener<CameraDevice> openListener, Logger logger)153         public OpenCameraStateCallback(SingleDeviceOpenListener<CameraDevice> openListener,
154               Logger logger) {
155             mOpenListener = openListener;
156             mLogger = logger;
157         }
158 
159         @Override
onOpened(CameraDevice cameraDevice)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
onClosed(CameraDevice cameraDevice)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
onDisconnected(CameraDevice cameraDevice)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
onError(CameraDevice cameraDevice, int errorId)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 
called()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