blob: 966fcfaa6560ff911732778076e24df86f9ac48a [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;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070032import android.os.Handler;
33import android.os.Looper;
Igor Murashkin70725502013-06-25 20:27:06 +000034import android.util.Log;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070035import android.util.SparseArray;
Igor Murashkin70725502013-06-25 20:27:06 +000036import android.view.Surface;
37
Igor Murashkin57ea59b2013-08-23 16:55:57 -070038import java.util.ArrayList;
Igor Murashkin70725502013-06-25 20:27:06 +000039import java.util.HashMap;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070040import java.util.HashSet;
Igor Murashkin70725502013-06-25 20:27:06 +000041import java.util.List;
42import java.util.Stack;
43
44/**
45 * HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate
46 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070047public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000048
49 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070050 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000051
52 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
53 private ICameraDeviceUser mRemoteDevice;
54
55 private final Object mLock = new Object();
56 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
57
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070058 private CameraDeviceListener mDeviceListener;
59 private Handler mDeviceHandler;
60
61 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
62 new SparseArray<CaptureListenerHolder>();
Igor Murashkin70725502013-06-25 20:27:06 +000063
64 private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -070065 // Map stream IDs to Surfaces
66 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
Igor Murashkin70725502013-06-25 20:27:06 +000067
68 private final String mCameraId;
69
70 public CameraDevice(String cameraId) {
71 mCameraId = cameraId;
72 TAG = String.format("CameraDevice-%s-JV", mCameraId);
Zhijun Heecb323e2013-07-31 09:40:27 -070073 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +000074 }
75
76 public CameraDeviceCallbacks getCallbacks() {
77 return mCallbacks;
78 }
79
Igor Murashkin70725502013-06-25 20:27:06 +000080 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070081 // TODO: Move from decorator to direct binder-mediated exceptions
82 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
Igor Murashkin70725502013-06-25 20:27:06 +000083 }
84
85 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070086 public String getId() {
87 return mCameraId;
88 }
89
90 @Override
Igor Murashkin70725502013-06-25 20:27:06 +000091 public CameraProperties getProperties() throws CameraAccessException {
Igor Murashkind7bf1772013-07-12 18:01:31 -070092
93 CameraProperties properties = new CameraProperties();
94 CameraMetadata info = new CameraMetadata();
95
96 try {
97 mRemoteDevice.getCameraInfo(/*out*/info);
98 } catch(CameraRuntimeException e) {
99 throw e.asChecked();
100 } catch(RemoteException e) {
101 // impossible
102 return null;
103 }
104
105 properties.swap(info);
106 return properties;
Igor Murashkin70725502013-06-25 20:27:06 +0000107 }
108
109 @Override
110 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
111 synchronized (mLock) {
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700112 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
113 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
114
115 // Determine which streams need to be created, which to be deleted
116 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
117 int streamId = mConfiguredOutputs.keyAt(i);
118 Surface s = mConfiguredOutputs.valueAt(i);
119
120 if (!outputs.contains(s)) {
121 deleteList.add(streamId);
122 } else {
123 addSet.remove(s); // Don't create a stream previously created
124 }
125 }
126
127 try {
128 // TODO: mRemoteDevice.beginConfigure
129
130 // Delete all streams first (to free up HW resources)
131 for (Integer streamId : deleteList) {
132 mRemoteDevice.deleteStream(streamId);
133 mConfiguredOutputs.delete(streamId);
134 }
135
136 // Add all new streams
137 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000138 // TODO: remove width,height,format since we are ignoring
139 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700140 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
141 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000142 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700143
144 // TODO: mRemoteDevice.endConfigure
145 } catch (CameraRuntimeException e) {
146 if (e.getReason() == CAMERA_IN_USE) {
147 throw new IllegalStateException("The camera is currently busy." +
148 " You must call waitUntilIdle before trying to reconfigure.");
149 }
150
151 throw e.asChecked();
152 } catch (RemoteException e) {
153 // impossible
154 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000155 }
156 }
157 }
158
159 @Override
160 public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException {
161
162 synchronized (mLock) {
163
164 CameraMetadata templatedRequest = new CameraMetadata();
165
166 try {
167 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
168 } catch (CameraRuntimeException e) {
169 throw e.asChecked();
170 } catch (RemoteException e) {
171 // impossible
172 return null;
173 }
174
175 CaptureRequest request = new CaptureRequest();
Igor Murashkin70725502013-06-25 20:27:06 +0000176 request.swap(templatedRequest);
177
178 return request;
179
180 }
181 }
182
183 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700184 public void capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000185 throws CameraAccessException {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700186 submitCaptureRequest(request, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000187 }
188
189 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700190 public void captureBurst(List<CaptureRequest> requests, CaptureListener listener,
191 Handler handler) throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700192 if (requests.isEmpty()) {
193 Log.w(TAG, "Capture burst request list is empty, do nothing!");
194 return;
195 }
Igor Murashkin70725502013-06-25 20:27:06 +0000196 // TODO
197 throw new UnsupportedOperationException("Burst capture implemented yet");
198
199 }
200
201 private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700202 Handler handler, boolean repeating) throws CameraAccessException {
203
204 // Need a valid handler, or current thread needs to have a looper, if
205 // listener is valid
206 if (handler == null && listener != null) {
207 Looper looper = Looper.myLooper();
208 if (looper == null) {
209 throw new IllegalArgumentException(
210 "No handler given, and current thread has no looper!");
211 }
212 handler = new Handler(looper);
213 }
Igor Murashkin70725502013-06-25 20:27:06 +0000214
215 synchronized (mLock) {
216
217 int requestId;
218
219 try {
220 requestId = mRemoteDevice.submitRequest(request, repeating);
221 } catch (CameraRuntimeException e) {
222 throw e.asChecked();
223 } catch (RemoteException e) {
224 // impossible
225 return;
226 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700227 if (listener != null) {
228 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
229 handler, repeating));
230 }
Igor Murashkin70725502013-06-25 20:27:06 +0000231
232 if (repeating) {
233 mRepeatingRequestIdStack.add(requestId);
234 }
235
236 }
237 }
238
239 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700240 public void setRepeatingRequest(CaptureRequest request, CaptureListener listener,
241 Handler handler) throws CameraAccessException {
242 submitCaptureRequest(request, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000243 }
244
245 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700246 public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
247 Handler handler) throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700248 if (requests.isEmpty()) {
249 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
250 return;
251 }
Igor Murashkin70725502013-06-25 20:27:06 +0000252 // TODO
253 throw new UnsupportedOperationException("Burst capture implemented yet");
254 }
255
256 @Override
257 public void stopRepeating() throws CameraAccessException {
258
259 synchronized (mLock) {
260
261 while (!mRepeatingRequestIdStack.isEmpty()) {
262 int requestId = mRepeatingRequestIdStack.pop();
263
264 try {
265 mRemoteDevice.cancelRequest(requestId);
266 } catch (CameraRuntimeException e) {
267 throw e.asChecked();
268 } catch (RemoteException e) {
269 // impossible
270 return;
271 }
272 }
273 }
274 }
275
276 @Override
277 public void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700278
279 synchronized (mLock) {
280 checkIfCameraClosed();
281 if (!mRepeatingRequestIdStack.isEmpty()) {
282 throw new IllegalStateException("Active repeating request ongoing");
283 }
284
285 try {
286 mRemoteDevice.waitUntilIdle();
287 } catch (CameraRuntimeException e) {
288 throw e.asChecked();
289 } catch (RemoteException e) {
290 // impossible
291 return;
292 }
293 }
Igor Murashkin70725502013-06-25 20:27:06 +0000294 }
295
296 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700297 public void setDeviceListener(CameraDeviceListener listener, Handler handler) {
298 synchronized (mLock) {
299 mDeviceListener = listener;
300 mDeviceHandler = handler;
301 }
Igor Murashkin70725502013-06-25 20:27:06 +0000302 }
303
304 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700305 public void flush() throws CameraAccessException {
306 synchronized (mLock) {
307 try {
308 mRemoteDevice.flush();
309 } catch (CameraRuntimeException e) {
310 throw e.asChecked();
311 } catch (RemoteException e) {
312 // impossible
313 return;
314 }
315 }
316 }
317
318 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000319 public void close() throws Exception {
320
321 // TODO: every method should throw IllegalStateException after close has been called
322
323 synchronized (mLock) {
324
325 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700326 if (mRemoteDevice != null) {
327 mRemoteDevice.disconnect();
328 }
Igor Murashkin70725502013-06-25 20:27:06 +0000329 } catch (CameraRuntimeException e) {
330 throw e.asChecked();
331 } catch (RemoteException e) {
332 // impossible
333 }
334
335 mRemoteDevice = null;
336
337 }
338 }
339
340 @Override
341 protected void finalize() throws Throwable {
342 try {
343 close();
344 } catch (CameraRuntimeException e) {
345 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
346 }
347 finally {
348 super.finalize();
349 }
350 }
351
352 static class CaptureListenerHolder {
353
354 private final boolean mRepeating;
355 private final CaptureListener mListener;
356 private final CaptureRequest mRequest;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700357 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000358
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700359 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
360 boolean repeating) {
361 if (listener == null || handler == null) {
362 throw new UnsupportedOperationException(
363 "Must have a valid handler and a valid listener");
364 }
Igor Murashkin70725502013-06-25 20:27:06 +0000365 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700366 mHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000367 mRequest = request;
368 mListener = listener;
369 }
370
371 public boolean isRepeating() {
372 return mRepeating;
373 }
374
375 public CaptureListener getListener() {
376 return mListener;
377 }
378
379 public CaptureRequest getRequest() {
380 return mRequest;
381 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700382
383 public Handler getHandler() {
384 return mHandler;
385 }
386
Igor Murashkin70725502013-06-25 20:27:06 +0000387 }
388
389 // TODO: unit tests
Zhijun Heecb323e2013-07-31 09:40:27 -0700390 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000391
392 @Override
393 public IBinder asBinder() {
394 return this;
395 }
396
397 // TODO: consider rename to onMessageReceived
398 @Override
399 public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700400 if (DEBUG) {
401 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
402 }
Igor Murashkin70725502013-06-25 20:27:06 +0000403 // TODO implement rest
404 }
405
406 @Override
Zhijun Heecb323e2013-07-31 09:40:27 -0700407 public void onResultReceived(int requestId, CameraMetadata result) throws RemoteException {
408 if (DEBUG) {
409 Log.d(TAG, "Received result for id " + requestId);
410 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700411 final CaptureListenerHolder holder;
Igor Murashkin70725502013-06-25 20:27:06 +0000412
413 synchronized (mLock) {
414 // TODO: move this whole map into this class to make it more testable,
415 // exposing the methods necessary like subscribeToRequest, unsubscribe..
416 // TODO: make class static class
417
Zhijun Heecb323e2013-07-31 09:40:27 -0700418 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000419
420 // Clean up listener once we no longer expect to see it.
421
422 // TODO: how to handle repeating listeners?
423 // we probably want cancelRequest to return # of times it already enqueued and
424 // keep a counter.
425 if (holder != null && !holder.isRepeating()) {
Zhijun Heecb323e2013-07-31 09:40:27 -0700426 CameraDevice.this.mCaptureListenerMap.remove(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000427 }
428 }
429
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700430 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000431 if (holder == null) {
Igor Murashkin70725502013-06-25 20:27:06 +0000432 return;
433 }
434
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700435 final CaptureResult resultAsCapture = new CaptureResult();
Igor Murashkin70725502013-06-25 20:27:06 +0000436 resultAsCapture.swap(result);
437
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700438 holder.getHandler().post(
439 new Runnable() {
440 public void run() {
441 holder.getListener().onCaptureCompleted(
442 CameraDevice.this,
443 holder.getRequest(),
444 resultAsCapture);
445 }
446 });
Igor Murashkin70725502013-06-25 20:27:06 +0000447 }
448
449 }
450
Zhijun He7f4d3142013-07-23 07:54:38 -0700451 private void checkIfCameraClosed() {
452 if (mRemoteDevice == null) {
453 throw new IllegalStateException("CameraDevice was already closed");
454 }
455 }
Igor Murashkin70725502013-06-25 20:27:06 +0000456}