blob: 995555a3bce526ec30d8c56a41d5a3f7a180c5f5 [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/**
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070045 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
Igor Murashkin70725502013-06-25 20:27:06 +000046 */
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
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070093 CameraMetadataNative info = new CameraMetadataNative();
Igor Murashkind7bf1772013-07-12 18:01:31 -070094
95 try {
96 mRemoteDevice.getCameraInfo(/*out*/info);
97 } catch(CameraRuntimeException e) {
98 throw e.asChecked();
99 } catch(RemoteException e) {
100 // impossible
101 return null;
102 }
103
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700104 CameraProperties properties = new CameraProperties(info);
Igor Murashkind7bf1772013-07-12 18:01:31 -0700105 return properties;
Igor Murashkin70725502013-06-25 20:27:06 +0000106 }
107
108 @Override
109 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
110 synchronized (mLock) {
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700111 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
112 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
113
114 // Determine which streams need to be created, which to be deleted
115 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
116 int streamId = mConfiguredOutputs.keyAt(i);
117 Surface s = mConfiguredOutputs.valueAt(i);
118
119 if (!outputs.contains(s)) {
120 deleteList.add(streamId);
121 } else {
122 addSet.remove(s); // Don't create a stream previously created
123 }
124 }
125
126 try {
127 // TODO: mRemoteDevice.beginConfigure
128
129 // Delete all streams first (to free up HW resources)
130 for (Integer streamId : deleteList) {
131 mRemoteDevice.deleteStream(streamId);
132 mConfiguredOutputs.delete(streamId);
133 }
134
135 // Add all new streams
136 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000137 // TODO: remove width,height,format since we are ignoring
138 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700139 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
140 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000141 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700142
143 // TODO: mRemoteDevice.endConfigure
144 } catch (CameraRuntimeException e) {
145 if (e.getReason() == CAMERA_IN_USE) {
146 throw new IllegalStateException("The camera is currently busy." +
147 " You must call waitUntilIdle before trying to reconfigure.");
148 }
149
150 throw e.asChecked();
151 } catch (RemoteException e) {
152 // impossible
153 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000154 }
155 }
156 }
157
158 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700159 public CaptureRequest.Builder createCaptureRequest(int templateType)
160 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000161 synchronized (mLock) {
162
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700163 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000164
165 try {
166 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
167 } catch (CameraRuntimeException e) {
168 throw e.asChecked();
169 } catch (RemoteException e) {
170 // impossible
171 return null;
172 }
173
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700174 CaptureRequest.Builder builder =
175 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000176
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700177 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000178 }
179 }
180
181 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700182 public void capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000183 throws CameraAccessException {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700184 submitCaptureRequest(request, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000185 }
186
187 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700188 public void captureBurst(List<CaptureRequest> requests, CaptureListener listener,
189 Handler handler) throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700190 if (requests.isEmpty()) {
191 Log.w(TAG, "Capture burst request list is empty, do nothing!");
192 return;
193 }
Igor Murashkin70725502013-06-25 20:27:06 +0000194 // TODO
195 throw new UnsupportedOperationException("Burst capture implemented yet");
196
197 }
198
199 private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700200 Handler handler, boolean repeating) throws CameraAccessException {
201
202 // Need a valid handler, or current thread needs to have a looper, if
203 // listener is valid
204 if (handler == null && listener != null) {
205 Looper looper = Looper.myLooper();
206 if (looper == null) {
207 throw new IllegalArgumentException(
208 "No handler given, and current thread has no looper!");
209 }
210 handler = new Handler(looper);
211 }
Igor Murashkin70725502013-06-25 20:27:06 +0000212
213 synchronized (mLock) {
214
215 int requestId;
216
217 try {
218 requestId = mRemoteDevice.submitRequest(request, repeating);
219 } catch (CameraRuntimeException e) {
220 throw e.asChecked();
221 } catch (RemoteException e) {
222 // impossible
223 return;
224 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700225 if (listener != null) {
226 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
227 handler, repeating));
228 }
Igor Murashkin70725502013-06-25 20:27:06 +0000229
230 if (repeating) {
231 mRepeatingRequestIdStack.add(requestId);
232 }
233
234 }
235 }
236
237 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700238 public void setRepeatingRequest(CaptureRequest request, CaptureListener listener,
239 Handler handler) throws CameraAccessException {
240 submitCaptureRequest(request, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000241 }
242
243 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700244 public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
245 Handler handler) throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700246 if (requests.isEmpty()) {
247 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
248 return;
249 }
Igor Murashkin70725502013-06-25 20:27:06 +0000250 // TODO
251 throw new UnsupportedOperationException("Burst capture implemented yet");
252 }
253
254 @Override
255 public void stopRepeating() throws CameraAccessException {
256
257 synchronized (mLock) {
258
259 while (!mRepeatingRequestIdStack.isEmpty()) {
260 int requestId = mRepeatingRequestIdStack.pop();
261
262 try {
263 mRemoteDevice.cancelRequest(requestId);
264 } catch (CameraRuntimeException e) {
265 throw e.asChecked();
266 } catch (RemoteException e) {
267 // impossible
268 return;
269 }
270 }
271 }
272 }
273
274 @Override
275 public void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700276
277 synchronized (mLock) {
278 checkIfCameraClosed();
279 if (!mRepeatingRequestIdStack.isEmpty()) {
280 throw new IllegalStateException("Active repeating request ongoing");
281 }
282
283 try {
284 mRemoteDevice.waitUntilIdle();
285 } catch (CameraRuntimeException e) {
286 throw e.asChecked();
287 } catch (RemoteException e) {
288 // impossible
289 return;
290 }
291 }
Igor Murashkin70725502013-06-25 20:27:06 +0000292 }
293
294 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700295 public void setDeviceListener(CameraDeviceListener listener, Handler handler) {
296 synchronized (mLock) {
297 mDeviceListener = listener;
298 mDeviceHandler = handler;
299 }
Igor Murashkin70725502013-06-25 20:27:06 +0000300 }
301
302 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700303 public void flush() throws CameraAccessException {
304 synchronized (mLock) {
305 try {
306 mRemoteDevice.flush();
307 } catch (CameraRuntimeException e) {
308 throw e.asChecked();
309 } catch (RemoteException e) {
310 // impossible
311 return;
312 }
313 }
314 }
315
316 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000317 public void close() throws Exception {
318
319 // TODO: every method should throw IllegalStateException after close has been called
320
321 synchronized (mLock) {
322
323 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700324 if (mRemoteDevice != null) {
325 mRemoteDevice.disconnect();
326 }
Igor Murashkin70725502013-06-25 20:27:06 +0000327 } catch (CameraRuntimeException e) {
328 throw e.asChecked();
329 } catch (RemoteException e) {
330 // impossible
331 }
332
333 mRemoteDevice = null;
334
335 }
336 }
337
338 @Override
339 protected void finalize() throws Throwable {
340 try {
341 close();
342 } catch (CameraRuntimeException e) {
343 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
344 }
345 finally {
346 super.finalize();
347 }
348 }
349
350 static class CaptureListenerHolder {
351
352 private final boolean mRepeating;
353 private final CaptureListener mListener;
354 private final CaptureRequest mRequest;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700355 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000356
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700357 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
358 boolean repeating) {
359 if (listener == null || handler == null) {
360 throw new UnsupportedOperationException(
361 "Must have a valid handler and a valid listener");
362 }
Igor Murashkin70725502013-06-25 20:27:06 +0000363 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700364 mHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000365 mRequest = request;
366 mListener = listener;
367 }
368
369 public boolean isRepeating() {
370 return mRepeating;
371 }
372
373 public CaptureListener getListener() {
374 return mListener;
375 }
376
377 public CaptureRequest getRequest() {
378 return mRequest;
379 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700380
381 public Handler getHandler() {
382 return mHandler;
383 }
384
Igor Murashkin70725502013-06-25 20:27:06 +0000385 }
386
387 // TODO: unit tests
Zhijun Heecb323e2013-07-31 09:40:27 -0700388 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000389
390 @Override
391 public IBinder asBinder() {
392 return this;
393 }
394
395 // TODO: consider rename to onMessageReceived
396 @Override
397 public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700398 if (DEBUG) {
399 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
400 }
Igor Murashkin70725502013-06-25 20:27:06 +0000401 // TODO implement rest
402 }
403
404 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700405 public void onResultReceived(int requestId, CameraMetadataNative result)
406 throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700407 if (DEBUG) {
408 Log.d(TAG, "Received result for id " + requestId);
409 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700410 final CaptureListenerHolder holder;
Igor Murashkin70725502013-06-25 20:27:06 +0000411
412 synchronized (mLock) {
413 // TODO: move this whole map into this class to make it more testable,
414 // exposing the methods necessary like subscribeToRequest, unsubscribe..
415 // TODO: make class static class
416
Zhijun Heecb323e2013-07-31 09:40:27 -0700417 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000418
419 // Clean up listener once we no longer expect to see it.
420
421 // TODO: how to handle repeating listeners?
422 // we probably want cancelRequest to return # of times it already enqueued and
423 // keep a counter.
424 if (holder != null && !holder.isRepeating()) {
Zhijun Heecb323e2013-07-31 09:40:27 -0700425 CameraDevice.this.mCaptureListenerMap.remove(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000426 }
427 }
428
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700429 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000430 if (holder == null) {
Igor Murashkin70725502013-06-25 20:27:06 +0000431 return;
432 }
433
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700434 final CaptureResult resultAsCapture = new CaptureResult(result);
Igor Murashkin70725502013-06-25 20:27:06 +0000435
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700436 holder.getHandler().post(
437 new Runnable() {
438 public void run() {
439 holder.getListener().onCaptureCompleted(
440 CameraDevice.this,
441 holder.getRequest(),
442 resultAsCapture);
443 }
444 });
Igor Murashkin70725502013-06-25 20:27:06 +0000445 }
446
447 }
448
Zhijun He7f4d3142013-07-23 07:54:38 -0700449 private void checkIfCameraClosed() {
450 if (mRemoteDevice == null) {
451 throw new IllegalStateException("CameraDevice was already closed");
452 }
453 }
Igor Murashkin70725502013-06-25 20:27:06 +0000454}