OSDN Git Service

Camera2: Fix null-listener capture for CameraCaptureSession
[android-x86/frameworks-base.git] / core / java / android / hardware / camera2 / impl / CameraCaptureSessionImpl.java
1 /*
2  * Copyright (C) 2014 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 package android.hardware.camera2.impl;
17
18 import android.hardware.camera2.CameraAccessException;
19 import android.hardware.camera2.CameraCaptureSession;
20 import android.hardware.camera2.CameraDevice;
21 import android.hardware.camera2.CaptureRequest;
22 import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
23 import android.hardware.camera2.dispatch.BroadcastDispatcher;
24 import android.hardware.camera2.dispatch.Dispatchable;
25 import android.hardware.camera2.dispatch.DuckTypingDispatcher;
26 import android.hardware.camera2.dispatch.HandlerDispatcher;
27 import android.hardware.camera2.dispatch.InvokeDispatcher;
28 import android.hardware.camera2.dispatch.NullDispatcher;
29 import android.hardware.camera2.utils.TaskDrainer;
30 import android.hardware.camera2.utils.TaskSingleDrainer;
31 import android.os.Handler;
32 import android.util.Log;
33 import android.view.Surface;
34
35 import java.util.Arrays;
36 import java.util.List;
37
38 import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
39 import static com.android.internal.util.Preconditions.*;
40
41 public class CameraCaptureSessionImpl extends CameraCaptureSession {
42     private static final String TAG = "CameraCaptureSession";
43     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
44
45     /** User-specified set of surfaces used as the configuration outputs */
46     private final List<Surface> mOutputs;
47     /**
48      * User-specified state listener, used for outgoing events; calls to this object will be
49      * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
50      */
51     private final CameraCaptureSession.StateListener mStateListener;
52     /** User-specified state handler used for outgoing state listener events */
53     private final Handler mStateHandler;
54
55     /** Internal camera device; used to translate calls into existing deprecated API */
56     private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
57     /** Internal handler; used for all incoming events to preserve total order */
58     private final Handler mDeviceHandler;
59
60     /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
61     private final TaskDrainer<Integer> mSequenceDrainer;
62     /** Drain state transitions from ACTIVE -> IDLE */
63     private final TaskSingleDrainer mIdleDrainer;
64     /** Drain state transitions from BUSY -> IDLE */
65     private final TaskSingleDrainer mAbortDrainer;
66     /** Drain the UNCONFIGURED state transition */
67     private final TaskSingleDrainer mUnconfigureDrainer;
68
69     /** This session is closed; all further calls will throw ISE */
70     private boolean mClosed = false;
71     /** Do not unconfigure if this is set; another session will overwrite configuration */
72     private boolean mSkipUnconfigure = false;
73
74     /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
75     private boolean mAborting;
76
77     /**
78      * Create a new CameraCaptureSession.
79      *
80      * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
81      * There must be no pending actions
82      * (e.g. no pending captures, no repeating requests, no flush).</p>
83      */
84     CameraCaptureSessionImpl(List<Surface> outputs,
85             CameraCaptureSession.StateListener listener, Handler stateHandler,
86             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
87             Handler deviceStateHandler, boolean configureSuccess) {
88         if (outputs == null || outputs.isEmpty()) {
89             throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
90         } else if (listener == null) {
91             throw new IllegalArgumentException("listener must not be null");
92         }
93
94         // TODO: extra verification of outputs
95         mOutputs = outputs;
96         mStateHandler = checkHandler(stateHandler);
97         mStateListener = createUserStateListenerProxy(mStateHandler, listener);
98
99         mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
100         mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
101
102         /*
103          * Use the same handler as the device's StateListener for all the internal coming events
104          *
105          * This ensures total ordering between CameraDevice.StateListener and
106          * CameraDevice.CaptureListener events.
107          */
108         mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
109                 /*name*/"seq");
110         mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
111                 /*name*/"idle");
112         mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
113                 /*name*/"abort");
114         mUnconfigureDrainer = new TaskSingleDrainer(mDeviceHandler, new UnconfigureDrainListener(),
115                 /*name*/"unconf");
116
117         // CameraDevice should call configureOutputs and have it finish before constructing us
118
119         if (configureSuccess) {
120             mStateListener.onConfigured(this);
121             if (VERBOSE) Log.v(TAG, "ctor - Created session successfully");
122         } else {
123             mStateListener.onConfigureFailed(this);
124             mClosed = true; // do not fire any other callbacks, do not allow any other work
125             Log.e(TAG, "Failed to create capture session; configuration failed");
126         }
127     }
128
129     @Override
130     public CameraDevice getDevice() {
131         return mDeviceImpl;
132     }
133
134     @Override
135     public synchronized int capture(CaptureRequest request, CaptureListener listener,
136             Handler handler) throws CameraAccessException {
137         if (request == null) {
138             throw new IllegalArgumentException("request must not be null");
139         }
140
141         checkNotClosed();
142         checkLegalToCapture();
143
144         handler = checkHandler(handler, listener);
145
146         if (VERBOSE) {
147             Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" +
148                     " " + handler);
149         }
150
151         return addPendingSequence(mDeviceImpl.capture(request,
152                 createCaptureListenerProxy(handler, listener), mDeviceHandler));
153     }
154
155     @Override
156     public synchronized int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
157             Handler handler) throws CameraAccessException {
158         if (requests == null) {
159             throw new IllegalArgumentException("requests must not be null");
160         } else if (requests.isEmpty()) {
161             throw new IllegalArgumentException("requests must have at least one element");
162         }
163
164         checkNotClosed();
165         checkLegalToCapture();
166
167         handler = checkHandler(handler, listener);
168
169         if (VERBOSE) {
170             CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
171             Log.v(TAG, "captureBurst - requests " + Arrays.toString(requestArray) + ", listener " +
172                     listener + " handler" + "" + handler);
173         }
174
175         return addPendingSequence(mDeviceImpl.captureBurst(requests,
176                 createCaptureListenerProxy(handler, listener), mDeviceHandler));
177     }
178
179     @Override
180     public synchronized int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
181             Handler handler) throws CameraAccessException {
182         if (request == null) {
183             throw new IllegalArgumentException("request must not be null");
184         }
185
186         checkNotClosed();
187         checkLegalToCapture();
188
189         handler = checkHandler(handler, listener);
190
191         if (VERBOSE) {
192             Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener +
193                     " handler" + " " + handler);
194         }
195
196         return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
197                 createCaptureListenerProxy(handler, listener), mDeviceHandler));
198     }
199
200     @Override
201     public synchronized int setRepeatingBurst(List<CaptureRequest> requests,
202             CaptureListener listener, Handler handler) throws CameraAccessException {
203         if (requests == null) {
204             throw new IllegalArgumentException("requests must not be null");
205         } else if (requests.isEmpty()) {
206             throw new IllegalArgumentException("requests must have at least one element");
207         }
208
209         checkNotClosed();
210         checkLegalToCapture();
211
212         handler = checkHandler(handler, listener);
213
214         if (VERBOSE) {
215             CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
216             Log.v(TAG, "setRepeatingBurst - requests " + Arrays.toString(requestArray) +
217                     ", listener " + listener + " handler" + "" + handler);
218         }
219
220         return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
221                 createCaptureListenerProxy(handler, listener), mDeviceHandler));
222     }
223
224     @Override
225     public synchronized void stopRepeating() throws CameraAccessException {
226         checkNotClosed();
227
228         if (VERBOSE) {
229             Log.v(TAG, "stopRepeating");
230         }
231
232         mDeviceImpl.stopRepeating();
233     }
234
235     @Override
236     public synchronized void abortCaptures() throws CameraAccessException {
237         checkNotClosed();
238
239         if (VERBOSE) {
240             Log.v(TAG, "abortCaptures");
241         }
242
243         if (mAborting) {
244             Log.w(TAG, "abortCaptures - Session is already aborting; doing nothing");
245             return;
246         }
247
248         mAborting = true;
249         mAbortDrainer.taskStarted();
250
251         mDeviceImpl.flush();
252         // The next BUSY -> IDLE set of transitions will mark the end of the abort.
253     }
254
255     /**
256      * Replace this session with another session.
257      *
258      * <p>This is an optimization to avoid unconfiguring and then immediately having to
259      * reconfigure again.</p>
260      *
261      * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
262      * <p>
263      *
264      * @see CameraCaptureSession#close
265      */
266     synchronized void replaceSessionClose(CameraCaptureSession other) {
267         /*
268          * In order for creating new sessions to be fast, the new session should be created
269          * before the old session is closed.
270          *
271          * Otherwise the old session will always unconfigure if there is no new session to
272          * replace it.
273          *
274          * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
275          * to skip unconfigure if a new session is created before the captures are all drained,
276          * but this would introduce nondeterministic behavior.
277          */
278
279         if (VERBOSE) Log.v(TAG, "replaceSessionClose");
280
281         // #close was already called explicitly, keep going the slow route
282         if (mClosed) {
283             if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called");
284             return;
285         }
286
287         mSkipUnconfigure = true;
288         close();
289     }
290
291     @Override
292     public synchronized void close() {
293
294         if (mClosed) {
295             if (VERBOSE) Log.v(TAG, "close - reentering");
296             return;
297         }
298
299         if (VERBOSE) Log.v(TAG, "close - first time");
300
301         mClosed = true;
302
303         /*
304          * Flush out any repeating request. Since camera is closed, no new requests
305          * can be queued, and eventually the entire request queue will be drained.
306          *
307          * If the camera device was already closed, short circuit and do nothing; since
308          * no more internal device callbacks will fire anyway.
309          *
310          * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the
311          * camera. Once that's done, fire #onClosed.
312          */
313         try {
314             mDeviceImpl.stopRepeating();
315         } catch (IllegalStateException e) {
316             // OK: Camera device may already be closed, nothing else to do
317             Log.w(TAG, "The camera device was already closed: ", e);
318
319             // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
320             // or just suppress the ISE only and rely onClosed.
321             // Also skip any of the draining work if this is already closed.
322
323             // Short-circuit; queue listener immediately and return
324             mStateListener.onClosed(this);
325             return;
326         } catch (CameraAccessException e) {
327             // OK: close does not throw checked exceptions.
328             Log.e(TAG, "Exception while stopping repeating: ", e);
329
330             // TODO: call onError instead of onClosed if this happens
331         }
332
333         // If no sequences are pending, fire #onClosed immediately
334         mSequenceDrainer.beginDrain();
335     }
336
337     /**
338      * Post calls into a CameraCaptureSession.StateListener to the user-specified {@code handler}.
339      */
340     private StateListener createUserStateListenerProxy(Handler handler, StateListener listener) {
341         InvokeDispatcher<StateListener> userListenerSink = new InvokeDispatcher<>(listener);
342         HandlerDispatcher<StateListener> handlerPassthrough =
343                 new HandlerDispatcher<>(userListenerSink, handler);
344
345         return new ListenerProxies.SessionStateListenerProxy(handlerPassthrough);
346     }
347
348     /**
349      * Forward callbacks from
350      * CameraDevice.CaptureListener to the CameraCaptureSession.CaptureListener.
351      *
352      * <p>In particular, all calls are automatically split to go both to our own
353      * internal listener, and to the user-specified listener (by transparently posting
354      * to the user-specified handler).</p>
355      *
356      * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
357      */
358     @SuppressWarnings("deprecation")
359     private CameraDevice.CaptureListener createCaptureListenerProxy(
360             Handler handler, CaptureListener listener) {
361         CameraDevice.CaptureListener localListener = new CameraDevice.CaptureListener() {
362             @Override
363             public void onCaptureSequenceCompleted(CameraDevice camera,
364                     int sequenceId, long frameNumber) {
365                 finishPendingSequence(sequenceId);
366             }
367
368             @Override
369             public void onCaptureSequenceAborted(CameraDevice camera,
370                     int sequenceId) {
371                 finishPendingSequence(sequenceId);
372             }
373         };
374
375         /*
376          * Split the calls from the device listener into local listener and the following chain:
377          * - replace the first CameraDevice arg with a CameraCaptureSession
378          * - duck type from device listener to session listener
379          * - then forward the call to a handler
380          * - then finally invoke the destination method on the session listener object
381          */
382         if (listener == null) {
383             // OK: API allows the user to not specify a listener, and the handler may
384             // also be null in that case. Collapse whole dispatch chain to only call the local
385             // listener
386             return localListener;
387         }
388
389         InvokeDispatcher<CameraDevice.CaptureListener> localSink =
390                 new InvokeDispatcher<>(localListener);
391
392         InvokeDispatcher<CaptureListener> userListenerSink =
393                 new InvokeDispatcher<>(listener);
394         HandlerDispatcher<CaptureListener> handlerPassthrough =
395                 new HandlerDispatcher<>(userListenerSink, handler);
396         DuckTypingDispatcher<CameraDevice.CaptureListener, CaptureListener> duckToSession
397                 = new DuckTypingDispatcher<>(handlerPassthrough, CaptureListener.class);
398         ArgumentReplacingDispatcher<CameraDevice.CaptureListener, CameraCaptureSessionImpl>
399             replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
400                     /*argumentIndex*/0, this);
401
402         BroadcastDispatcher<CameraDevice.CaptureListener> broadcaster =
403                 new BroadcastDispatcher<CameraDevice.CaptureListener>(
404                         replaceDeviceWithSession,
405                         localSink);
406
407         return new ListenerProxies.DeviceCaptureListenerProxy(broadcaster);
408     }
409
410     /**
411      *
412      * Create an internal state listener, to be invoked on the mDeviceHandler
413      *
414      * <p>It has a few behaviors:
415      * <ul>
416      * <li>Convert device state changes into session state changes.
417      * <li>Keep track of async tasks that the session began (idle, abort).
418      * </ul>
419      * </p>
420      * */
421     CameraDevice.StateListener getDeviceStateListener() {
422         final CameraCaptureSession session = this;
423
424         return new CameraDevice.StateListener() {
425             private boolean mBusy = false;
426             private boolean mActive = false;
427
428             @Override
429             public void onOpened(CameraDevice camera) {
430                 throw new AssertionError("Camera must already be open before creating a session");
431             }
432
433             @Override
434             public void onDisconnected(CameraDevice camera) {
435                 close();
436             }
437
438             @Override
439             public void onError(CameraDevice camera, int error) {
440                 // TODO: Handle errors somehow.
441                 Log.wtf(TAG, "Got device error " + error);
442             }
443
444             @Override
445             public void onActive(CameraDevice camera) {
446                 mIdleDrainer.taskStarted();
447                 mActive = true;
448
449                 mStateListener.onActive(session);
450             }
451
452             @Override
453             public void onIdle(CameraDevice camera) {
454                 boolean isAborting;
455                 synchronized (session) {
456                     isAborting = mAborting;
457                 }
458
459                 /*
460                  * Check which states we transitioned through:
461                  *
462                  * (ACTIVE -> IDLE)
463                  * (BUSY -> IDLE)
464                  *
465                  * Note that this is also legal:
466                  * (ACTIVE -> BUSY -> IDLE)
467                  *
468                  * and mark those tasks as finished
469                  */
470                 if (mBusy && isAborting) {
471                     mAbortDrainer.taskFinished();
472
473                     synchronized (session) {
474                         mAborting = false;
475                     }
476                 }
477
478                 if (mActive) {
479                     mIdleDrainer.taskFinished();
480                 }
481
482                 mBusy = false;
483                 mActive = false;
484
485                 mStateListener.onReady(session);
486             }
487
488             @Override
489             public void onBusy(CameraDevice camera) {
490                 mBusy = true;
491
492                 // TODO: Queue captures during abort instead of failing them
493                 // since the app won't be able to distinguish the two actives
494                 Log.w(TAG, "Device is now busy; do not submit new captures (TODO: allow this)");
495                 mStateListener.onActive(session);
496             }
497
498             @Override
499             public void onUnconfigured(CameraDevice camera) {
500                 synchronized (session) {
501                     // Ignore #onUnconfigured before #close is called
502                     if (mClosed) {
503                         mUnconfigureDrainer.taskFinished();
504                     }
505                 }
506             }
507         };
508
509     }
510
511     @Override
512     protected void finalize() throws Throwable {
513         try {
514             close();
515         } finally {
516             super.finalize();
517         }
518     }
519
520     private void checkLegalToCapture() {
521         if (mAborting) {
522             throw new IllegalStateException(
523                     "Session is aborting captures; new captures are not permitted");
524         }
525     }
526
527     private void checkNotClosed() {
528         if (mClosed) {
529             throw new IllegalStateException(
530                     "Session has been closed; further changes are illegal.");
531         }
532     }
533
534     /**
535      * Notify the session that a pending capture sequence has just been queued.
536      *
537      * <p>During a shutdown/close, the session waits until all pending sessions are finished
538      * before taking any further steps to shut down itself.</p>
539      *
540      * @see #finishPendingSequence
541      */
542     private int addPendingSequence(int sequenceId) {
543         mSequenceDrainer.taskStarted(sequenceId);
544         return sequenceId;
545     }
546
547     /**
548      * Notify the session that a pending capture sequence is now finished.
549      *
550      * <p>During a shutdown/close, once all pending sequences finish, it is safe to
551      * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
552      */
553     private void finishPendingSequence(int sequenceId) {
554         mSequenceDrainer.taskFinished(sequenceId);
555     }
556
557     private class SequenceDrainListener implements TaskDrainer.DrainListener {
558         @Override
559         public void onDrained() {
560             /*
561              * No repeating request is set; and the capture queue has fully drained.
562              *
563              * If no captures were queued to begin with, and an abort was queued,
564              * it's still possible to get another BUSY before the last IDLE.
565              *
566              * If the camera is already "IDLE" and no aborts are pending,
567              * then the drain immediately finishes.
568              */
569             mAbortDrainer.beginDrain();
570         }
571     }
572
573     private class AbortDrainListener implements TaskDrainer.DrainListener {
574         @Override
575         public void onDrained() {
576             synchronized (CameraCaptureSessionImpl.this) {
577                 /*
578                  * Any queued aborts have now completed.
579                  *
580                  * It's now safe to wait to receive the final "IDLE" event, as the camera device
581                  * will no longer again transition to "ACTIVE" by itself.
582                  *
583                  * If the camera is already "IDLE", then the drain immediately finishes.
584                  */
585                 mIdleDrainer.beginDrain();
586             }
587         }
588     }
589
590     private class IdleDrainListener implements TaskDrainer.DrainListener {
591         @Override
592         public void onDrained() {
593             synchronized (CameraCaptureSessionImpl.this) {
594                 /*
595                  * The device is now IDLE, and has settled. It will not transition to
596                  * ACTIVE or BUSY again by itself.
597                  *
598                  * It's now safe to unconfigure the outputs and after it's done invoke #onClosed.
599                  *
600                  * This operation is idempotent; a session will not be closed twice.
601                  */
602
603                 // Fast path: A new capture session has replaced this one; don't unconfigure.
604                 if (mSkipUnconfigure) {
605                     mStateListener.onClosed(CameraCaptureSessionImpl.this);
606                     return;
607                 }
608
609                 // Slow path: #close was called explicitly on this session; unconfigure first
610
611                 try {
612                     mUnconfigureDrainer.taskStarted();
613                     mDeviceImpl.configureOutputs(null); // begin transition to unconfigured state
614                 } catch (CameraAccessException e) {
615                     // OK: do not throw checked exceptions.
616                     Log.e(TAG, "Exception while configuring outputs: ", e);
617
618                     // TODO: call onError instead of onClosed if this happens
619                 }
620
621                 mUnconfigureDrainer.beginDrain();
622             }
623         }
624     }
625
626     private class UnconfigureDrainListener implements TaskDrainer.DrainListener {
627         @Override
628         public void onDrained() {
629             synchronized (CameraCaptureSessionImpl.this) {
630                 // The device has finished unconfiguring. It's now fully closed.
631                 mStateListener.onClosed(CameraCaptureSessionImpl.this);
632             }
633         }
634     }
635 }