blob: 40586f0efcd966298bd7cc0e7876b0f64506e101 [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;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070022import android.hardware.camera2.CaptureRequest;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070023import android.hardware.camera2.CaptureResult;
24import android.hardware.camera2.ICameraDeviceCallbacks;
25import android.hardware.camera2.ICameraDeviceUser;
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070026import android.hardware.camera2.utils.CameraBinderDecorator;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070027import android.hardware.camera2.utils.CameraRuntimeException;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070028import android.os.Handler;
Ruben Brunkdecfe952013-10-29 11:00:32 -070029import android.os.IBinder;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070030import android.os.Looper;
Ruben Brunkdecfe952013-10-29 11:00:32 -070031import android.os.RemoteException;
Igor Murashkin70725502013-06-25 20:27:06 +000032import 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 Murashkin57ea59b2013-08-23 16:55:57 -070037import java.util.HashSet;
Ruben Brunkdecfe952013-10-29 11:00:32 -070038import java.util.Iterator;
Igor Murashkin70725502013-06-25 20:27:06 +000039import java.util.List;
Igor Murashkin70725502013-06-25 20:27:06 +000040
41/**
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070042 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
Igor Murashkin70725502013-06-25 20:27:06 +000043 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070044public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000045
46 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070047 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000048
Ruben Brunkdecfe952013-10-29 11:00:32 -070049 private static final int REQUEST_ID_NONE = -1;
50
Igor Murashkin70725502013-06-25 20:27:06 +000051 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
52 private ICameraDeviceUser mRemoteDevice;
53
54 private final Object mLock = new Object();
55 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
56
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070057 private final StateListener mDeviceListener;
58 private final Handler mDeviceHandler;
59
60 private boolean mIdle = true;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070061
62 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
63 new SparseArray<CaptureListenerHolder>();
Igor Murashkin70725502013-06-25 20:27:06 +000064
Ruben Brunkdecfe952013-10-29 11:00:32 -070065 private int mRepeatingRequestId = REQUEST_ID_NONE;
66 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -070067 // Map stream IDs to Surfaces
68 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
Igor Murashkin70725502013-06-25 20:27:06 +000069
70 private final String mCameraId;
71
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070072 // Runnables for all state transitions, except error, which needs the
73 // error code argument
74
75 private final Runnable mCallOnOpened = new Runnable() {
76 public void run() {
77 if (!CameraDevice.this.isClosed()) {
78 mDeviceListener.onOpened(CameraDevice.this);
79 }
80 }
81 };
82
83 private final Runnable mCallOnUnconfigured = new Runnable() {
84 public void run() {
85 if (!CameraDevice.this.isClosed()) {
86 mDeviceListener.onUnconfigured(CameraDevice.this);
87 }
88 }
89 };
90
91 private final Runnable mCallOnActive = new Runnable() {
92 public void run() {
93 if (!CameraDevice.this.isClosed()) {
94 mDeviceListener.onActive(CameraDevice.this);
95 }
96 }
97 };
98
99 private final Runnable mCallOnBusy = new Runnable() {
100 public void run() {
101 if (!CameraDevice.this.isClosed()) {
102 mDeviceListener.onBusy(CameraDevice.this);
103 }
104 }
105 };
106
107 private final Runnable mCallOnClosed = new Runnable() {
108 public void run() {
109 if (!CameraDevice.this.isClosed()) {
110 mDeviceListener.onClosed(CameraDevice.this);
111 }
112 }
113 };
114
115 private final Runnable mCallOnIdle = new Runnable() {
116 public void run() {
117 if (!CameraDevice.this.isClosed()) {
118 mDeviceListener.onIdle(CameraDevice.this);
119 }
120 }
121 };
122
123 private final Runnable mCallOnDisconnected = new Runnable() {
124 public void run() {
125 if (!CameraDevice.this.isClosed()) {
126 mDeviceListener.onDisconnected(CameraDevice.this);
127 }
128 }
129 };
130
131 public CameraDevice(String cameraId, StateListener listener, Handler handler) {
132 if (cameraId == null || listener == null || handler == null) {
133 throw new IllegalArgumentException("Null argument given");
134 }
Igor Murashkin70725502013-06-25 20:27:06 +0000135 mCameraId = cameraId;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700136 mDeviceListener = listener;
137 mDeviceHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000138 TAG = String.format("CameraDevice-%s-JV", mCameraId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700139 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +0000140 }
141
142 public CameraDeviceCallbacks getCallbacks() {
143 return mCallbacks;
144 }
145
Igor Murashkin70725502013-06-25 20:27:06 +0000146 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -0700147 // TODO: Move from decorator to direct binder-mediated exceptions
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700148 synchronized(mLock) {
149 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
150
151 mDeviceHandler.post(mCallOnOpened);
152 mDeviceHandler.post(mCallOnUnconfigured);
153 }
Igor Murashkin70725502013-06-25 20:27:06 +0000154 }
155
156 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700157 public String getId() {
158 return mCameraId;
159 }
160
161 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000162 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700163 // Treat a null input the same an empty list
164 if (outputs == null) {
165 outputs = new ArrayList<Surface>();
166 }
Igor Murashkin70725502013-06-25 20:27:06 +0000167 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700168 checkIfCameraClosed();
169
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700170 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
171 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
172
173 // Determine which streams need to be created, which to be deleted
174 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
175 int streamId = mConfiguredOutputs.keyAt(i);
176 Surface s = mConfiguredOutputs.valueAt(i);
177
178 if (!outputs.contains(s)) {
179 deleteList.add(streamId);
180 } else {
181 addSet.remove(s); // Don't create a stream previously created
182 }
183 }
184
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700185 mDeviceHandler.post(mCallOnBusy);
186 stopRepeating();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700187
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700188 try {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700189 waitUntilIdle();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700190
191 // TODO: mRemoteDevice.beginConfigure
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700192 // Delete all streams first (to free up HW resources)
193 for (Integer streamId : deleteList) {
194 mRemoteDevice.deleteStream(streamId);
195 mConfiguredOutputs.delete(streamId);
196 }
197
198 // Add all new streams
199 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000200 // TODO: remove width,height,format since we are ignoring
201 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700202 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
203 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000204 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700205
206 // TODO: mRemoteDevice.endConfigure
207 } catch (CameraRuntimeException e) {
208 if (e.getReason() == CAMERA_IN_USE) {
209 throw new IllegalStateException("The camera is currently busy." +
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700210 " You must wait until the previous operation completes.");
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700211 }
212
213 throw e.asChecked();
214 } catch (RemoteException e) {
215 // impossible
216 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000217 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700218
219 if (outputs.size() > 0) {
220 mDeviceHandler.post(mCallOnIdle);
221 } else {
222 mDeviceHandler.post(mCallOnUnconfigured);
223 }
Igor Murashkin70725502013-06-25 20:27:06 +0000224 }
225 }
226
227 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700228 public CaptureRequest.Builder createCaptureRequest(int templateType)
229 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000230 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700231 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000232
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700233 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000234
235 try {
236 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
237 } catch (CameraRuntimeException e) {
238 throw e.asChecked();
239 } catch (RemoteException e) {
240 // impossible
241 return null;
242 }
243
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700244 CaptureRequest.Builder builder =
245 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000246
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700247 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000248 }
249 }
250
251 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700252 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000253 throws CameraAccessException {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700254 return submitCaptureRequest(request, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000255 }
256
257 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700258 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700259 Handler handler) throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700260 if (requests.isEmpty()) {
261 Log.w(TAG, "Capture burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700262 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700263 }
Igor Murashkin70725502013-06-25 20:27:06 +0000264 // TODO
265 throw new UnsupportedOperationException("Burst capture implemented yet");
266
267 }
268
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700269 private int submitCaptureRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700270 Handler handler, boolean repeating) throws CameraAccessException {
271
272 // Need a valid handler, or current thread needs to have a looper, if
273 // listener is valid
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700274 if (listener != null) {
275 handler = checkHandler(handler);
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700276 }
Igor Murashkin70725502013-06-25 20:27:06 +0000277
278 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700279 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000280 int requestId;
281
Ruben Brunke73b41b2013-11-07 19:30:43 -0800282 if (repeating) {
283 stopRepeating();
284 }
285
Igor Murashkin70725502013-06-25 20:27:06 +0000286 try {
287 requestId = mRemoteDevice.submitRequest(request, repeating);
288 } catch (CameraRuntimeException e) {
289 throw e.asChecked();
290 } catch (RemoteException e) {
291 // impossible
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700292 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000293 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700294 if (listener != null) {
295 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
296 handler, repeating));
297 }
Igor Murashkin70725502013-06-25 20:27:06 +0000298
299 if (repeating) {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700300 mRepeatingRequestId = requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000301 }
302
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700303 if (mIdle) {
304 mDeviceHandler.post(mCallOnActive);
305 }
306 mIdle = false;
307
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700308 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000309 }
310 }
311
312 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700313 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700314 Handler handler) throws CameraAccessException {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700315 return submitCaptureRequest(request, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000316 }
317
318 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700319 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700320 Handler handler) throws CameraAccessException {
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700321 if (requests.isEmpty()) {
322 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700323 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700324 }
Igor Murashkin70725502013-06-25 20:27:06 +0000325 // TODO
326 throw new UnsupportedOperationException("Burst capture implemented yet");
327 }
328
329 @Override
330 public void stopRepeating() throws CameraAccessException {
331
332 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700333 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700334 if (mRepeatingRequestId != REQUEST_ID_NONE) {
335
336 int requestId = mRepeatingRequestId;
337 mRepeatingRequestId = REQUEST_ID_NONE;
338
339 // Queue for deletion after in-flight requests finish
340 mRepeatingRequestIdDeletedList.add(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000341
342 try {
343 mRemoteDevice.cancelRequest(requestId);
344 } catch (CameraRuntimeException e) {
345 throw e.asChecked();
346 } catch (RemoteException e) {
347 // impossible
348 return;
349 }
350 }
351 }
352 }
353
354 @Override
355 public void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700356
357 synchronized (mLock) {
358 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700359 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -0700360 throw new IllegalStateException("Active repeating request ongoing");
361 }
362
363 try {
364 mRemoteDevice.waitUntilIdle();
365 } catch (CameraRuntimeException e) {
366 throw e.asChecked();
367 } catch (RemoteException e) {
368 // impossible
369 return;
370 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700371
372 mRepeatingRequestId = REQUEST_ID_NONE;
373 mRepeatingRequestIdDeletedList.clear();
374 mCaptureListenerMap.clear();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700375 }
Igor Murashkin70725502013-06-25 20:27:06 +0000376 }
377
378 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700379 public void flush() throws CameraAccessException {
380 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700381 checkIfCameraClosed();
382
383 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700384 try {
385 mRemoteDevice.flush();
386 } catch (CameraRuntimeException e) {
387 throw e.asChecked();
388 } catch (RemoteException e) {
389 // impossible
390 return;
391 }
392 }
393 }
394
395 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700396 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000397 synchronized (mLock) {
398
399 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700400 if (mRemoteDevice != null) {
401 mRemoteDevice.disconnect();
402 }
Igor Murashkin70725502013-06-25 20:27:06 +0000403 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700404 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000405 } catch (RemoteException e) {
406 // impossible
407 }
408
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700409 if (mRemoteDevice != null) {
410 mDeviceHandler.post(mCallOnClosed);
411 }
Igor Murashkin70725502013-06-25 20:27:06 +0000412
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700413 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000414 }
415 }
416
417 @Override
418 protected void finalize() throws Throwable {
419 try {
420 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000421 }
422 finally {
423 super.finalize();
424 }
425 }
426
427 static class CaptureListenerHolder {
428
429 private final boolean mRepeating;
430 private final CaptureListener mListener;
431 private final CaptureRequest mRequest;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700432 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000433
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700434 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
435 boolean repeating) {
436 if (listener == null || handler == null) {
437 throw new UnsupportedOperationException(
438 "Must have a valid handler and a valid listener");
439 }
Igor Murashkin70725502013-06-25 20:27:06 +0000440 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700441 mHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000442 mRequest = request;
443 mListener = listener;
444 }
445
446 public boolean isRepeating() {
447 return mRepeating;
448 }
449
450 public CaptureListener getListener() {
451 return mListener;
452 }
453
454 public CaptureRequest getRequest() {
455 return mRequest;
456 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700457
458 public Handler getHandler() {
459 return mHandler;
460 }
461
Igor Murashkin70725502013-06-25 20:27:06 +0000462 }
463
Zhijun Heecb323e2013-07-31 09:40:27 -0700464 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000465
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700466 //
467 // Constants below need to be kept up-to-date with
468 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
469 //
470
471 //
472 // Error codes for onCameraError
473 //
474
475 /**
476 * Camera has been disconnected
477 */
478 static final int ERROR_CAMERA_DISCONNECTED = 0;
479
480 /**
481 * Camera has encountered a device-level error
482 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
483 */
484 static final int ERROR_CAMERA_DEVICE = 1;
485
486 /**
487 * Camera has encountered a service-level error
488 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
489 */
490 static final int ERROR_CAMERA_SERVICE = 2;
491
Igor Murashkin70725502013-06-25 20:27:06 +0000492 @Override
493 public IBinder asBinder() {
494 return this;
495 }
496
Igor Murashkin70725502013-06-25 20:27:06 +0000497 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700498 public void onCameraError(final int errorCode) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700499 Runnable r = null;
500 if (isClosed()) return;
501
502 synchronized(mLock) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700503 switch (errorCode) {
504 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700505 r = mCallOnDisconnected;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700506 break;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700507 default:
508 Log.e(TAG, "Unknown error from camera device: " + errorCode);
509 // no break
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700510 case ERROR_CAMERA_DEVICE:
511 case ERROR_CAMERA_SERVICE:
512 r = new Runnable() {
513 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700514 if (!CameraDevice.this.isClosed()) {
515 mDeviceListener.onError(CameraDevice.this, errorCode);
516 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700517 }
518 };
519 break;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700520 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700521 CameraDevice.this.mDeviceHandler.post(r);
Zhijun Heecb323e2013-07-31 09:40:27 -0700522 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700523 }
524
525 @Override
526 public void onCameraIdle() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700527 if (isClosed()) return;
528
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700529 if (DEBUG) {
530 Log.d(TAG, "Camera now idle");
531 }
532 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700533 if (!CameraDevice.this.mIdle) {
534 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
535 }
536 CameraDevice.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700537 }
538 }
539
540 @Override
541 public void onCaptureStarted(int requestId, final long timestamp) {
542 if (DEBUG) {
543 Log.d(TAG, "Capture started for id " + requestId);
544 }
545 final CaptureListenerHolder holder;
546
547 // Get the listener for this frame ID, if there is one
548 synchronized (mLock) {
549 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
550 }
551
552 if (holder == null) {
553 return;
554 }
555
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700556 if (isClosed()) return;
557
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700558 // Dispatch capture start notice
559 holder.getHandler().post(
560 new Runnable() {
561 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700562 if (!CameraDevice.this.isClosed()) {
563 holder.getListener().onCaptureStarted(
564 CameraDevice.this,
565 holder.getRequest(),
566 timestamp);
567 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700568 }
569 });
Igor Murashkin70725502013-06-25 20:27:06 +0000570 }
571
572 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700573 public void onResultReceived(int requestId, CameraMetadataNative result)
574 throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700575 if (DEBUG) {
576 Log.d(TAG, "Received result for id " + requestId);
577 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700578 final CaptureListenerHolder holder;
Igor Murashkin70725502013-06-25 20:27:06 +0000579
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800580 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
581 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
582
Igor Murashkin70725502013-06-25 20:27:06 +0000583 synchronized (mLock) {
584 // TODO: move this whole map into this class to make it more testable,
585 // exposing the methods necessary like subscribeToRequest, unsubscribe..
586 // TODO: make class static class
587
Zhijun Heecb323e2013-07-31 09:40:27 -0700588 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000589
590 // Clean up listener once we no longer expect to see it.
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800591 if (holder != null && !holder.isRepeating() && !quirkIsPartialResult) {
Zhijun Heecb323e2013-07-31 09:40:27 -0700592 CameraDevice.this.mCaptureListenerMap.remove(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000593 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700594
595 // TODO: add 'capture sequence completed' callback to the
596 // service, and clean up repeating requests there instead.
597
598 // If we received a result for a repeating request and have
599 // prior repeating requests queued for deletion, remove those
600 // requests from mCaptureListenerMap.
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800601 if (holder != null && holder.isRepeating() && !quirkIsPartialResult
Ruben Brunkdecfe952013-10-29 11:00:32 -0700602 && mRepeatingRequestIdDeletedList.size() > 0) {
603 Iterator<Integer> iter = mRepeatingRequestIdDeletedList.iterator();
604 while (iter.hasNext()) {
605 int deletedRequestId = iter.next();
606 if (deletedRequestId < requestId) {
607 CameraDevice.this.mCaptureListenerMap.remove(deletedRequestId);
608 iter.remove();
609 }
610 }
611 }
612
Igor Murashkin70725502013-06-25 20:27:06 +0000613 }
614
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700615 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000616 if (holder == null) {
Igor Murashkin70725502013-06-25 20:27:06 +0000617 return;
618 }
619
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700620 if (isClosed()) return;
621
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700622 final CaptureRequest request = holder.getRequest();
623 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000624
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800625 Runnable resultDispatch = null;
626
627 // Either send a partial result or the final capture completed result
628 if (quirkIsPartialResult) {
629 // Partial result
630 resultDispatch = new Runnable() {
631 @Override
632 public void run() {
633 if (!CameraDevice.this.isClosed()){
634 holder.getListener().onCapturePartial(
635 CameraDevice.this,
636 request,
637 resultAsCapture);
638 }
639 }
640 };
641 } else {
642 // Final capture result
643 resultDispatch = new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700644 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700645 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700646 if (!CameraDevice.this.isClosed()){
647 holder.getListener().onCaptureCompleted(
648 CameraDevice.this,
649 request,
650 resultAsCapture);
651 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700652 }
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800653 };
654 }
655
656 holder.getHandler().post(resultDispatch);
Igor Murashkin70725502013-06-25 20:27:06 +0000657 }
658
659 }
660
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700661 /**
662 * Default handler management. If handler is null, get the current thread's
663 * Looper to create a Handler with. If no looper exists, throw exception.
664 */
665 private Handler checkHandler(Handler handler) {
666 if (handler == null) {
667 Looper looper = Looper.myLooper();
668 if (looper == null) {
669 throw new IllegalArgumentException(
670 "No handler given, and current thread has no looper!");
671 }
672 handler = new Handler(looper);
673 }
674 return handler;
675 }
676
Zhijun He7f4d3142013-07-23 07:54:38 -0700677 private void checkIfCameraClosed() {
678 if (mRemoteDevice == null) {
679 throw new IllegalStateException("CameraDevice was already closed");
680 }
681 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700682
683 private boolean isClosed() {
684 synchronized(mLock) {
685 return (mRemoteDevice == null);
686 }
687 }
Igor Murashkin70725502013-06-25 20:27:06 +0000688}