Home | History | Annotate | Download | only in legacy
      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 
     17 package android.hardware.camera2.legacy;
     18 
     19 import android.hardware.camera2.impl.CameraDeviceImpl;
     20 import android.hardware.camera2.impl.CameraMetadataNative;
     21 import android.os.Handler;
     22 import android.util.Log;
     23 
     24 /**
     25  * Emulates a the state of a single Camera2 device.
     26  *
     27  * <p>
     28  * This class acts as the state machine for a camera device.  Valid state transitions are given
     29  * in the table below:
     30  * </p>
     31  *
     32  * <ul>
     33  *      <li>{@code UNCONFIGURED -> CONFIGURING}</li>
     34  *      <li>{@code CONFIGURING -> IDLE}</li>
     35  *      <li>{@code IDLE -> CONFIGURING}</li>
     36  *      <li>{@code IDLE -> CAPTURING}</li>
     37  *      <li>{@code IDLE -> IDLE}</li>
     38  *      <li>{@code CAPTURING -> IDLE}</li>
     39  *      <li>{@code ANY -> ERROR}</li>
     40  * </ul>
     41  */
     42 public class CameraDeviceState {
     43     private static final String TAG = "CameraDeviceState";
     44     private static final boolean DEBUG = false;
     45 
     46     private static final int STATE_ERROR = 0;
     47     private static final int STATE_UNCONFIGURED = 1;
     48     private static final int STATE_CONFIGURING = 2;
     49     private static final int STATE_IDLE = 3;
     50     private static final int STATE_CAPTURING = 4;
     51 
     52     private static final String[] sStateNames = { "ERROR", "UNCONFIGURED", "CONFIGURING", "IDLE",
     53             "CAPTURING"};
     54 
     55     private int mCurrentState = STATE_UNCONFIGURED;
     56     private int mCurrentError = NO_CAPTURE_ERROR;
     57 
     58     private RequestHolder mCurrentRequest = null;
     59 
     60     private Handler mCurrentHandler = null;
     61     private CameraDeviceStateListener mCurrentListener = null;
     62 
     63     /**
     64      * Error code used by {@link #setCaptureStart} and {@link #setCaptureResult} to indicate that no
     65      * error has occurred.
     66      */
     67     public static final int NO_CAPTURE_ERROR = -1;
     68 
     69     /**
     70      * CameraDeviceStateListener callbacks to be called after state transitions.
     71      */
     72     public interface CameraDeviceStateListener {
     73         void onError(int errorCode, Object errorArg, RequestHolder holder);
     74         void onConfiguring();
     75         void onIdle();
     76         void onBusy();
     77         void onCaptureStarted(RequestHolder holder, long timestamp);
     78         void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
     79         void onRepeatingRequestError(long lastFrameNumber);
     80     }
     81 
     82     /**
     83      * Transition to the {@code ERROR} state.
     84      *
     85      * <p>
     86      * The device cannot exit the {@code ERROR} state.  If the device was not already in the
     87      * {@code ERROR} state, {@link CameraDeviceStateListener#onError(int, RequestHolder)} will be
     88      * called.
     89      * </p>
     90      *
     91      * @param error the error to set.  Should be one of the error codes defined in
     92      *      {@link CameraDeviceImpl.CameraDeviceCallbacks}.
     93      */
     94     public synchronized void setError(int error) {
     95         mCurrentError = error;
     96         doStateTransition(STATE_ERROR);
     97     }
     98 
     99     /**
    100      * Transition to the {@code CONFIGURING} state, or {@code ERROR} if in an invalid state.
    101      *
    102      * <p>
    103      * If the device was not already in the {@code CONFIGURING} state,
    104      * {@link CameraDeviceStateListener#onConfiguring()} will be called.
    105      * </p>
    106      *
    107      * @return {@code false} if an error has occurred.
    108      */
    109     public synchronized boolean setConfiguring() {
    110         doStateTransition(STATE_CONFIGURING);
    111         return mCurrentError == NO_CAPTURE_ERROR;
    112     }
    113 
    114     /**
    115      * Transition to the {@code IDLE} state, or {@code ERROR} if in an invalid state.
    116      *
    117      * <p>
    118      * If the device was not already in the {@code IDLE} state,
    119      * {@link CameraDeviceStateListener#onIdle()} will be called.
    120      * </p>
    121      *
    122      * @return {@code false} if an error has occurred.
    123      */
    124     public synchronized boolean setIdle() {
    125         doStateTransition(STATE_IDLE);
    126         return mCurrentError == NO_CAPTURE_ERROR;
    127     }
    128 
    129     /**
    130      * Transition to the {@code CAPTURING} state, or {@code ERROR} if in an invalid state.
    131      *
    132      * <p>
    133      * If the device was not already in the {@code CAPTURING} state,
    134      * {@link CameraDeviceStateListener#onCaptureStarted(RequestHolder)} will be called.
    135      * </p>
    136      *
    137      * @param request A {@link RequestHolder} containing the request for the current capture.
    138      * @param timestamp The timestamp of the capture start in nanoseconds.
    139      * @param captureError Report a recoverable error for a single request using a valid
    140      *                     error code for {@code ICameraDeviceCallbacks}, or
    141      *                     {@link #NO_CAPTURE_ERROR}
    142      * @return {@code false} if an error has occurred.
    143      */
    144     public synchronized boolean setCaptureStart(final RequestHolder request, long timestamp,
    145                                             int captureError) {
    146         mCurrentRequest = request;
    147         doStateTransition(STATE_CAPTURING, timestamp, captureError);
    148         return mCurrentError == NO_CAPTURE_ERROR;
    149     }
    150 
    151     /**
    152      * Set the result for a capture.
    153      *
    154      * <p>
    155      * If the device was in the {@code CAPTURING} state,
    156      * {@link CameraDeviceStateListener#onCaptureResult(CameraMetadataNative, RequestHolder)} will
    157      * be called with the given result, otherwise this will result in the device transitioning to
    158      * the {@code ERROR} state,
    159      * </p>
    160      *
    161      * @param request The {@link RequestHolder} request that created this result.
    162      * @param result The {@link CameraMetadataNative} result to set.
    163      * @param captureError Report a recoverable error for a single buffer or result using a valid
    164      *                     error code for {@code ICameraDeviceCallbacks}, or
    165      *                     {@link #NO_CAPTURE_ERROR}.
    166      * @param captureErrorArg An argument for some error captureError codes.
    167      * @return {@code false} if an error has occurred.
    168      */
    169     public synchronized boolean setCaptureResult(final RequestHolder request,
    170             final CameraMetadataNative result,
    171             final int captureError, final Object captureErrorArg) {
    172         if (mCurrentState != STATE_CAPTURING) {
    173             Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
    174             mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    175             doStateTransition(STATE_ERROR);
    176             return mCurrentError == NO_CAPTURE_ERROR;
    177         }
    178 
    179         if (mCurrentHandler != null && mCurrentListener != null) {
    180             if (captureError != NO_CAPTURE_ERROR) {
    181                 mCurrentHandler.post(new Runnable() {
    182                     @Override
    183                     public void run() {
    184                         mCurrentListener.onError(captureError, captureErrorArg, request);
    185                     }
    186                 });
    187             } else {
    188                 mCurrentHandler.post(new Runnable() {
    189                     @Override
    190                     public void run() {
    191                         mCurrentListener.onCaptureResult(result, request);
    192                     }
    193                 });
    194             }
    195         }
    196         return mCurrentError == NO_CAPTURE_ERROR;
    197     }
    198 
    199     public synchronized boolean setCaptureResult(final RequestHolder request,
    200             final CameraMetadataNative result) {
    201         return setCaptureResult(request, result, NO_CAPTURE_ERROR, /*errorArg*/null);
    202     }
    203 
    204     /**
    205      * Set repeating request error.
    206      *
    207      * <p>Repeating request has been stopped due to an error such as abandoned output surfaces.</p>
    208      *
    209      * @param lastFrameNumber Frame number of the last repeating request before it is stopped.
    210      */
    211     public synchronized void setRepeatingRequestError(final long lastFrameNumber) {
    212         mCurrentHandler.post(new Runnable() {
    213             @Override
    214             public void run() {
    215                 mCurrentListener.onRepeatingRequestError(lastFrameNumber);
    216             }
    217         });
    218     }
    219 
    220     /**
    221      * Set the listener for state transition callbacks.
    222      *
    223      * @param handler handler on which to call the callbacks.
    224      * @param listener the {@link CameraDeviceStateListener} callbacks to call.
    225      */
    226     public synchronized void setCameraDeviceCallbacks(Handler handler,
    227                                                       CameraDeviceStateListener listener) {
    228         mCurrentHandler = handler;
    229         mCurrentListener = listener;
    230     }
    231 
    232     private void doStateTransition(int newState) {
    233         doStateTransition(newState, /*timestamp*/0, NO_CAPTURE_ERROR);
    234     }
    235 
    236     private void doStateTransition(int newState, final long timestamp, final int error) {
    237         if (newState != mCurrentState) {
    238             String stateName = "UNKNOWN";
    239             if (newState >= 0 && newState < sStateNames.length) {
    240                 stateName = sStateNames[newState];
    241             }
    242             Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
    243         }
    244 
    245         // If we transitioned into a non-IDLE/non-ERROR state then mark the device as busy
    246         if(newState != STATE_ERROR && newState != STATE_IDLE) {
    247             if (mCurrentState != newState && mCurrentHandler != null &&
    248                     mCurrentListener != null) {
    249                 mCurrentHandler.post(new Runnable() {
    250                     @Override
    251                     public void run() {
    252                         mCurrentListener.onBusy();
    253                     }
    254                 });
    255             }
    256         }
    257 
    258         switch(newState) {
    259             case STATE_ERROR:
    260                 if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
    261                         mCurrentListener != null) {
    262                     mCurrentHandler.post(new Runnable() {
    263                         @Override
    264                         public void run() {
    265                             mCurrentListener.onError(mCurrentError, /*errorArg*/null, mCurrentRequest);
    266                         }
    267                     });
    268                 }
    269                 mCurrentState = STATE_ERROR;
    270                 break;
    271             case STATE_CONFIGURING:
    272                 if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
    273                     Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
    274                     mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    275                     doStateTransition(STATE_ERROR);
    276                     break;
    277                 }
    278                 if (mCurrentState != STATE_CONFIGURING && mCurrentHandler != null &&
    279                         mCurrentListener != null) {
    280                     mCurrentHandler.post(new Runnable() {
    281                         @Override
    282                         public void run() {
    283                             mCurrentListener.onConfiguring();
    284                         }
    285                     });
    286                 }
    287                 mCurrentState = STATE_CONFIGURING;
    288                 break;
    289             case STATE_IDLE:
    290                 if (mCurrentState == STATE_IDLE) {
    291                     break;
    292                 }
    293 
    294                 if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
    295                     Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
    296                     mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    297                     doStateTransition(STATE_ERROR);
    298                     break;
    299                 }
    300 
    301                 if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
    302                         mCurrentListener != null) {
    303                     mCurrentHandler.post(new Runnable() {
    304                         @Override
    305                         public void run() {
    306                             mCurrentListener.onIdle();
    307                         }
    308                     });
    309                 }
    310                 mCurrentState = STATE_IDLE;
    311                 break;
    312             case STATE_CAPTURING:
    313                 if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
    314                     Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
    315                     mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    316                     doStateTransition(STATE_ERROR);
    317                     break;
    318                 }
    319 
    320                 if (mCurrentHandler != null && mCurrentListener != null) {
    321                     if (error != NO_CAPTURE_ERROR) {
    322                         mCurrentHandler.post(new Runnable() {
    323                             @Override
    324                             public void run() {
    325                                 mCurrentListener.onError(error, /*errorArg*/null, mCurrentRequest);
    326                             }
    327                         });
    328                     } else {
    329                         mCurrentHandler.post(new Runnable() {
    330                             @Override
    331                             public void run() {
    332                                 mCurrentListener.onCaptureStarted(mCurrentRequest, timestamp);
    333                             }
    334                         });
    335                     }
    336                 }
    337                 mCurrentState = STATE_CAPTURING;
    338                 break;
    339             default:
    340                 throw new IllegalStateException("Transition to unknown state: " + newState);
    341         }
    342     }
    343 
    344 
    345 }
    346