blob: efbd7695115a069ce43fb4bb5f0823b6700c47c4 [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
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
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700295 public void setDeviceListener(StateListener listener, Handler handler) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700296 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 Murashkin5c9eaf62013-09-10 19:35:24 -0700317 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000318
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) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700328 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000329 } 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();
Igor Murashkin70725502013-06-25 20:27:06 +0000342 }
343 finally {
344 super.finalize();
345 }
346 }
347
348 static class CaptureListenerHolder {
349
350 private final boolean mRepeating;
351 private final CaptureListener mListener;
352 private final CaptureRequest mRequest;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700353 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000354
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700355 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
356 boolean repeating) {
357 if (listener == null || handler == null) {
358 throw new UnsupportedOperationException(
359 "Must have a valid handler and a valid listener");
360 }
Igor Murashkin70725502013-06-25 20:27:06 +0000361 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700362 mHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000363 mRequest = request;
364 mListener = listener;
365 }
366
367 public boolean isRepeating() {
368 return mRepeating;
369 }
370
371 public CaptureListener getListener() {
372 return mListener;
373 }
374
375 public CaptureRequest getRequest() {
376 return mRequest;
377 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700378
379 public Handler getHandler() {
380 return mHandler;
381 }
382
Igor Murashkin70725502013-06-25 20:27:06 +0000383 }
384
385 // TODO: unit tests
Zhijun Heecb323e2013-07-31 09:40:27 -0700386 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000387
388 @Override
389 public IBinder asBinder() {
390 return this;
391 }
392
393 // TODO: consider rename to onMessageReceived
394 @Override
395 public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700396 if (DEBUG) {
397 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
398 }
Igor Murashkin70725502013-06-25 20:27:06 +0000399 // TODO implement rest
400 }
401
402 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700403 public void onResultReceived(int requestId, CameraMetadataNative result)
404 throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700405 if (DEBUG) {
406 Log.d(TAG, "Received result for id " + requestId);
407 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700408 final CaptureListenerHolder holder;
Igor Murashkin70725502013-06-25 20:27:06 +0000409
410 synchronized (mLock) {
411 // TODO: move this whole map into this class to make it more testable,
412 // exposing the methods necessary like subscribeToRequest, unsubscribe..
413 // TODO: make class static class
414
Zhijun Heecb323e2013-07-31 09:40:27 -0700415 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000416
417 // Clean up listener once we no longer expect to see it.
418
419 // TODO: how to handle repeating listeners?
420 // we probably want cancelRequest to return # of times it already enqueued and
421 // keep a counter.
422 if (holder != null && !holder.isRepeating()) {
Zhijun Heecb323e2013-07-31 09:40:27 -0700423 CameraDevice.this.mCaptureListenerMap.remove(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000424 }
425 }
426
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700427 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000428 if (holder == null) {
Igor Murashkin70725502013-06-25 20:27:06 +0000429 return;
430 }
431
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700432 final CaptureResult resultAsCapture = new CaptureResult(result);
Igor Murashkin70725502013-06-25 20:27:06 +0000433
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700434 holder.getHandler().post(
435 new Runnable() {
436 public void run() {
437 holder.getListener().onCaptureCompleted(
438 CameraDevice.this,
439 holder.getRequest(),
440 resultAsCapture);
441 }
442 });
Igor Murashkin70725502013-06-25 20:27:06 +0000443 }
444
445 }
446
Zhijun He7f4d3142013-07-23 07:54:38 -0700447 private void checkIfCameraClosed() {
448 if (mRemoteDevice == null) {
449 throw new IllegalStateException("CameraDevice was already closed");
450 }
451 }
Igor Murashkin70725502013-06-25 20:27:06 +0000452}