1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.camera.one.v2;
18 
19 import android.annotation.TargetApi;
20 import android.content.Context;
21 import android.hardware.camera2.CameraAccessException;
22 import android.hardware.camera2.CameraCharacteristics;
23 import android.hardware.camera2.CameraDevice;
24 import android.hardware.camera2.CameraManager;
25 import android.os.Build;
26 import android.os.Handler;
27 import android.util.DisplayMetrics;
28 
29 import com.android.camera.FatalErrorHandler;
30 import com.android.camera.SoundPlayer;
31 import com.android.camera.async.MainThread;
32 import com.android.camera.burst.BurstFacade;
33 import com.android.camera.debug.Log;
34 import com.android.camera.debug.Log.Tag;
35 import com.android.camera.device.ActiveCameraDeviceTracker;
36 import com.android.camera.device.CameraId;
37 import com.android.camera.one.OneCamera;
38 import com.android.camera.one.OneCamera.OpenCallback;
39 import com.android.camera.one.OneCameraAccessException;
40 import com.android.camera.one.OneCameraCaptureSetting;
41 import com.android.camera.one.OneCameraOpener;
42 import com.android.camera.one.config.OneCameraFeatureConfig;
43 import com.android.camera.one.v2.photo.ImageRotationCalculator;
44 import com.android.camera.util.AndroidServices;
45 import com.android.camera.util.ApiHelper;
46 import com.google.common.base.Optional;
47 
48 /**
49  * The {@link com.android.camera.one.OneCameraOpener} implementation on top of Camera2 API.
50  */
51 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
52 public class Camera2OneCameraOpenerImpl implements OneCameraOpener {
53     private static final Tag TAG = new Tag("OneCamera1Opnr");
54 
55     private final Context mContext;
56     private final OneCameraFeatureConfig mFeatureConfig;
57     private final ActiveCameraDeviceTracker mActiveCameraDeviceTracker;
58     private final CameraManager mCameraManager;
59     private final DisplayMetrics mDisplayMetrics;
60 
create( OneCameraFeatureConfig featureConfig, Context context, ActiveCameraDeviceTracker activeCameraDeviceTracker, DisplayMetrics displayMetrics)61     public static Optional<OneCameraOpener> create(
62             OneCameraFeatureConfig featureConfig,
63             Context context,
64             ActiveCameraDeviceTracker activeCameraDeviceTracker,
65             DisplayMetrics displayMetrics) {
66         if (!ApiHelper.HAS_CAMERA_2_API) {
67             return Optional.absent();
68         }
69         CameraManager cameraManager;
70         try {
71             cameraManager = AndroidServices.instance().provideCameraManager();
72         } catch (IllegalStateException ex) {
73             Log.e(TAG, "camera2.CameraManager is not available.");
74             return Optional.absent();
75         }
76         OneCameraOpener oneCameraOpener = new Camera2OneCameraOpenerImpl(
77                 featureConfig,
78                 context,
79                 cameraManager,
80                 activeCameraDeviceTracker,
81                 displayMetrics);
82         return Optional.of(oneCameraOpener);
83     }
84 
85     /**
86      * Instantiates a new {@link com.android.camera.one.OneCameraOpener} for Camera2 API.
87      *
88      * @param cameraManager the underlying Camera2 camera manager.
89      */
Camera2OneCameraOpenerImpl(OneCameraFeatureConfig featureConfig, Context context, CameraManager cameraManager, ActiveCameraDeviceTracker activeCameraDeviceTracker, DisplayMetrics displayMetrics)90     public Camera2OneCameraOpenerImpl(OneCameraFeatureConfig featureConfig,
91             Context context,
92             CameraManager cameraManager,
93             ActiveCameraDeviceTracker activeCameraDeviceTracker,
94             DisplayMetrics displayMetrics) {
95         mFeatureConfig = featureConfig;
96         mContext = context;
97         mCameraManager = cameraManager;
98         mActiveCameraDeviceTracker = activeCameraDeviceTracker;
99         mDisplayMetrics = displayMetrics;
100     }
101 
102     @Override
open( final CameraId cameraKey, final OneCameraCaptureSetting captureSetting, final Handler handler, final MainThread mainThread, final ImageRotationCalculator imageRotationCalculator, final BurstFacade burstController, final SoundPlayer soundPlayer, final OpenCallback openCallback, final FatalErrorHandler fatalErrorHandler)103     public void open(
104             final CameraId cameraKey,
105             final OneCameraCaptureSetting captureSetting,
106             final Handler handler,
107             final MainThread mainThread,
108             final ImageRotationCalculator imageRotationCalculator,
109             final BurstFacade burstController,
110             final SoundPlayer soundPlayer,
111             final OpenCallback openCallback,
112             final FatalErrorHandler fatalErrorHandler) {
113         try {
114             Log.i(TAG, "Opening Camera: " + cameraKey);
115 
116             mActiveCameraDeviceTracker.onCameraOpening(cameraKey);
117 
118             mCameraManager.openCamera(cameraKey.getValue(), new CameraDevice.StateCallback() {
119                 // We may get multiple calls to StateCallback, but only the
120                 // first callback indicates the status of the camera-opening
121                 // operation. For example, we may receive onOpened() and later
122                 // onClosed(), but only the first should be relayed to
123                 // openCallback.
124                 private boolean isFirstCallback = true;
125 
126                 @Override
127                 public void onDisconnected(CameraDevice device) {
128                     if (isFirstCallback) {
129                         isFirstCallback = false;
130                         // If the camera is disconnected before it is opened
131                         // then we must call close.
132                         device.close();
133                         openCallback.onCameraClosed();
134                     } else {
135                         // Disconnected during active session
136                         openCallback.onCameraInterrupted();
137                     }
138                 }
139 
140                 @Override
141                 public void onClosed(CameraDevice device) {
142                     if (isFirstCallback) {
143                         isFirstCallback = false;
144                         openCallback.onCameraClosed();
145                     }
146                 }
147 
148                 @Override
149                 public void onError(CameraDevice device, int error) {
150                     if (error == CameraDevice.StateCallback.ERROR_CAMERA_IN_USE) {
151                         // Tolerate ERROR_CAMERA_IN_USE for split-screen.
152                         isFirstCallback = false;
153                         device.close();
154                         openCallback.onCameraInUse();
155                     } else if (isFirstCallback) {
156                         isFirstCallback = false;
157                         device.close();
158                         openCallback.onFailure();
159                     } else {
160                         // Ensures we handle the case where an error occurs
161                         // after the camera has been opened.
162                         fatalErrorHandler.onGenericCameraAccessFailure();
163                     }
164                 }
165 
166                 @Override
167                 public void onOpened(CameraDevice device) {
168                     if (isFirstCallback) {
169                         isFirstCallback = false;
170                         try {
171                             CameraCharacteristics characteristics = mCameraManager
172                                     .getCameraCharacteristics(device.getId());
173                             // TODO: Set boolean based on whether HDR+ is
174                             // enabled.
175                             OneCamera oneCamera = OneCameraCreator.create(
176                                     device,
177                                     characteristics,
178                                     mFeatureConfig,
179                                     captureSetting,
180                                     mDisplayMetrics,
181                                     mContext,
182                                     mainThread,
183                                     imageRotationCalculator,
184                                     burstController,
185                                     soundPlayer, fatalErrorHandler);
186 
187                             if (oneCamera != null) {
188                                 openCallback.onCameraOpened(oneCamera);
189                             } else {
190                                 Log.d(TAG, "Could not construct a OneCamera object!");
191                                 openCallback.onFailure();
192                             }
193                         } catch (CameraAccessException e) {
194                             Log.d(TAG, "Could not get camera characteristics", e);
195                             openCallback.onFailure();
196                         } catch (OneCameraAccessException e) {
197                             Log.d(TAG, "Could not create OneCamera", e);
198                             openCallback.onFailure();
199                         }
200                     }
201                 }
202             }, handler);
203         } catch (CameraAccessException ex) {
204             Log.e(TAG, "Could not open camera. " + ex.getMessage());
205             handler.post(new Runnable() {
206                 @Override
207                 public void run() {
208                     if (ex.getReason() == CameraAccessException.CAMERA_IN_USE) {
209                         openCallback.onCameraInUse();
210                     } else {
211                         openCallback.onFailure();
212                     }
213                 }
214             });
215         } catch (UnsupportedOperationException ex) {
216             Log.e(TAG, "Could not open camera. " + ex.getMessage());
217             handler.post(new Runnable() {
218                 @Override
219                 public void run() {
220                     openCallback.onFailure();
221                 }
222             });
223         } catch (SecurityException ex) {
224             fatalErrorHandler.onCameraDisabledFailure();
225         }
226     }
227 }
228