1 /*
2  * Copyright (C) 2013 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.app;
18 
19 import android.content.Context;
20 import android.os.Handler;
21 
22 import com.android.camera.CameraDisabledException;
23 import com.android.camera.debug.Log;
24 import com.android.camera.device.ActiveCameraDeviceTracker;
25 import com.android.camera.device.CameraId;
26 import com.android.camera.util.CameraUtil;
27 import com.android.camera.util.GservicesHelper;
28 import com.android.ex.camera2.portability.CameraAgent;
29 import com.android.ex.camera2.portability.CameraDeviceInfo;
30 import com.android.ex.camera2.portability.CameraExceptionHandler;
31 
32 import javax.annotation.Nonnull;
33 import javax.annotation.Nullable;
34 
35 /**
36  * A class which implements {@link com.android.camera.app.CameraProvider} used
37  * by {@link com.android.camera.CameraActivity}.
38  * TODO: Make this class package private.
39  */
40 public class CameraController implements CameraAgent.CameraOpenCallback, CameraProvider {
41     private static final Log.Tag TAG = new Log.Tag("CameraController");
42     private static final int EMPTY_REQUEST = -1;
43     private final Context mContext;
44     private final Handler mCallbackHandler;
45     private final CameraAgent mCameraAgent;
46     private final CameraAgent mCameraAgentNg;
47     private final ActiveCameraDeviceTracker mActiveCameraDeviceTracker;
48 
49     private CameraAgent.CameraOpenCallback mCallbackReceiver;
50 
51     /** The one for the API that is currently in use (deprecated one by default). */
52     private CameraDeviceInfo mInfo;
53 
54     private CameraAgent.CameraProxy mCameraProxy;
55     private int mRequestingCameraId = EMPTY_REQUEST;
56 
57     /**
58      * Determines which of mCameraAgent and mCameraAgentNg is currently in use.
59      * <p>It's only possible to enable this if the new API is actually
60      * supported.</p>
61      */
62     private boolean mUsingNewApi = false;
63 
64     /**
65      * Constructor.
66      *
67      * @param context The {@link android.content.Context} used to check if the
68      *                camera is disabled.
69      * @param handler The {@link android.os.Handler} to post the camera
70      *                callbacks to.
71      * @param cameraManager Used for camera open/close.
72      * @param cameraManagerNg Used for camera open/close with the new API. If
73      *                        {@code null} or the same object as
74      *                        {@code cameraManager}, the new API will not be
75      *                        exposed and requests for it will get the old one.
76      * @param activeCameraDeviceTracker Tracks the active device across multiple
77      *                                  api versions and implementations.
78      */
CameraController(@onnull Context context, @Nullable CameraAgent.CameraOpenCallback callbackReceiver, @Nonnull Handler handler, @Nonnull CameraAgent cameraManager, @Nonnull CameraAgent cameraManagerNg, @Nonnull ActiveCameraDeviceTracker activeCameraDeviceTracker)79     public CameraController(@Nonnull Context context,
80           @Nullable CameraAgent.CameraOpenCallback callbackReceiver,
81           @Nonnull Handler handler,
82           @Nonnull CameraAgent cameraManager,
83           @Nonnull CameraAgent cameraManagerNg,
84           @Nonnull ActiveCameraDeviceTracker activeCameraDeviceTracker) {
85         mContext = context;
86         mCallbackReceiver = callbackReceiver;
87         mCallbackHandler = handler;
88         mCameraAgent = cameraManager;
89         // If the new implementation is the same as the old, the
90         // CameraAgentFactory decided this device doesn't support the new API.
91         mCameraAgentNg = cameraManagerNg != cameraManager ? cameraManagerNg : null;
92         mActiveCameraDeviceTracker = activeCameraDeviceTracker;
93         mInfo = mCameraAgent.getCameraDeviceInfo();
94         if (mInfo == null && mCallbackReceiver != null) {
95             mCallbackReceiver.onDeviceOpenFailure(-1, "GETTING_CAMERA_INFO");
96         }
97     }
98 
99     @Override
setCameraExceptionHandler(CameraExceptionHandler exceptionHandler)100     public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) {
101         mCameraAgent.setCameraExceptionHandler(exceptionHandler);
102         if (mCameraAgentNg != null) {
103             mCameraAgentNg.setCameraExceptionHandler(exceptionHandler);
104         }
105     }
106 
107     @Override
getCharacteristics(int cameraId)108     public CameraDeviceInfo.Characteristics getCharacteristics(int cameraId) {
109         if (mInfo == null) {
110             return null;
111         }
112         return mInfo.getCharacteristics(cameraId);
113     }
114 
115     @Override
116     @Deprecated
getCurrentCameraId()117     public CameraId getCurrentCameraId() {
118         return mActiveCameraDeviceTracker.getActiveOrPreviousCamera();
119     }
120 
121     @Override
getNumberOfCameras()122     public int getNumberOfCameras() {
123         if (mInfo == null) {
124             return 0;
125         }
126         return mInfo.getNumberOfCameras();
127     }
128 
129     @Override
getFirstBackCameraId()130     public int getFirstBackCameraId() {
131         if (mInfo == null) {
132             return -1;
133         }
134         return mInfo.getFirstBackCameraId();
135     }
136 
137     @Override
getFirstFrontCameraId()138     public int getFirstFrontCameraId() {
139         if (mInfo == null) {
140             return -1;
141         }
142         return mInfo.getFirstFrontCameraId();
143     }
144 
145     @Override
isFrontFacingCamera(int id)146     public boolean isFrontFacingCamera(int id) {
147         if (mInfo == null) {
148             return false;
149         }
150         if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
151             Log.e(TAG, "Camera info not available:" + id);
152             return false;
153         }
154         return mInfo.getCharacteristics(id).isFacingFront();
155     }
156 
157     @Override
isBackFacingCamera(int id)158     public boolean isBackFacingCamera(int id) {
159         if (mInfo == null) {
160             return false;
161         }
162         if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
163             Log.e(TAG, "Camera info not available:" + id);
164             return false;
165         }
166         return mInfo.getCharacteristics(id).isFacingBack();
167     }
168 
169     @Override
onCameraOpened(CameraAgent.CameraProxy camera)170     public void onCameraOpened(CameraAgent.CameraProxy camera) {
171         Log.v(TAG, "onCameraOpened");
172         if (mRequestingCameraId != camera.getCameraId()) {
173             return;
174         }
175         mCameraProxy = camera;
176         mRequestingCameraId = EMPTY_REQUEST;
177         if (mCallbackReceiver != null) {
178             mCallbackReceiver.onCameraOpened(camera);
179         }
180     }
181 
182     @Override
onCameraDisabled(int cameraId)183     public void onCameraDisabled(int cameraId) {
184         if (mCallbackReceiver != null) {
185             mCallbackReceiver.onCameraDisabled(cameraId);
186         }
187     }
188 
189     @Override
onDeviceOpenFailure(int cameraId, String info)190     public void onDeviceOpenFailure(int cameraId, String info) {
191         if (mCallbackReceiver != null) {
192             mCallbackReceiver.onDeviceOpenFailure(cameraId, info);
193         }
194     }
195 
196     @Override
onDeviceOpenedAlready(int cameraId, String info)197     public void onDeviceOpenedAlready(int cameraId, String info) {
198         if (mCallbackReceiver != null) {
199             mCallbackReceiver.onDeviceOpenedAlready(cameraId, info);
200         }
201     }
202 
203     @Override
onReconnectionFailure(CameraAgent mgr, String info)204     public void onReconnectionFailure(CameraAgent mgr, String info) {
205         if (mCallbackReceiver != null) {
206             mCallbackReceiver.onReconnectionFailure(mgr, info);
207         }
208     }
209 
210     @Override
requestCamera(int id)211     public void requestCamera(int id) {
212         requestCamera(id, false);
213     }
214 
215     @Override
requestCamera(int id, boolean useNewApi)216     public void requestCamera(int id, boolean useNewApi) {
217         Log.v(TAG, "requestCamera");
218         // Based on
219         // (mRequestingCameraId == id, mRequestingCameraId == EMPTY_REQUEST),
220         // we have (T, T), (T, F), (F, T), (F, F).
221         // (T, T): implies id == EMPTY_REQUEST. We don't allow this to happen
222         //         here. Return.
223         // (F, F): A previous request hasn't been fulfilled yet. Return.
224         // (T, F): Already requested the same camera. No-op. Return.
225         // (F, T): Nothing is going on. Continue.
226         if (mRequestingCameraId != EMPTY_REQUEST || mRequestingCameraId == id) {
227             return;
228         }
229         if (mInfo == null) {
230             return;
231         }
232         mRequestingCameraId = id;
233         mActiveCameraDeviceTracker.onCameraOpening(CameraId.fromLegacyId(id));
234 
235         // Only actually use the new API if it's supported on this device.
236         useNewApi = mCameraAgentNg != null && useNewApi;
237         CameraAgent cameraManager = useNewApi ? mCameraAgentNg : mCameraAgent;
238 
239         if (mCameraProxy == null) {
240             // No camera yet.
241             checkAndOpenCamera(cameraManager, id, mCallbackHandler, this);
242         } else if (mCameraProxy.getCameraId() != id || mUsingNewApi != useNewApi) {
243             boolean syncClose = GservicesHelper.useCamera2ApiThroughPortabilityLayer(mContext
244                     .getContentResolver());
245             Log.v(TAG, "different camera already opened, closing then reopening");
246             // Already has camera opened, and is switching cameras and/or APIs.
247             if (mUsingNewApi) {
248                 mCameraAgentNg.closeCamera(mCameraProxy, true);
249             } else {
250                 // if using API2 ensure API1 usage is also synced
251                 mCameraAgent.closeCamera(mCameraProxy, syncClose);
252             }
253             checkAndOpenCamera(cameraManager, id, mCallbackHandler, this);
254         } else {
255             // The same camera, just do a reconnect.
256             Log.v(TAG, "reconnecting to use the existing camera");
257             mCameraProxy.reconnect(mCallbackHandler, this);
258             mCameraProxy = null;
259         }
260 
261         mUsingNewApi = useNewApi;
262         mInfo = cameraManager.getCameraDeviceInfo();
263     }
264 
265     @Override
waitingForCamera()266     public boolean waitingForCamera() {
267         return mRequestingCameraId != EMPTY_REQUEST;
268     }
269 
270     @Override
releaseCamera(int id)271     public void releaseCamera(int id) {
272         if (mCameraProxy == null) {
273             if (mRequestingCameraId == EMPTY_REQUEST) {
274                 // Camera not requested yet.
275                 Log.w(TAG, "Trying to release the camera before requesting");
276             }
277             // Camera requested but not available yet.
278             mRequestingCameraId = EMPTY_REQUEST;
279             return;
280         }
281         int currentId = mCameraProxy.getCameraId();
282         if (currentId != id) {
283             if (mRequestingCameraId == id) {
284                 Log.w(TAG, "Releasing camera which was requested but not yet "
285                         + "opened (current:requested): " + currentId + ":" + id);
286             } else {
287                 throw new IllegalStateException("Trying to release a camera neither opened"
288                         + "nor requested (current:requested:for-release): "
289                         + currentId + ":" + mRequestingCameraId + ":" + id);
290             }
291         }
292 
293         mActiveCameraDeviceTracker.onCameraClosed(CameraId.fromLegacyId(id));
294         mRequestingCameraId = EMPTY_REQUEST;
295     }
296 
removeCallbackReceiver()297     public void removeCallbackReceiver() {
298         mCallbackReceiver = null;
299     }
300 
301     /**
302      * Closes the opened camera device.
303      * TODO: Make this method package private.
304      */
closeCamera(boolean synced)305     public void closeCamera(boolean synced) {
306         Log.v(TAG, "Closing camera");
307         mCameraProxy = null;
308         if (mUsingNewApi) {
309             mCameraAgentNg.closeCamera(mCameraProxy, synced);
310         } else {
311             mCameraAgent.closeCamera(mCameraProxy, synced);
312         }
313         mRequestingCameraId = EMPTY_REQUEST;
314         mUsingNewApi = false;
315     }
316 
checkAndOpenCamera(CameraAgent cameraManager, final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb)317     private static void checkAndOpenCamera(CameraAgent cameraManager,
318             final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb) {
319         Log.v(TAG, "checkAndOpenCamera");
320         try {
321             CameraUtil.throwIfCameraDisabled();
322             cameraManager.openCamera(handler, cameraId, cb);
323         } catch (CameraDisabledException ex) {
324             handler.post(new Runnable() {
325                 @Override
326                 public void run() {
327                     cb.onCameraDisabled(cameraId);
328                 }
329             });
330         }
331     }
332 
setOneShotPreviewCallback(Handler handler, CameraAgent.CameraPreviewDataCallback cb)333     public void setOneShotPreviewCallback(Handler handler,
334             CameraAgent.CameraPreviewDataCallback cb) {
335         mCameraProxy.setOneShotPreviewCallback(handler, cb);
336     }
337 }
338