blob: c5983a1f28484c5d0a0515fb9720a552921ae963 [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
Igor Murashkin5c9eaf62013-09-10 19:35:24 -070058 private StateListener mDeviceListener;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070059 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
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700182 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000183 throws CameraAccessException {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700184 return submitCaptureRequest(request, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000185 }
186
187 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700188 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700189 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!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700192 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700193 }
Igor Murashkin70725502013-06-25 20:27:06 +0000194 // TODO
195 throw new UnsupportedOperationException("Burst capture implemented yet");
196
197 }
198
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700199 private int 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
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700223 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000224 }
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
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700234 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000235 }
236 }
237
238 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700239 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700240 Handler handler) throws CameraAccessException {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700241 return submitCaptureRequest(request, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000242 }
243
244 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700245 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700246 Handler handler) throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700247 if (requests.isEmpty()) {
248 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700249 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700250 }
Igor Murashkin70725502013-06-25 20:27:06 +0000251 // TODO
252 throw new UnsupportedOperationException("Burst capture implemented yet");
253 }
254
255 @Override
256 public void stopRepeating() throws CameraAccessException {
257
258 synchronized (mLock) {
259
260 while (!mRepeatingRequestIdStack.isEmpty()) {
261 int requestId = mRepeatingRequestIdStack.pop();
262
263 try {
264 mRemoteDevice.cancelRequest(requestId);
265 } catch (CameraRuntimeException e) {
266 throw e.asChecked();
267 } catch (RemoteException e) {
268 // impossible
269 return;
270 }
271 }
272 }
273 }
274
275 @Override
276 public void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700277
278 synchronized (mLock) {
279 checkIfCameraClosed();
280 if (!mRepeatingRequestIdStack.isEmpty()) {
281 throw new IllegalStateException("Active repeating request ongoing");
282 }
283
284 try {
285 mRemoteDevice.waitUntilIdle();
286 } catch (CameraRuntimeException e) {
287 throw e.asChecked();
288 } catch (RemoteException e) {
289 // impossible
290 return;
291 }
292 }
Igor Murashkin70725502013-06-25 20:27:06 +0000293 }
294
295 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700296 public void setDeviceListener(StateListener listener, Handler handler) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700297 synchronized (mLock) {
298 mDeviceListener = listener;
299 mDeviceHandler = handler;
300 }
Igor Murashkin70725502013-06-25 20:27:06 +0000301 }
302
303 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700304 public void flush() throws CameraAccessException {
305 synchronized (mLock) {
306 try {
307 mRemoteDevice.flush();
308 } catch (CameraRuntimeException e) {
309 throw e.asChecked();
310 } catch (RemoteException e) {
311 // impossible
312 return;
313 }
314 }
315 }
316
317 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700318 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000319
320 // TODO: every method should throw IllegalStateException after close has been called
321
322 synchronized (mLock) {
323
324 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700325 if (mRemoteDevice != null) {
326 mRemoteDevice.disconnect();
327 }
Igor Murashkin70725502013-06-25 20:27:06 +0000328 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700329 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000330 } catch (RemoteException e) {
331 // impossible
332 }
333
334 mRemoteDevice = null;
335
336 }
337 }
338
339 @Override
340 protected void finalize() throws Throwable {
341 try {
342 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000343 }
344 finally {
345 super.finalize();
346 }
347 }
348
349 static class CaptureListenerHolder {
350
351 private final boolean mRepeating;
352 private final CaptureListener mListener;
353 private final CaptureRequest mRequest;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700354 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000355
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700356 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
357 boolean repeating) {
358 if (listener == null || handler == null) {
359 throw new UnsupportedOperationException(
360 "Must have a valid handler and a valid listener");
361 }
Igor Murashkin70725502013-06-25 20:27:06 +0000362 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700363 mHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000364 mRequest = request;
365 mListener = listener;
366 }
367
368 public boolean isRepeating() {
369 return mRepeating;
370 }
371
372 public CaptureListener getListener() {
373 return mListener;
374 }
375
376 public CaptureRequest getRequest() {
377 return mRequest;
378 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700379
380 public Handler getHandler() {
381 return mHandler;
382 }
383
Igor Murashkin70725502013-06-25 20:27:06 +0000384 }
385
386 // TODO: unit tests
Zhijun Heecb323e2013-07-31 09:40:27 -0700387 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000388
389 @Override
390 public IBinder asBinder() {
391 return this;
392 }
393
394 // TODO: consider rename to onMessageReceived
395 @Override
396 public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700397 if (DEBUG) {
398 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
399 }
Igor Murashkin70725502013-06-25 20:27:06 +0000400 // TODO implement rest
401 }
402
403 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700404 public void onResultReceived(int requestId, CameraMetadataNative result)
405 throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700406 if (DEBUG) {
407 Log.d(TAG, "Received result for id " + requestId);
408 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700409 final CaptureListenerHolder holder;
Igor Murashkin70725502013-06-25 20:27:06 +0000410
411 synchronized (mLock) {
412 // TODO: move this whole map into this class to make it more testable,
413 // exposing the methods necessary like subscribeToRequest, unsubscribe..
414 // TODO: make class static class
415
Zhijun Heecb323e2013-07-31 09:40:27 -0700416 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000417
418 // Clean up listener once we no longer expect to see it.
419
420 // TODO: how to handle repeating listeners?
421 // we probably want cancelRequest to return # of times it already enqueued and
422 // keep a counter.
423 if (holder != null && !holder.isRepeating()) {
Zhijun Heecb323e2013-07-31 09:40:27 -0700424 CameraDevice.this.mCaptureListenerMap.remove(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000425 }
426 }
427
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700428 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000429 if (holder == null) {
Igor Murashkin70725502013-06-25 20:27:06 +0000430 return;
431 }
432
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700433 final CaptureRequest request = holder.getRequest();
434 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000435
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700436 holder.getHandler().post(
437 new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700438 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700439 public void run() {
440 holder.getListener().onCaptureCompleted(
441 CameraDevice.this,
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700442 request,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700443 resultAsCapture);
444 }
445 });
Igor Murashkin70725502013-06-25 20:27:06 +0000446 }
447
448 }
449
Zhijun He7f4d3142013-07-23 07:54:38 -0700450 private void checkIfCameraClosed() {
451 if (mRemoteDevice == null) {
452 throw new IllegalStateException("CameraDevice was already closed");
453 }
454 }
Igor Murashkin70725502013-06-25 20:27:06 +0000455}