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 onRequestQueueEmpty();
     80         void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId);
     81     }
     82 
     83     /**
     84      * Transition to the {@code ERROR} state.
     85      *
     86      * <p>
     87      * The device cannot exit the {@code ERROR} state.  If the device was not already in the
     88      * {@code ERROR} state, {@link CameraDeviceStateListener#onError(int, RequestHolder)} will be
     89      * called.
     90      * </p>
     91      *
     92      * @param error the error to set.  Should be one of the error codes defined in
     93      *      {@link CameraDeviceImpl.CameraDeviceCallbacks}.
     94      */
     95     public synchronized void setError(int error) {
     96         mCurrentError = error;
     97         doStateTransition(STATE_ERROR);
     98     }
     99 
    100     /**
    101      * Transition to the {@code CONFIGURING} state, or {@code ERROR} if in an invalid state.
    102      *
    103      * <p>
    104      * If the device was not already in the {@code CONFIGURING} state,
    105      * {@link CameraDeviceStateListener#onConfiguring()} will be called.
    106      * </p>
    107      *
    108      * @return {@code false} if an error has occurred.
    109      */
    110     public synchronized boolean setConfiguring() {
    111         doStateTransition(STATE_CONFIGURING);
    112         return mCurrentError == NO_CAPTURE_ERROR;
    113     }
    114 
    115     /**
    116      * Transition to the {@code IDLE} state, or {@code ERROR} if in an invalid state.
    117      *
    118      * <p>
    119      * If the device was not already in the {@code IDLE} state,
    120      * {@link CameraDeviceStateListener#onIdle()} will be called.
    121      * </p>
    122      *
    123      * @return {@code false} if an error has occurred.
    124      */
    125     public synchronized boolean setIdle() {
    126         doStateTransition(STATE_IDLE);
    127         return mCurrentError == NO_CAPTURE_ERROR;
    128     }
    129 
    130     /**
    131      * Transition to the {@code CAPTURING} state, or {@code ERROR} if in an invalid state.
    132      *
    133      * <p>
    134      * If the device was not already in the {@code CAPTURING} state,
    135      * {@link CameraDeviceStateListener#onCaptureStarted(RequestHolder)} will be called.
    136      * </p>
    137      *
    138      * @param request A {@link RequestHolder} containing the request for the current capture.
    139      * @param timestamp The timestamp of the capture start in nanoseconds.
    140      * @param captureError Report a recoverable error for a single request using a valid
    141      *                     error code for {@code ICameraDeviceCallbacks}, or
    142      *                     {@link #NO_CAPTURE_ERROR}
    143      * @return {@code false} if an error has occurred.
    144      */
    145     public synchronized boolean setCaptureStart(final RequestHolder request, long timestamp,
    146                                             int captureError) {
    147         mCurrentRequest = request;
    148         doStateTransition(STATE_CAPTURING, timestamp, captureError);
    149         return mCurrentError == NO_CAPTURE_ERROR;
    150     }
    151 
    152     /**
    153      * Set the result for a capture.
    154      *
    155      * <p>
    156      * If the device was in the {@code CAPTURING} state,
    157      * {@link CameraDeviceStateListener#onCaptureResult(CameraMetadataNative, RequestHolder)} will
    158      * be called with the given result, otherwise this will result in the device transitioning to
    159      * the {@code ERROR} state,
    160      * </p>
    161      *
    162      * @param request The {@link RequestHolder} request that created this result.
    163      * @param result The {@link CameraMetadataNative} result to set.
    164      * @param captureError Report a recoverable error for a single buffer or result using a valid
    165      *                     error code for {@code ICameraDeviceCallbacks}, or
    166      *                     {@link #NO_CAPTURE_ERROR}.
    167      * @param captureErrorArg An argument for some error captureError codes.
    168      * @return {@code false} if an error has occurred.
    169      */
    170     public synchronized boolean setCaptureResult(final RequestHolder request,
    171             final CameraMetadataNative result,
    172             final int captureError, final Object captureErrorArg) {
    173         if (mCurrentState != STATE_CAPTURING) {
    174             Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
    175             mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    176             doStateTransition(STATE_ERROR);
    177             return mCurrentError == NO_CAPTURE_ERROR;
    178         }
    179 
    180         if (mCurrentHandler != null && mCurrentListener != null) {
    181             if (captureError != NO_CAPTURE_ERROR) {
    182                 mCurrentHandler.post(new Runnable() {
    183                     @Override
    184                     public void run() {
    185                         mCurrentListener.onError(captureError, captureErrorArg, request);
    186                     }
    187                 });
    188             } else {
    189                 mCurrentHandler.post(new Runnable() {
    190                     @Override
    191                     public void run() {
    192                         mCurrentListener.onCaptureResult(result, request);
    193                     }
    194                 });
    195             }
    196         }
    197         return mCurrentError == NO_CAPTURE_ERROR;
    198     }
    199 
    200     public synchronized boolean setCaptureResult(final RequestHolder request,
    201             final CameraMetadataNative result) {
    202         return setCaptureResult(request, result, NO_CAPTURE_ERROR, /*errorArg*/null);
    203     }
    204 
    205     /**
    206      * Set repeating request error.
    207      *
    208      * <p>Repeating request has been stopped due to an error such as abandoned output surfaces.</p>
    209      *
    210      * @param lastFrameNumber Frame number of the last repeating request before it is stopped.
    211      * @param repeatingRequestId The ID of the repeating request being stopped
    212      */
    213     public synchronized void setRepeatingRequestError(final long lastFrameNumber,
    214             final int repeatingRequestId) {
    215         mCurrentHandler.post(new Runnable() {
    216             @Override
    217             public void run() {
    218                 mCurrentListener.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
    219             }
    220         });
    221     }
    222 
    223     /**
    224      * Indicate that request queue (non-repeating) becomes empty.
    225      *
    226      * <p> Send notification that all non-repeating requests have been sent to camera device. </p>
    227      */
    228     public synchronized void setRequestQueueEmpty() {
    229         mCurrentHandler.post(new Runnable() {
    230             @Override
    231             public void run() {
    232                 mCurrentListener.onRequestQueueEmpty();
    233             }
    234         });
    235     }
    236 
    237     /**
    238      * Set the listener for state transition callbacks.
    239      *
    240      * @param handler handler on which to call the callbacks.
    241      * @param listener the {@link CameraDeviceStateListener} callbacks to call.
    242      */
    243     public synchronized void setCameraDeviceCallbacks(Handler handler,
    244                                                       CameraDeviceStateListener listener) {
    245         mCurrentHandler = handler;
    246         mCurrentListener = listener;
    247     }
    248 
    249     private void doStateTransition(int newState) {
    250         doStateTransition(newState, /*timestamp*/0, NO_CAPTURE_ERROR);
    251     }
    252 
    253     private void doStateTransition(int newState, final long timestamp, final int error) {
    254         if (newState != mCurrentState) {
    255             String stateName = "UNKNOWN";
    256             if (newState >= 0 && newState < sStateNames.length) {
    257                 stateName = sStateNames[newState];
    258             }
    259             Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
    260         }
    261 
    262         // If we transitioned into a non-IDLE/non-ERROR state then mark the device as busy
    263         if(newState != STATE_ERROR && newState != STATE_IDLE) {
    264             if (mCurrentState != newState && mCurrentHandler != null &&
    265                     mCurrentListener != null) {
    266                 mCurrentHandler.post(new Runnable() {
    267                     @Override
    268                     public void run() {
    269                         mCurrentListener.onBusy();
    270                     }
    271                 });
    272             }
    273         }
    274 
    275         switch(newState) {
    276             case STATE_ERROR:
    277                 if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
    278                         mCurrentListener != null) {
    279                     mCurrentHandler.post(new Runnable() {
    280                         @Override
    281                         public void run() {
    282                             mCurrentListener.onError(mCurrentError, /*errorArg*/null, mCurrentRequest);
    283                         }
    284                     });
    285                 }
    286                 mCurrentState = STATE_ERROR;
    287                 break;
    288             case STATE_CONFIGURING:
    289                 if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
    290                     Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
    291                     mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    292                     doStateTransition(STATE_ERROR);
    293                     break;
    294                 }
    295                 if (mCurrentState != STATE_CONFIGURING && mCurrentHandler != null &&
    296                         mCurrentListener != null) {
    297                     mCurrentHandler.post(new Runnable() {
    298                         @Override
    299                         public void run() {
    300                             mCurrentListener.onConfiguring();
    301                         }
    302                     });
    303                 }
    304                 mCurrentState = STATE_CONFIGURING;
    305                 break;
    306             case STATE_IDLE:
    307                 if (mCurrentState == STATE_IDLE) {
    308                     break;
    309                 }
    310 
    311                 if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
    312                     Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
    313                     mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    314                     doStateTransition(STATE_ERROR);
    315                     break;
    316                 }
    317 
    318                 if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
    319                         mCurrentListener != null) {
    320                     mCurrentHandler.post(new Runnable() {
    321                         @Override
    322                         public void run() {
    323                             mCurrentListener.onIdle();
    324                         }
    325                     });
    326                 }
    327                 mCurrentState = STATE_IDLE;
    328                 break;
    329             case STATE_CAPTURING:
    330                 if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
    331                     Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
    332                     mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
    333                     doStateTransition(STATE_ERROR);
    334                     break;
    335                 }
    336 
    337                 if (mCurrentHandler != null && mCurrentListener != null) {
    338                     if (error != NO_CAPTURE_ERROR) {
    339                         mCurrentHandler.post(new Runnable() {
    340                             @Override
    341                             public void run() {
    342                                 mCurrentListener.onError(error, /*errorArg*/null, mCurrentRequest);
    343                             }
    344                         });
    345                     } else {
    346                         mCurrentHandler.post(new Runnable() {
    347                             @Override
    348                             public void run() {
    349                                 mCurrentListener.onCaptureStarted(mCurrentRequest, timestamp);
    350                             }
    351                         });
    352                     }
    353                 }
    354                 mCurrentState = STATE_CAPTURING;
    355                 break;
    356             default:
    357                 throw new IllegalStateException("Transition to unknown state: " + newState);
    358         }
    359     }
    360 
    361 
    362 }
    363