blob: ac9f591550bb16370a0ac0f1e23f5d934d260228 [file] [log] [blame]
Igor Murashkin70725502013-06-25 20:27:06 +00001/*
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
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070017package android.hardware.camera2.impl;
Igor Murashkin70725502013-06-25 20:27:06 +000018
Igor Murashkin57ea59b2013-08-23 16:55:57 -070019import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
20
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070021import android.hardware.camera2.CameraAccessException;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070022import android.hardware.camera2.CameraMetadata;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070023import android.hardware.camera2.CameraProperties;
24import android.hardware.camera2.CaptureRequest;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070025import android.hardware.camera2.CaptureResult;
26import android.hardware.camera2.ICameraDeviceCallbacks;
27import android.hardware.camera2.ICameraDeviceUser;
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070028import android.hardware.camera2.utils.CameraBinderDecorator;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070029import android.hardware.camera2.utils.CameraRuntimeException;
Igor Murashkin70725502013-06-25 20:27:06 +000030import android.os.IBinder;
31import android.os.RemoteException;
32import android.util.Log;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070033import android.util.SparseArray;
Igor Murashkin70725502013-06-25 20:27:06 +000034import android.view.Surface;
35
Igor Murashkin57ea59b2013-08-23 16:55:57 -070036import java.util.ArrayList;
Igor Murashkin70725502013-06-25 20:27:06 +000037import java.util.HashMap;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070038import java.util.HashSet;
Igor Murashkin70725502013-06-25 20:27:06 +000039import java.util.List;
40import java.util.Stack;
41
42/**
43 * HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate
44 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070045public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000046
47 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070048 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000049
50 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
51 private ICameraDeviceUser mRemoteDevice;
52
53 private final Object mLock = new Object();
54 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
55
56 // XX: Make this a WeakReference<CaptureListener> ?
Igor Murashkind7bf1772013-07-12 18:01:31 -070057 // TODO: Convert to SparseIntArray
Igor Murashkin70725502013-06-25 20:27:06 +000058 private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap =
59 new HashMap<Integer, CaptureListenerHolder>();
60
61 private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -070062 // Map stream IDs to Surfaces
63 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
Igor Murashkin70725502013-06-25 20:27:06 +000064
65 private final String mCameraId;
66
67 public CameraDevice(String cameraId) {
68 mCameraId = cameraId;
69 TAG = String.format("CameraDevice-%s-JV", mCameraId);
Zhijun Heecb323e2013-07-31 09:40:27 -070070 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +000071 }
72
73 public CameraDeviceCallbacks getCallbacks() {
74 return mCallbacks;
75 }
76
Igor Murashkin70725502013-06-25 20:27:06 +000077 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070078 // TODO: Move from decorator to direct binder-mediated exceptions
79 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
Igor Murashkin70725502013-06-25 20:27:06 +000080 }
81
82 @Override
83 public CameraProperties getProperties() throws CameraAccessException {
Igor Murashkind7bf1772013-07-12 18:01:31 -070084
85 CameraProperties properties = new CameraProperties();
86 CameraMetadata info = new CameraMetadata();
87
88 try {
89 mRemoteDevice.getCameraInfo(/*out*/info);
90 } catch(CameraRuntimeException e) {
91 throw e.asChecked();
92 } catch(RemoteException e) {
93 // impossible
94 return null;
95 }
96
97 properties.swap(info);
98 return properties;
Igor Murashkin70725502013-06-25 20:27:06 +000099 }
100
101 @Override
102 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
103 synchronized (mLock) {
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700104 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
105 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
106
107 // Determine which streams need to be created, which to be deleted
108 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
109 int streamId = mConfiguredOutputs.keyAt(i);
110 Surface s = mConfiguredOutputs.valueAt(i);
111
112 if (!outputs.contains(s)) {
113 deleteList.add(streamId);
114 } else {
115 addSet.remove(s); // Don't create a stream previously created
116 }
117 }
118
119 try {
120 // TODO: mRemoteDevice.beginConfigure
121
122 // Delete all streams first (to free up HW resources)
123 for (Integer streamId : deleteList) {
124 mRemoteDevice.deleteStream(streamId);
125 mConfiguredOutputs.delete(streamId);
126 }
127
128 // Add all new streams
129 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000130 // TODO: remove width,height,format since we are ignoring
131 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700132 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
133 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000134 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700135
136 // TODO: mRemoteDevice.endConfigure
137 } catch (CameraRuntimeException e) {
138 if (e.getReason() == CAMERA_IN_USE) {
139 throw new IllegalStateException("The camera is currently busy." +
140 " You must call waitUntilIdle before trying to reconfigure.");
141 }
142
143 throw e.asChecked();
144 } catch (RemoteException e) {
145 // impossible
146 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000147 }
148 }
149 }
150
151 @Override
152 public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException {
153
154 synchronized (mLock) {
155
156 CameraMetadata templatedRequest = new CameraMetadata();
157
158 try {
159 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
160 } catch (CameraRuntimeException e) {
161 throw e.asChecked();
162 } catch (RemoteException e) {
163 // impossible
164 return null;
165 }
166
167 CaptureRequest request = new CaptureRequest();
Igor Murashkin70725502013-06-25 20:27:06 +0000168 request.swap(templatedRequest);
169
170 return request;
171
172 }
173 }
174
175 @Override
176 public void capture(CaptureRequest request, CaptureListener listener)
177 throws CameraAccessException {
178 submitCaptureRequest(request, listener, /*streaming*/false);
179 }
180
181 @Override
182 public void captureBurst(List<CaptureRequest> requests, CaptureListener listener)
183 throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700184 if (requests.isEmpty()) {
185 Log.w(TAG, "Capture burst request list is empty, do nothing!");
186 return;
187 }
Igor Murashkin70725502013-06-25 20:27:06 +0000188 // TODO
189 throw new UnsupportedOperationException("Burst capture implemented yet");
190
191 }
192
193 private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
194 boolean repeating) throws CameraAccessException {
195
196 synchronized (mLock) {
197
198 int requestId;
199
200 try {
201 requestId = mRemoteDevice.submitRequest(request, repeating);
202 } catch (CameraRuntimeException e) {
203 throw e.asChecked();
204 } catch (RemoteException e) {
205 // impossible
206 return;
207 }
208
209 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
210 repeating));
211
212 if (repeating) {
213 mRepeatingRequestIdStack.add(requestId);
214 }
215
216 }
217 }
218
219 @Override
220 public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
221 throws CameraAccessException {
222 submitCaptureRequest(request, listener, /*streaming*/true);
223 }
224
225 @Override
226 public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
227 throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700228 if (requests.isEmpty()) {
229 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
230 return;
231 }
Igor Murashkin70725502013-06-25 20:27:06 +0000232 // TODO
233 throw new UnsupportedOperationException("Burst capture implemented yet");
234 }
235
236 @Override
237 public void stopRepeating() throws CameraAccessException {
238
239 synchronized (mLock) {
240
241 while (!mRepeatingRequestIdStack.isEmpty()) {
242 int requestId = mRepeatingRequestIdStack.pop();
243
244 try {
245 mRemoteDevice.cancelRequest(requestId);
246 } catch (CameraRuntimeException e) {
247 throw e.asChecked();
248 } catch (RemoteException e) {
249 // impossible
250 return;
251 }
252 }
253 }
254 }
255
256 @Override
257 public void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700258
259 synchronized (mLock) {
260 checkIfCameraClosed();
261 if (!mRepeatingRequestIdStack.isEmpty()) {
262 throw new IllegalStateException("Active repeating request ongoing");
263 }
264
265 try {
266 mRemoteDevice.waitUntilIdle();
267 } catch (CameraRuntimeException e) {
268 throw e.asChecked();
269 } catch (RemoteException e) {
270 // impossible
271 return;
272 }
273 }
Igor Murashkin70725502013-06-25 20:27:06 +0000274 }
275
276 @Override
277 public void setErrorListener(ErrorListener listener) {
278 // TODO Auto-generated method stub
279
280 }
281
282 @Override
283 public void close() throws Exception {
284
285 // TODO: every method should throw IllegalStateException after close has been called
286
287 synchronized (mLock) {
288
289 try {
290 mRemoteDevice.disconnect();
291 } catch (CameraRuntimeException e) {
292 throw e.asChecked();
293 } catch (RemoteException e) {
294 // impossible
295 }
296
297 mRemoteDevice = null;
298
299 }
300 }
301
302 @Override
303 protected void finalize() throws Throwable {
304 try {
305 close();
306 } catch (CameraRuntimeException e) {
307 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
308 }
309 finally {
310 super.finalize();
311 }
312 }
313
314 static class CaptureListenerHolder {
315
316 private final boolean mRepeating;
317 private final CaptureListener mListener;
318 private final CaptureRequest mRequest;
319
320 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, boolean repeating) {
321 mRepeating = repeating;
322 mRequest = request;
323 mListener = listener;
324 }
325
326 public boolean isRepeating() {
327 return mRepeating;
328 }
329
330 public CaptureListener getListener() {
331 return mListener;
332 }
333
334 public CaptureRequest getRequest() {
335 return mRequest;
336 }
337 }
338
339 // TODO: unit tests
Zhijun Heecb323e2013-07-31 09:40:27 -0700340 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000341
342 @Override
343 public IBinder asBinder() {
344 return this;
345 }
346
347 // TODO: consider rename to onMessageReceived
348 @Override
349 public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700350 if (DEBUG) {
351 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
352 }
Igor Murashkin70725502013-06-25 20:27:06 +0000353 // TODO implement rest
354 }
355
356 @Override
Zhijun Heecb323e2013-07-31 09:40:27 -0700357 public void onResultReceived(int requestId, CameraMetadata result) throws RemoteException {
358 if (DEBUG) {
359 Log.d(TAG, "Received result for id " + requestId);
360 }
Igor Murashkin70725502013-06-25 20:27:06 +0000361 CaptureListenerHolder holder;
362
363 synchronized (mLock) {
364 // TODO: move this whole map into this class to make it more testable,
365 // exposing the methods necessary like subscribeToRequest, unsubscribe..
366 // TODO: make class static class
367
Zhijun Heecb323e2013-07-31 09:40:27 -0700368 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000369
370 // Clean up listener once we no longer expect to see it.
371
372 // TODO: how to handle repeating listeners?
373 // we probably want cancelRequest to return # of times it already enqueued and
374 // keep a counter.
375 if (holder != null && !holder.isRepeating()) {
Zhijun Heecb323e2013-07-31 09:40:27 -0700376 CameraDevice.this.mCaptureListenerMap.remove(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000377 }
378 }
379
380 if (holder == null) {
381 Log.e(TAG, "Result had no listener holder associated with it, dropping result");
382 return;
383 }
384
385 CaptureResult resultAsCapture = new CaptureResult();
386 resultAsCapture.swap(result);
387
388 if (holder.getListener() != null) {
389 holder.getListener().onCaptureComplete(CameraDevice.this, holder.getRequest(),
390 resultAsCapture);
391 }
392 }
393
394 }
395
Zhijun He7f4d3142013-07-23 07:54:38 -0700396 private void checkIfCameraClosed() {
397 if (mRemoteDevice == null) {
398 throw new IllegalStateException("CameraDevice was already closed");
399 }
400 }
Igor Murashkin70725502013-06-25 20:27:06 +0000401}