blob: fa790510b46b5897ab1daf272163dc1979cc9c42 [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
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070019import android.hardware.camera2.CameraMetadata;
20import android.hardware.camera2.CaptureResult;
21import android.hardware.camera2.ICameraDeviceUser;
22import android.hardware.camera2.ICameraDeviceCallbacks;
23import android.hardware.camera2.CameraAccessException;
24import android.hardware.camera2.CameraProperties;
25import android.hardware.camera2.CaptureRequest;
26import android.hardware.camera2.utils.CameraRuntimeException;
Igor Murashkin70725502013-06-25 20:27:06 +000027import android.os.IBinder;
28import android.os.RemoteException;
29import android.util.Log;
30import android.view.Surface;
31
32import java.util.HashMap;
33import java.util.List;
34import java.util.Stack;
35
36/**
37 * HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate
38 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070039public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000040
41 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070042 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000043
44 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
45 private ICameraDeviceUser mRemoteDevice;
46
47 private final Object mLock = new Object();
48 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
49
50 // XX: Make this a WeakReference<CaptureListener> ?
Igor Murashkind7bf1772013-07-12 18:01:31 -070051 // TODO: Convert to SparseIntArray
Igor Murashkin70725502013-06-25 20:27:06 +000052 private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap =
53 new HashMap<Integer, CaptureListenerHolder>();
54
55 private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
56
57 private final String mCameraId;
58
59 public CameraDevice(String cameraId) {
60 mCameraId = cameraId;
61 TAG = String.format("CameraDevice-%s-JV", mCameraId);
Zhijun Heecb323e2013-07-31 09:40:27 -070062 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +000063 }
64
65 public CameraDeviceCallbacks getCallbacks() {
66 return mCallbacks;
67 }
68
Igor Murashkin70725502013-06-25 20:27:06 +000069 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
70 mRemoteDevice = remoteDevice;
71 }
72
73 @Override
74 public CameraProperties getProperties() throws CameraAccessException {
Igor Murashkind7bf1772013-07-12 18:01:31 -070075
76 CameraProperties properties = new CameraProperties();
77 CameraMetadata info = new CameraMetadata();
78
79 try {
80 mRemoteDevice.getCameraInfo(/*out*/info);
81 } catch(CameraRuntimeException e) {
82 throw e.asChecked();
83 } catch(RemoteException e) {
84 // impossible
85 return null;
86 }
87
88 properties.swap(info);
89 return properties;
Igor Murashkin70725502013-06-25 20:27:06 +000090 }
91
92 @Override
93 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
94 synchronized (mLock) {
95 // TODO: delete outputs that aren't in this list that were configured previously
96 for (Surface s : outputs) {
97 try {
98 // TODO: remove width,height,format since we are ignoring
99 // it.
100 mRemoteDevice.createStream(0, 0, 0, s);
101 } catch (CameraRuntimeException e) {
102 throw e.asChecked();
103 } catch (RemoteException e) {
104 // impossible
105 return;
106 }
107 }
108 }
109 }
110
111 @Override
112 public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException {
113
114 synchronized (mLock) {
115
116 CameraMetadata templatedRequest = new CameraMetadata();
117
118 try {
119 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
120 } catch (CameraRuntimeException e) {
121 throw e.asChecked();
122 } catch (RemoteException e) {
123 // impossible
124 return null;
125 }
126
127 CaptureRequest request = new CaptureRequest();
Igor Murashkin70725502013-06-25 20:27:06 +0000128 request.swap(templatedRequest);
129
130 return request;
131
132 }
133 }
134
135 @Override
136 public void capture(CaptureRequest request, CaptureListener listener)
137 throws CameraAccessException {
138 submitCaptureRequest(request, listener, /*streaming*/false);
139 }
140
141 @Override
142 public void captureBurst(List<CaptureRequest> requests, CaptureListener listener)
143 throws CameraAccessException {
144 // TODO
145 throw new UnsupportedOperationException("Burst capture implemented yet");
146
147 }
148
149 private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
150 boolean repeating) throws CameraAccessException {
151
152 synchronized (mLock) {
153
154 int requestId;
155
156 try {
157 requestId = mRemoteDevice.submitRequest(request, repeating);
158 } catch (CameraRuntimeException e) {
159 throw e.asChecked();
160 } catch (RemoteException e) {
161 // impossible
162 return;
163 }
164
165 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
166 repeating));
167
168 if (repeating) {
169 mRepeatingRequestIdStack.add(requestId);
170 }
171
172 }
173 }
174
175 @Override
176 public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
177 throws CameraAccessException {
178 submitCaptureRequest(request, listener, /*streaming*/true);
179 }
180
181 @Override
182 public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
183 throws CameraAccessException {
184 // TODO
185 throw new UnsupportedOperationException("Burst capture implemented yet");
186 }
187
188 @Override
189 public void stopRepeating() throws CameraAccessException {
190
191 synchronized (mLock) {
192
193 while (!mRepeatingRequestIdStack.isEmpty()) {
194 int requestId = mRepeatingRequestIdStack.pop();
195
196 try {
197 mRemoteDevice.cancelRequest(requestId);
198 } catch (CameraRuntimeException e) {
199 throw e.asChecked();
200 } catch (RemoteException e) {
201 // impossible
202 return;
203 }
204 }
205 }
206 }
207
208 @Override
209 public void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700210
211 synchronized (mLock) {
212 checkIfCameraClosed();
213 if (!mRepeatingRequestIdStack.isEmpty()) {
214 throw new IllegalStateException("Active repeating request ongoing");
215 }
216
217 try {
218 mRemoteDevice.waitUntilIdle();
219 } catch (CameraRuntimeException e) {
220 throw e.asChecked();
221 } catch (RemoteException e) {
222 // impossible
223 return;
224 }
225 }
Igor Murashkin70725502013-06-25 20:27:06 +0000226 }
227
228 @Override
229 public void setErrorListener(ErrorListener listener) {
230 // TODO Auto-generated method stub
231
232 }
233
234 @Override
235 public void close() throws Exception {
236
237 // TODO: every method should throw IllegalStateException after close has been called
238
239 synchronized (mLock) {
240
241 try {
242 mRemoteDevice.disconnect();
243 } catch (CameraRuntimeException e) {
244 throw e.asChecked();
245 } catch (RemoteException e) {
246 // impossible
247 }
248
249 mRemoteDevice = null;
250
251 }
252 }
253
254 @Override
255 protected void finalize() throws Throwable {
256 try {
257 close();
258 } catch (CameraRuntimeException e) {
259 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
260 }
261 finally {
262 super.finalize();
263 }
264 }
265
266 static class CaptureListenerHolder {
267
268 private final boolean mRepeating;
269 private final CaptureListener mListener;
270 private final CaptureRequest mRequest;
271
272 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, boolean repeating) {
273 mRepeating = repeating;
274 mRequest = request;
275 mListener = listener;
276 }
277
278 public boolean isRepeating() {
279 return mRepeating;
280 }
281
282 public CaptureListener getListener() {
283 return mListener;
284 }
285
286 public CaptureRequest getRequest() {
287 return mRequest;
288 }
289 }
290
291 // TODO: unit tests
Zhijun Heecb323e2013-07-31 09:40:27 -0700292 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000293
294 @Override
295 public IBinder asBinder() {
296 return this;
297 }
298
299 // TODO: consider rename to onMessageReceived
300 @Override
301 public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
Zhijun Heecb323e2013-07-31 09:40:27 -0700302 if (DEBUG) {
303 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
304 }
Igor Murashkin70725502013-06-25 20:27:06 +0000305 // TODO implement rest
306 }
307
308 @Override
Zhijun Heecb323e2013-07-31 09:40:27 -0700309 public void onResultReceived(int requestId, CameraMetadata result) throws RemoteException {
310 if (DEBUG) {
311 Log.d(TAG, "Received result for id " + requestId);
312 }
Igor Murashkin70725502013-06-25 20:27:06 +0000313 CaptureListenerHolder holder;
314
315 synchronized (mLock) {
316 // TODO: move this whole map into this class to make it more testable,
317 // exposing the methods necessary like subscribeToRequest, unsubscribe..
318 // TODO: make class static class
319
Zhijun Heecb323e2013-07-31 09:40:27 -0700320 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000321
322 // Clean up listener once we no longer expect to see it.
323
324 // TODO: how to handle repeating listeners?
325 // we probably want cancelRequest to return # of times it already enqueued and
326 // keep a counter.
327 if (holder != null && !holder.isRepeating()) {
Zhijun Heecb323e2013-07-31 09:40:27 -0700328 CameraDevice.this.mCaptureListenerMap.remove(requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000329 }
330 }
331
332 if (holder == null) {
333 Log.e(TAG, "Result had no listener holder associated with it, dropping result");
334 return;
335 }
336
337 CaptureResult resultAsCapture = new CaptureResult();
338 resultAsCapture.swap(result);
339
340 if (holder.getListener() != null) {
341 holder.getListener().onCaptureComplete(CameraDevice.this, holder.getRequest(),
342 resultAsCapture);
343 }
344 }
345
346 }
347
Zhijun He7f4d3142013-07-23 07:54:38 -0700348 private void checkIfCameraClosed() {
349 if (mRemoteDevice == null) {
350 throw new IllegalStateException("CameraDevice was already closed");
351 }
352 }
Igor Murashkin70725502013-06-25 20:27:06 +0000353}