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