Home | History | Annotate | Download | only in blocking
      1 /*
      2  * Copyright 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 com.android.ex.camera2.blocking;
     17 
     18 import android.hardware.camera2.CameraCaptureSession;
     19 import android.os.ConditionVariable;
     20 import android.util.Log;
     21 
     22 import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
     23 import com.android.ex.camera2.utils.StateChangeListener;
     24 import com.android.ex.camera2.utils.StateWaiter;
     25 
     26 import java.util.ArrayList;
     27 import java.util.concurrent.ExecutionException;
     28 import java.util.concurrent.Future;
     29 import java.util.concurrent.TimeUnit;
     30 import java.util.concurrent.TimeoutException;
     31 
     32 
     33 /**
     34  * A camera session listener that implements blocking operations on session state changes.
     35  *
     36  * <p>Provides a waiter that can be used to block until the next unobserved state of the
     37  * requested type arrives.</p>
     38  *
     39  * <p>Pass-through all StateCallback changes to the proxy.</p>
     40  *
     41  * @see #getStateWaiter
     42  */
     43 public class BlockingSessionCallback extends CameraCaptureSession.StateCallback {
     44     /**
     45      * Session is configured, ready for captures
     46      */
     47     public static final int SESSION_CONFIGURED = 0;
     48 
     49     /**
     50      * Session has failed to configure, can't do any captures
     51      */
     52     public static final int SESSION_CONFIGURE_FAILED = 1;
     53 
     54     /**
     55      * Session is ready
     56      */
     57     public static final int SESSION_READY = 2;
     58 
     59     /**
     60      * Session is active (transitory)
     61      */
     62     public static final int SESSION_ACTIVE = 3;
     63 
     64     /**
     65      * Session is closed
     66      */
     67     public static final int SESSION_CLOSED = 4;
     68 
     69     private final int NUM_STATES = 5;
     70 
     71     /*
     72      * Private fields
     73      */
     74     private static final String TAG = "BlockingSessionCallback";
     75     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     76 
     77     private final CameraCaptureSession.StateCallback mProxy;
     78     private final SessionFuture mSessionFuture = new SessionFuture();
     79 
     80     private final StateWaiter mStateWaiter = new StateWaiter(sStateNames);
     81     private final StateChangeListener mStateChangeListener = mStateWaiter.getListener();
     82 
     83     private static final String[] sStateNames = {
     84         "SESSION_CONFIGURED",
     85         "SESSION_CONFIGURE_FAILED",
     86         "SESSION_READY",
     87         "SESSION_ACTIVE",
     88         "SESSION_CLOSED"
     89     };
     90 
     91     /**
     92      * Create a blocking session listener without forwarding the session listener invocations
     93      * to another session listener.
     94      */
     95     public BlockingSessionCallback() {
     96         mProxy = null;
     97     }
     98 
     99     /**
    100      * Create a blocking session listener; forward original listener invocations
    101      * into {@code listener}.
    102      *
    103      * @param listener a non-{@code null} listener to forward invocations into
    104      *
    105      * @throws NullPointerException if {@code listener} was {@code null}
    106      */
    107     public BlockingSessionCallback(CameraCaptureSession.StateCallback listener) {
    108         if (listener == null) {
    109             throw new NullPointerException("listener must not be null");
    110         }
    111         mProxy = listener;
    112     }
    113 
    114     /**
    115      * Acquire the state waiter; can be used to block until a set of state transitions have
    116      * been reached.
    117      *
    118      * <p>Only one thread should wait at a time.</p>
    119      */
    120     public StateWaiter getStateWaiter() {
    121         return mStateWaiter;
    122     }
    123 
    124     /**
    125      * Return session if already have it; otherwise wait until any of the session listener
    126      * invocations fire and the session is available.
    127      *
    128      * <p>Does not consume any of the states from the state waiter.</p>
    129      *
    130      * @param timeoutMs how many milliseconds to wait for
    131      * @return a non-{@code null} {@link CameraCaptureSession} instance
    132      *
    133      * @throws TimeoutRuntimeException if waiting for more than {@long timeoutMs}
    134      */
    135     public CameraCaptureSession waitAndGetSession(long timeoutMs) {
    136         try {
    137             return mSessionFuture.get(timeoutMs, TimeUnit.MILLISECONDS);
    138         } catch (TimeoutException e) {
    139             throw new TimeoutRuntimeException(
    140                     String.format("Failed to get session after %s milliseconds", timeoutMs), e);
    141         }
    142     }
    143 
    144     /*
    145      * CameraCaptureSession.StateCallback implementation
    146      */
    147 
    148     @Override
    149     public void onActive(CameraCaptureSession session) {
    150         mSessionFuture.setSession(session);
    151         if (mProxy != null) mProxy.onActive(session);
    152         mStateChangeListener.onStateChanged(SESSION_ACTIVE);
    153     }
    154 
    155     @Override
    156     public void onClosed(CameraCaptureSession session) {
    157         mSessionFuture.setSession(session);
    158         if (mProxy != null) mProxy.onClosed(session);
    159         mStateChangeListener.onStateChanged(SESSION_CLOSED);
    160     }
    161 
    162     @Override
    163     public void onConfigured(CameraCaptureSession session) {
    164         mSessionFuture.setSession(session);
    165         if (mProxy != null) mProxy.onConfigured(session);
    166         mStateChangeListener.onStateChanged(SESSION_CONFIGURED);
    167     }
    168 
    169     @Override
    170     public void onConfigureFailed(CameraCaptureSession session) {
    171         mSessionFuture.setSession(session);
    172         if (mProxy != null) mProxy.onConfigureFailed(session);
    173         mStateChangeListener.onStateChanged(SESSION_CONFIGURE_FAILED);
    174     }
    175 
    176     @Override
    177     public void onReady(CameraCaptureSession session) {
    178         mSessionFuture.setSession(session);
    179         if (mProxy != null) mProxy.onReady(session);
    180         mStateChangeListener.onStateChanged(SESSION_READY);
    181     }
    182 
    183     private static class SessionFuture implements Future<CameraCaptureSession> {
    184         private volatile CameraCaptureSession mSession;
    185         ConditionVariable mCondVar = new ConditionVariable(/*opened*/false);
    186 
    187         public void setSession(CameraCaptureSession session) {
    188             mSession = session;
    189             mCondVar.open();
    190         }
    191 
    192         @Override
    193         public boolean cancel(boolean mayInterruptIfRunning) {
    194             return false; // don't allow canceling this task
    195         }
    196 
    197         @Override
    198         public boolean isCancelled() {
    199             return false; // can never cancel this task
    200         }
    201 
    202         @Override
    203         public boolean isDone() {
    204             return mSession != null;
    205         }
    206 
    207         @Override
    208         public CameraCaptureSession get() {
    209             mCondVar.block();
    210             return mSession;
    211         }
    212 
    213         @Override
    214         public CameraCaptureSession get(long timeout, TimeUnit unit) throws TimeoutException {
    215             long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS);
    216             if (!mCondVar.block(timeoutMs)) {
    217                 throw new TimeoutException(
    218                         "Failed to receive session after " + timeout + " " + unit);
    219             }
    220 
    221             if (mSession == null) {
    222                 throw new AssertionError();
    223             }
    224             return mSession;
    225         }
    226 
    227     }
    228 }
    229