OSDN Git Service

c428a17ef3874651dd5bfebf16b97dfedb2b8f78
[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.CaptureRequest;
23 import android.hardware.camera2.CaptureResult;
24 import android.hardware.camera2.ICameraDeviceCallbacks;
25 import android.hardware.camera2.ICameraDeviceUser;
26 import android.hardware.camera2.utils.CameraBinderDecorator;
27 import android.hardware.camera2.utils.CameraRuntimeException;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Looper;
31 import android.os.RemoteException;
32 import android.util.Log;
33 import android.util.SparseArray;
34 import android.view.Surface;
35
36 import java.util.ArrayList;
37 import java.util.HashSet;
38 import java.util.Iterator;
39 import java.util.List;
40
41 /**
42  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
43  */
44 public class CameraDevice implements android.hardware.camera2.CameraDevice {
45
46     private final String TAG;
47     private final boolean DEBUG;
48
49     private static final int REQUEST_ID_NONE = -1;
50
51     // 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
57     private final StateListener mDeviceListener;
58     private final Handler mDeviceHandler;
59
60     private boolean mIdle = true;
61
62     private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
63             new SparseArray<CaptureListenerHolder>();
64
65     private int mRepeatingRequestId = REQUEST_ID_NONE;
66     private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
67     // Map stream IDs to Surfaces
68     private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
69
70     private final String mCameraId;
71
72     // 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         }
135         mCameraId = cameraId;
136         mDeviceListener = listener;
137         mDeviceHandler = handler;
138         TAG = String.format("CameraDevice-%s-JV", mCameraId);
139         DEBUG = Log.isLoggable(TAG, Log.DEBUG);
140     }
141
142     public CameraDeviceCallbacks getCallbacks() {
143         return mCallbacks;
144     }
145
146     public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
147         // TODO: Move from decorator to direct binder-mediated exceptions
148         synchronized(mLock) {
149             mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
150
151             mDeviceHandler.post(mCallOnOpened);
152             mDeviceHandler.post(mCallOnUnconfigured);
153         }
154     }
155
156     @Override
157     public String getId() {
158         return mCameraId;
159     }
160
161     @Override
162     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
163         // Treat a null input the same an empty list
164         if (outputs == null) {
165             outputs = new ArrayList<Surface>();
166         }
167         synchronized (mLock) {
168             checkIfCameraClosed();
169
170             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
185             mDeviceHandler.post(mCallOnBusy);
186             stopRepeating();
187
188             try {
189                 waitUntilIdle();
190
191                 // TODO: mRemoteDevice.beginConfigure
192                 // 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) {
200                     // TODO: remove width,height,format since we are ignoring
201                     // it.
202                     int streamId = mRemoteDevice.createStream(0, 0, 0, s);
203                     mConfiguredOutputs.put(streamId, s);
204                 }
205
206                 // TODO: mRemoteDevice.endConfigure
207             } catch (CameraRuntimeException e) {
208                 if (e.getReason() == CAMERA_IN_USE) {
209                     throw new IllegalStateException("The camera is currently busy." +
210                             " You must wait until the previous operation completes.");
211                 }
212
213                 throw e.asChecked();
214             } catch (RemoteException e) {
215                 // impossible
216                 return;
217             }
218
219             if (outputs.size() > 0) {
220                 mDeviceHandler.post(mCallOnIdle);
221             } else {
222                 mDeviceHandler.post(mCallOnUnconfigured);
223             }
224         }
225     }
226
227     @Override
228     public CaptureRequest.Builder createCaptureRequest(int templateType)
229             throws CameraAccessException {
230         synchronized (mLock) {
231             checkIfCameraClosed();
232
233             CameraMetadataNative templatedRequest = new CameraMetadataNative();
234
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
244             CaptureRequest.Builder builder =
245                     new CaptureRequest.Builder(templatedRequest);
246
247             return builder;
248         }
249     }
250
251     @Override
252     public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
253             throws CameraAccessException {
254         return submitCaptureRequest(request, listener, handler, /*streaming*/false);
255     }
256
257     @Override
258     public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
259             Handler handler) throws CameraAccessException {
260         if (requests.isEmpty()) {
261             Log.w(TAG, "Capture burst request list is empty, do nothing!");
262             return -1;
263         }
264         // TODO
265         throw new UnsupportedOperationException("Burst capture implemented yet");
266
267     }
268
269     private int submitCaptureRequest(CaptureRequest request, CaptureListener listener,
270             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
274         if (listener != null) {
275             handler = checkHandler(handler);
276         }
277
278         synchronized (mLock) {
279             checkIfCameraClosed();
280             int requestId;
281
282             try {
283                 requestId = mRemoteDevice.submitRequest(request, repeating);
284             } catch (CameraRuntimeException e) {
285                 throw e.asChecked();
286             } catch (RemoteException e) {
287                 // impossible
288                 return -1;
289             }
290             if (listener != null) {
291                 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
292                         handler, repeating));
293             }
294
295             if (repeating) {
296                 // Queue for deletion after in-flight requests finish
297                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
298                     mRepeatingRequestIdDeletedList.add(mRepeatingRequestId);
299                 }
300                 mRepeatingRequestId = requestId;
301             }
302
303             if (mIdle) {
304                 mDeviceHandler.post(mCallOnActive);
305             }
306             mIdle = false;
307
308             return requestId;
309         }
310     }
311
312     @Override
313     public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
314             Handler handler) throws CameraAccessException {
315         return submitCaptureRequest(request, listener, handler, /*streaming*/true);
316     }
317
318     @Override
319     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
320             Handler handler) throws CameraAccessException {
321         if (requests.isEmpty()) {
322             Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
323             return -1;
324         }
325         // TODO
326         throw new UnsupportedOperationException("Burst capture implemented yet");
327     }
328
329     @Override
330     public void stopRepeating() throws CameraAccessException {
331
332         synchronized (mLock) {
333             checkIfCameraClosed();
334             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);
341
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 {
356
357         synchronized (mLock) {
358             checkIfCameraClosed();
359             if (mRepeatingRequestId != REQUEST_ID_NONE) {
360                 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             }
371
372             mRepeatingRequestId = REQUEST_ID_NONE;
373             mRepeatingRequestIdDeletedList.clear();
374             mCaptureListenerMap.clear();
375         }
376     }
377
378     @Override
379     public void flush() throws CameraAccessException {
380         synchronized (mLock) {
381             checkIfCameraClosed();
382
383             mDeviceHandler.post(mCallOnBusy);
384             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
396     public void close() {
397         synchronized (mLock) {
398
399             try {
400                 if (mRemoteDevice != null) {
401                     mRemoteDevice.disconnect();
402                 }
403             } catch (CameraRuntimeException e) {
404                 Log.e(TAG, "Exception while closing: ", e.asChecked());
405             } catch (RemoteException e) {
406                 // impossible
407             }
408
409             if (mRemoteDevice != null) {
410                 mDeviceHandler.post(mCallOnClosed);
411             }
412
413             mRemoteDevice = null;
414         }
415     }
416
417     @Override
418     protected void finalize() throws Throwable {
419         try {
420             close();
421         }
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;
432         private final Handler mHandler;
433
434         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             }
440             mRepeating = repeating;
441             mHandler = handler;
442             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         }
457
458         public Handler getHandler() {
459             return mHandler;
460         }
461
462     }
463
464     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
465
466         //
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
492         @Override
493         public IBinder asBinder() {
494             return this;
495         }
496
497         @Override
498         public void onCameraError(final int errorCode) {
499             Runnable r = null;
500             if (isClosed()) return;
501
502             synchronized(mLock) {
503                 switch (errorCode) {
504                     case ERROR_CAMERA_DISCONNECTED:
505                         r = mCallOnDisconnected;
506                         break;
507                     default:
508                         Log.e(TAG, "Unknown error from camera device: " + errorCode);
509                         // no break
510                     case ERROR_CAMERA_DEVICE:
511                     case ERROR_CAMERA_SERVICE:
512                         r = new Runnable() {
513                             public void run() {
514                                 if (!CameraDevice.this.isClosed()) {
515                                     mDeviceListener.onError(CameraDevice.this, errorCode);
516                                 }
517                             }
518                         };
519                         break;
520                 }
521                 CameraDevice.this.mDeviceHandler.post(r);
522             }
523         }
524
525         @Override
526         public void onCameraIdle() {
527             if (isClosed()) return;
528
529             if (DEBUG) {
530                 Log.d(TAG, "Camera now idle");
531             }
532             synchronized (mLock) {
533                 if (!CameraDevice.this.mIdle) {
534                     CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
535                 }
536                 CameraDevice.this.mIdle = true;
537             }
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
556             if (isClosed()) return;
557
558             // Dispatch capture start notice
559             holder.getHandler().post(
560                 new Runnable() {
561                     public void run() {
562                         if (!CameraDevice.this.isClosed()) {
563                             holder.getListener().onCaptureStarted(
564                                 CameraDevice.this,
565                                 holder.getRequest(),
566                                 timestamp);
567                         }
568                     }
569                 });
570         }
571
572         @Override
573         public void onResultReceived(int requestId, CameraMetadataNative result)
574                 throws RemoteException {
575             if (DEBUG) {
576                 Log.d(TAG, "Received result for id " + requestId);
577             }
578             final CaptureListenerHolder holder;
579
580             synchronized (mLock) {
581                 // TODO: move this whole map into this class to make it more testable,
582                 //        exposing the methods necessary like subscribeToRequest, unsubscribe..
583                 // TODO: make class static class
584
585                 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
586
587                 // Clean up listener once we no longer expect to see it.
588                 if (holder != null && !holder.isRepeating()) {
589                     CameraDevice.this.mCaptureListenerMap.remove(requestId);
590                 }
591
592                 // TODO: add 'capture sequence completed' callback to the
593                 // service, and clean up repeating requests there instead.
594
595                 // If we received a result for a repeating request and have
596                 // prior repeating requests queued for deletion, remove those
597                 // requests from mCaptureListenerMap.
598                 if (holder != null && holder.isRepeating()
599                         && mRepeatingRequestIdDeletedList.size() > 0) {
600                     Iterator<Integer> iter = mRepeatingRequestIdDeletedList.iterator();
601                     while (iter.hasNext()) {
602                         int deletedRequestId = iter.next();
603                         if (deletedRequestId < requestId) {
604                             CameraDevice.this.mCaptureListenerMap.remove(deletedRequestId);
605                             iter.remove();
606                         }
607                     }
608                 }
609
610             }
611
612             // Check if we have a listener for this
613             if (holder == null) {
614                 return;
615             }
616
617             if (isClosed()) return;
618
619             final CaptureRequest request = holder.getRequest();
620             final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
621
622             holder.getHandler().post(
623                 new Runnable() {
624                     @Override
625                     public void run() {
626                         if (!CameraDevice.this.isClosed()){
627                             holder.getListener().onCaptureCompleted(
628                                 CameraDevice.this,
629                                 request,
630                                 resultAsCapture);
631                         }
632                     }
633                 });
634         }
635
636     }
637
638     /**
639      * Default handler management. If handler is null, get the current thread's
640      * Looper to create a Handler with. If no looper exists, throw exception.
641      */
642     private Handler checkHandler(Handler handler) {
643         if (handler == null) {
644             Looper looper = Looper.myLooper();
645             if (looper == null) {
646                 throw new IllegalArgumentException(
647                     "No handler given, and current thread has no looper!");
648             }
649             handler = new Handler(looper);
650         }
651         return handler;
652     }
653
654     private void checkIfCameraClosed() {
655         if (mRemoteDevice == null) {
656             throw new IllegalStateException("CameraDevice was already closed");
657         }
658     }
659
660     private boolean isClosed() {
661         synchronized(mLock) {
662             return (mRemoteDevice == null);
663         }
664     }
665 }