OSDN Git Service

70a6f441ed0e0e758785549ebc3559f73a1c1a45
[android-x86/frameworks-base.git] / core / java / android / hardware / camera2 / impl / CameraDevice.java
1 /*
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
17 package android.hardware.camera2.impl;
18
19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
20
21 import android.hardware.camera2.CameraAccessException;
22 import android.hardware.camera2.CameraMetadata;
23 import android.hardware.camera2.CameraCharacteristics;
24 import android.hardware.camera2.CaptureRequest;
25 import android.hardware.camera2.CaptureResult;
26 import android.hardware.camera2.ICameraDeviceCallbacks;
27 import android.hardware.camera2.ICameraDeviceUser;
28 import android.hardware.camera2.utils.CameraBinderDecorator;
29 import android.hardware.camera2.utils.CameraRuntimeException;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.util.Log;
35 import android.util.SparseArray;
36 import android.view.Surface;
37
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Stack;
43
44 /**
45  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
46  */
47 public class CameraDevice implements android.hardware.camera2.CameraDevice {
48
49     private final String TAG;
50     private final boolean DEBUG;
51
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
58     private StateListener mDeviceListener;
59     private Handler mDeviceHandler;
60
61     private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
62             new SparseArray<CaptureListenerHolder>();
63
64     private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
65     // Map stream IDs to Surfaces
66     private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
67
68     private final String mCameraId;
69
70     public CameraDevice(String cameraId) {
71         mCameraId = cameraId;
72         TAG = String.format("CameraDevice-%s-JV", mCameraId);
73         DEBUG = Log.isLoggable(TAG, Log.DEBUG);
74     }
75
76     public CameraDeviceCallbacks getCallbacks() {
77         return mCallbacks;
78     }
79
80     public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
81         // TODO: Move from decorator to direct binder-mediated exceptions
82         mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
83     }
84
85     @Override
86     public String getId() {
87         return mCameraId;
88     }
89
90     @Override
91     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
92         synchronized (mLock) {
93             HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
94             List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete
95
96             // Determine which streams need to be created, which to be deleted
97             for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
98                 int streamId = mConfiguredOutputs.keyAt(i);
99                 Surface s = mConfiguredOutputs.valueAt(i);
100
101                 if (!outputs.contains(s)) {
102                     deleteList.add(streamId);
103                 } else {
104                     addSet.remove(s);  // Don't create a stream previously created
105                 }
106             }
107
108             try {
109                 // TODO: mRemoteDevice.beginConfigure
110
111                 // Delete all streams first (to free up HW resources)
112                 for (Integer streamId : deleteList) {
113                     mRemoteDevice.deleteStream(streamId);
114                     mConfiguredOutputs.delete(streamId);
115                 }
116
117                 // Add all new streams
118                 for (Surface s : addSet) {
119                     // TODO: remove width,height,format since we are ignoring
120                     // it.
121                     int streamId = mRemoteDevice.createStream(0, 0, 0, s);
122                     mConfiguredOutputs.put(streamId, s);
123                 }
124
125                 // TODO: mRemoteDevice.endConfigure
126             } catch (CameraRuntimeException e) {
127                 if (e.getReason() == CAMERA_IN_USE) {
128                     throw new IllegalStateException("The camera is currently busy." +
129                             " You must call waitUntilIdle before trying to reconfigure.");
130                 }
131
132                 throw e.asChecked();
133             } catch (RemoteException e) {
134                 // impossible
135                 return;
136             }
137         }
138     }
139
140     @Override
141     public CaptureRequest.Builder createCaptureRequest(int templateType)
142             throws CameraAccessException {
143         synchronized (mLock) {
144
145             CameraMetadataNative templatedRequest = new CameraMetadataNative();
146
147             try {
148                 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
149             } catch (CameraRuntimeException e) {
150                 throw e.asChecked();
151             } catch (RemoteException e) {
152                 // impossible
153                 return null;
154             }
155
156             CaptureRequest.Builder builder =
157                     new CaptureRequest.Builder(templatedRequest);
158
159             return builder;
160         }
161     }
162
163     @Override
164     public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
165             throws CameraAccessException {
166         return submitCaptureRequest(request, listener, handler, /*streaming*/false);
167     }
168
169     @Override
170     public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
171             Handler handler) throws CameraAccessException {
172         if (requests.isEmpty()) {
173             Log.w(TAG, "Capture burst request list is empty, do nothing!");
174             return -1;
175         }
176         // TODO
177         throw new UnsupportedOperationException("Burst capture implemented yet");
178
179     }
180
181     private int submitCaptureRequest(CaptureRequest request, CaptureListener listener,
182             Handler handler, boolean repeating) throws CameraAccessException {
183
184         // Need a valid handler, or current thread needs to have a looper, if
185         // listener is valid
186         if (handler == null && listener != null) {
187             Looper looper = Looper.myLooper();
188             if (looper == null) {
189                 throw new IllegalArgumentException(
190                         "No handler given, and current thread has no looper!");
191             }
192             handler = new Handler(looper);
193         }
194
195         synchronized (mLock) {
196
197             int requestId;
198
199             try {
200                 requestId = mRemoteDevice.submitRequest(request, repeating);
201             } catch (CameraRuntimeException e) {
202                 throw e.asChecked();
203             } catch (RemoteException e) {
204                 // impossible
205                 return -1;
206             }
207             if (listener != null) {
208                 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
209                         handler, repeating));
210             }
211
212             if (repeating) {
213                 mRepeatingRequestIdStack.add(requestId);
214             }
215
216             return requestId;
217         }
218     }
219
220     @Override
221     public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
222             Handler handler) throws CameraAccessException {
223         return submitCaptureRequest(request, listener, handler, /*streaming*/true);
224     }
225
226     @Override
227     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
228             Handler handler) throws CameraAccessException {
229         if (requests.isEmpty()) {
230             Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
231             return -1;
232         }
233         // TODO
234         throw new UnsupportedOperationException("Burst capture implemented yet");
235     }
236
237     @Override
238     public void stopRepeating() throws CameraAccessException {
239
240         synchronized (mLock) {
241
242             while (!mRepeatingRequestIdStack.isEmpty()) {
243                 int requestId = mRepeatingRequestIdStack.pop();
244
245                 try {
246                     mRemoteDevice.cancelRequest(requestId);
247                 } catch (CameraRuntimeException e) {
248                     throw e.asChecked();
249                 } catch (RemoteException e) {
250                     // impossible
251                     return;
252                 }
253             }
254         }
255     }
256
257     @Override
258     public void waitUntilIdle() throws CameraAccessException {
259
260         synchronized (mLock) {
261             checkIfCameraClosed();
262             if (!mRepeatingRequestIdStack.isEmpty()) {
263                 throw new IllegalStateException("Active repeating request ongoing");
264             }
265
266             try {
267                 mRemoteDevice.waitUntilIdle();
268             } catch (CameraRuntimeException e) {
269                 throw e.asChecked();
270             } catch (RemoteException e) {
271                 // impossible
272                 return;
273             }
274       }
275     }
276
277     @Override
278     public void setDeviceListener(StateListener listener, Handler handler) {
279         synchronized (mLock) {
280             mDeviceListener = listener;
281             mDeviceHandler = handler;
282         }
283     }
284
285     @Override
286     public void flush() throws CameraAccessException {
287         synchronized (mLock) {
288             try {
289                 mRemoteDevice.flush();
290             } catch (CameraRuntimeException e) {
291                 throw e.asChecked();
292             } catch (RemoteException e) {
293                 // impossible
294                 return;
295             }
296         }
297     }
298
299     @Override
300     public void close() {
301
302         // TODO: every method should throw IllegalStateException after close has been called
303
304         synchronized (mLock) {
305
306             try {
307                 if (mRemoteDevice != null) {
308                     mRemoteDevice.disconnect();
309                 }
310             } catch (CameraRuntimeException e) {
311                 Log.e(TAG, "Exception while closing: ", e.asChecked());
312             } catch (RemoteException e) {
313                 // impossible
314             }
315
316             mRemoteDevice = null;
317
318         }
319     }
320
321     @Override
322     protected void finalize() throws Throwable {
323         try {
324             close();
325         }
326         finally {
327             super.finalize();
328         }
329     }
330
331     static class CaptureListenerHolder {
332
333         private final boolean mRepeating;
334         private final CaptureListener mListener;
335         private final CaptureRequest mRequest;
336         private final Handler mHandler;
337
338         CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
339                 boolean repeating) {
340             if (listener == null || handler == null) {
341                 throw new UnsupportedOperationException(
342                     "Must have a valid handler and a valid listener");
343             }
344             mRepeating = repeating;
345             mHandler = handler;
346             mRequest = request;
347             mListener = listener;
348         }
349
350         public boolean isRepeating() {
351             return mRepeating;
352         }
353
354         public CaptureListener getListener() {
355             return mListener;
356         }
357
358         public CaptureRequest getRequest() {
359             return mRequest;
360         }
361
362         public Handler getHandler() {
363             return mHandler;
364         }
365
366     }
367
368     // TODO: unit tests
369     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
370
371         @Override
372         public IBinder asBinder() {
373             return this;
374         }
375
376         // TODO: consider rename to onMessageReceived
377         @Override
378         public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
379             if (DEBUG) {
380                 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
381             }
382             // TODO implement rest
383         }
384
385         @Override
386         public void onResultReceived(int requestId, CameraMetadataNative result)
387                 throws RemoteException {
388             if (DEBUG) {
389                 Log.d(TAG, "Received result for id " + requestId);
390             }
391             final CaptureListenerHolder holder;
392
393             synchronized (mLock) {
394                 // TODO: move this whole map into this class to make it more testable,
395                 //        exposing the methods necessary like subscribeToRequest, unsubscribe..
396                 // TODO: make class static class
397
398                 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
399
400                 // Clean up listener once we no longer expect to see it.
401
402                 // TODO: how to handle repeating listeners?
403                 // we probably want cancelRequest to return # of times it already enqueued and
404                 // keep a counter.
405                 if (holder != null && !holder.isRepeating()) {
406                     CameraDevice.this.mCaptureListenerMap.remove(requestId);
407                 }
408             }
409
410             // Check if we have a listener for this
411             if (holder == null) {
412                 return;
413             }
414
415             final CaptureRequest request = holder.getRequest();
416             final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
417
418             holder.getHandler().post(
419                 new Runnable() {
420                     @Override
421                     public void run() {
422                         holder.getListener().onCaptureCompleted(
423                             CameraDevice.this,
424                             request,
425                             resultAsCapture);
426                     }
427                 });
428         }
429
430     }
431
432     private void checkIfCameraClosed() {
433         if (mRemoteDevice == null) {
434             throw new IllegalStateException("CameraDevice was already closed");
435         }
436     }
437 }