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.graphics.ImageFormat;
     20 import android.graphics.SurfaceTexture;
     21 import android.hardware.Camera;
     22 import android.hardware.camera2.CameraCharacteristics;
     23 import android.hardware.camera2.CaptureRequest;
     24 import android.hardware.camera2.impl.CameraDeviceImpl;
     25 import android.hardware.camera2.impl.CaptureResultExtras;
     26 import android.hardware.camera2.ICameraDeviceCallbacks;
     27 import android.hardware.camera2.params.StreamConfigurationMap;
     28 import android.hardware.camera2.utils.ArrayUtils;
     29 import android.hardware.camera2.utils.SubmitInfo;
     30 import android.hardware.camera2.impl.CameraMetadataNative;
     31 import android.os.ConditionVariable;
     32 import android.os.Handler;
     33 import android.os.HandlerThread;
     34 import android.os.RemoteException;
     35 import android.os.ServiceSpecificException;
     36 import android.util.Log;
     37 import android.util.Pair;
     38 import android.util.Size;
     39 import android.util.SparseArray;
     40 import android.view.Surface;
     41 
     42 import java.util.ArrayList;
     43 import java.util.Arrays;
     44 import java.util.Collection;
     45 import java.util.List;
     46 
     47 import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
     48 import static com.android.internal.util.Preconditions.*;
     49 
     50 /**
     51  * This class emulates the functionality of a Camera2 device using a the old Camera class.
     52  *
     53  * <p>
     54  * There are two main components that are used to implement this:
     55  * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}).
     56  * - A message-queue based pipeline that manages an old Camera class, and executes capture and
     57  *   configuration requests.
     58  * </p>
     59  */
     60 public class LegacyCameraDevice implements AutoCloseable {
     61     private final String TAG;
     62 
     63     private static final boolean DEBUG = false;
     64     private final int mCameraId;
     65     private final CameraCharacteristics mStaticCharacteristics;
     66     private final ICameraDeviceCallbacks mDeviceCallbacks;
     67     private final CameraDeviceState mDeviceState = new CameraDeviceState();
     68     private SparseArray<Surface> mConfiguredSurfaces;
     69     private boolean mClosed = false;
     70 
     71     private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
     72 
     73     private final HandlerThread mResultThread = new HandlerThread("ResultThread");
     74     private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
     75     private final Handler mCallbackHandler;
     76     private final Handler mResultHandler;
     77     private static final int ILLEGAL_VALUE = -1;
     78 
     79     // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h
     80     private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000;
     81     private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003;
     82     private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
     83     private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800;
     84     private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
     85     private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
     86 
     87     public static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding
     88 
     89     // Keep up to date with values in system/core/include/system/window.h
     90     public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1;
     91 
     92     private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
     93         return getExtrasFromRequest(holder,
     94                 /*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null);
     95     }
     96 
     97     private CaptureResultExtras getExtrasFromRequest(RequestHolder holder,
     98             int errorCode, Object errorArg) {
     99         int errorStreamId = -1;
    100         if (errorCode == CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) {
    101             Surface errorTarget = (Surface) errorArg;
    102             int indexOfTarget = mConfiguredSurfaces.indexOfValue(errorTarget);
    103             if (indexOfTarget < 0) {
    104                 Log.e(TAG, "Buffer drop error reported for unknown Surface");
    105             } else {
    106                 errorStreamId = mConfiguredSurfaces.keyAt(indexOfTarget);
    107             }
    108         }
    109         if (holder == null) {
    110             return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
    111                     ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
    112         }
    113         return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
    114                 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
    115                 /*partialResultCount*/1, errorStreamId);
    116     }
    117 
    118     /**
    119      * Listener for the camera device state machine.  Calls the appropriate
    120      * {@link ICameraDeviceCallbacks} for each state transition.
    121      */
    122     private final CameraDeviceState.CameraDeviceStateListener mStateListener =
    123             new CameraDeviceState.CameraDeviceStateListener() {
    124         @Override
    125         public void onError(final int errorCode, final Object errorArg, final RequestHolder holder) {
    126             if (DEBUG) {
    127                 Log.d(TAG, "onError called, errorCode = " + errorCode + ", errorArg = " + errorArg);
    128             }
    129             switch (errorCode) {
    130                 /*
    131                  * Only be considered idle if we hit a fatal error
    132                  * and no further requests can be processed.
    133                  */
    134                 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
    135                 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE:
    136                 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: {
    137                     mIdle.open();
    138 
    139                     if (DEBUG) {
    140                         Log.d(TAG, "onError - opening idle");
    141                     }
    142                 }
    143             }
    144 
    145             final CaptureResultExtras extras = getExtrasFromRequest(holder, errorCode, errorArg);
    146             mResultHandler.post(new Runnable() {
    147                 @Override
    148                 public void run() {
    149                     if (DEBUG) {
    150                         Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
    151                                 ", with error code " + errorCode);
    152                     }
    153                     try {
    154                         mDeviceCallbacks.onDeviceError(errorCode, extras);
    155                     } catch (RemoteException e) {
    156                         throw new IllegalStateException(
    157                                 "Received remote exception during onCameraError callback: ", e);
    158                     }
    159                 }
    160             });
    161         }
    162 
    163         @Override
    164         public void onConfiguring() {
    165             // Do nothing
    166             if (DEBUG) {
    167                 Log.d(TAG, "doing onConfiguring callback.");
    168             }
    169         }
    170 
    171         @Override
    172         public void onIdle() {
    173             if (DEBUG) {
    174                 Log.d(TAG, "onIdle called");
    175             }
    176 
    177             mIdle.open();
    178 
    179             mResultHandler.post(new Runnable() {
    180                 @Override
    181                 public void run() {
    182                     if (DEBUG) {
    183                         Log.d(TAG, "doing onIdle callback.");
    184                     }
    185                     try {
    186                         mDeviceCallbacks.onDeviceIdle();
    187                     } catch (RemoteException e) {
    188                         throw new IllegalStateException(
    189                                 "Received remote exception during onCameraIdle callback: ", e);
    190                     }
    191                 }
    192             });
    193         }
    194 
    195         @Override
    196         public void onBusy() {
    197             mIdle.close();
    198 
    199             if (DEBUG) {
    200                 Log.d(TAG, "onBusy called");
    201             }
    202         }
    203 
    204         @Override
    205         public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
    206             final CaptureResultExtras extras = getExtrasFromRequest(holder);
    207 
    208             mResultHandler.post(new Runnable() {
    209                 @Override
    210                 public void run() {
    211                     if (DEBUG) {
    212                         Log.d(TAG, "doing onCaptureStarted callback for request " +
    213                                 holder.getRequestId());
    214                     }
    215                     try {
    216                         mDeviceCallbacks.onCaptureStarted(extras, timestamp);
    217                     } catch (RemoteException e) {
    218                         throw new IllegalStateException(
    219                                 "Received remote exception during onCameraError callback: ", e);
    220                     }
    221                 }
    222             });
    223         }
    224 
    225         @Override
    226         public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
    227             final CaptureResultExtras extras = getExtrasFromRequest(holder);
    228 
    229             mResultHandler.post(new Runnable() {
    230                 @Override
    231                 public void run() {
    232                     if (DEBUG) {
    233                         Log.d(TAG, "doing onCaptureResult callback for request " +
    234                                 holder.getRequestId());
    235                     }
    236                     try {
    237                         mDeviceCallbacks.onResultReceived(result, extras);
    238                     } catch (RemoteException e) {
    239                         throw new IllegalStateException(
    240                                 "Received remote exception during onCameraError callback: ", e);
    241                     }
    242                 }
    243             });
    244         }
    245 
    246         @Override
    247         public void onRepeatingRequestError(final long lastFrameNumber) {
    248             mResultHandler.post(new Runnable() {
    249                 @Override
    250                 public void run() {
    251                     if (DEBUG) {
    252                         Log.d(TAG, "doing onRepeatingRequestError callback.");
    253                     }
    254                     try {
    255                         mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber);
    256                     } catch (RemoteException e) {
    257                         throw new IllegalStateException(
    258                                 "Received remote exception during onRepeatingRequestError " +
    259                                 "callback: ", e);
    260                     }
    261                 }
    262             });
    263         }
    264     };
    265 
    266     private final RequestThreadManager mRequestThreadManager;
    267 
    268     /**
    269      * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
    270      * converted to this; YV12 and NV21 are the two currently supported formats.
    271      *
    272      * @param s the surface to check.
    273      * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
    274      *          format.
    275      */
    276     static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
    277         int nativeType = detectSurfaceType(s);
    278         return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
    279                 nativeType == ImageFormat.NV21;
    280     }
    281 
    282     /**
    283      * Create a new emulated camera device from a given Camera 1 API camera.
    284      *
    285      * <p>
    286      * The {@link Camera} provided to this constructor must already have been successfully opened,
    287      * and ownership of the provided camera is passed to this object.  No further calls to the
    288      * camera methods should be made following this constructor.
    289      * </p>
    290      *
    291      * @param cameraId the id of the camera.
    292      * @param camera an open {@link Camera} device.
    293      * @param characteristics the static camera characteristics for this camera device
    294      * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
    295      */
    296     public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
    297             ICameraDeviceCallbacks callbacks) {
    298         mCameraId = cameraId;
    299         mDeviceCallbacks = callbacks;
    300         TAG = String.format("CameraDevice-%d-LE", mCameraId);
    301 
    302         mResultThread.start();
    303         mResultHandler = new Handler(mResultThread.getLooper());
    304         mCallbackHandlerThread.start();
    305         mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
    306         mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
    307         mStaticCharacteristics = characteristics;
    308         mRequestThreadManager =
    309                 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
    310         mRequestThreadManager.start();
    311     }
    312 
    313     /**
    314      * Configure the device with a set of output surfaces.
    315      *
    316      * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
    317      *
    318      * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
    319      *
    320      * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
    321      *          list; it must not be modified by the caller once it's passed in.
    322      * @return an error code for this binder operation, or {@link NO_ERROR}
    323      *          on success.
    324      */
    325     public int configureOutputs(SparseArray<Surface> outputs) {
    326         List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
    327         if (outputs != null) {
    328             int count = outputs.size();
    329             for (int i = 0; i < count; i++)  {
    330                 Surface output = outputs.valueAt(i);
    331                 if (output == null) {
    332                     Log.e(TAG, "configureOutputs - null outputs are not allowed");
    333                     return BAD_VALUE;
    334                 }
    335                 if (!output.isValid()) {
    336                     Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed");
    337                     return BAD_VALUE;
    338                 }
    339                 StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
    340                         get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    341 
    342                 // Validate surface size and format.
    343                 try {
    344                     Size s = getSurfaceSize(output);
    345                     int surfaceType = detectSurfaceType(output);
    346 
    347                     boolean flexibleConsumer = isFlexibleConsumer(output);
    348 
    349                     Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
    350                     if (sizes == null) {
    351                         if (surfaceType == ImageFormat.PRIVATE) {
    352 
    353                             // YUV_420_888 is always present in LEGACY for all
    354                             // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
    355                             // API (i.e. {@code #getOutputSizes} works here).
    356                             sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
    357                         } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
    358                             sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
    359                         }
    360                     }
    361 
    362                     if (!ArrayUtils.contains(sizes, s)) {
    363                         if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
    364                             sizedSurfaces.add(new Pair<>(output, s));
    365                         } else {
    366                             String reason = (sizes == null) ? "format is invalid." :
    367                                     ("size not in valid set: " + Arrays.toString(sizes));
    368                             Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
    369                                     "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
    370                                     surfaceType, reason));
    371                             return BAD_VALUE;
    372                         }
    373                     } else {
    374                         sizedSurfaces.add(new Pair<>(output, s));
    375                     }
    376                     // Lock down the size before configuration
    377                     setSurfaceDimens(output, s.getWidth(), s.getHeight());
    378                 } catch (BufferQueueAbandonedException e) {
    379                     Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
    380                     return BAD_VALUE;
    381                 }
    382 
    383             }
    384         }
    385 
    386         boolean success = false;
    387         if (mDeviceState.setConfiguring()) {
    388             mRequestThreadManager.configure(sizedSurfaces);
    389             success = mDeviceState.setIdle();
    390         }
    391 
    392         if (success) {
    393             mConfiguredSurfaces = outputs;
    394         } else {
    395             return LegacyExceptionUtils.INVALID_OPERATION;
    396         }
    397         return LegacyExceptionUtils.NO_ERROR;
    398     }
    399 
    400     /**
    401      * Submit a burst of capture requests.
    402      *
    403      * @param requestList a list of capture requests to execute.
    404      * @param repeating {@code true} if this burst is repeating.
    405      * @return the submission info, including the new request id, and the last frame number, which
    406      *   contains either the frame number of the last frame that will be returned for this request,
    407      *   or the frame number of the last frame that will be returned for the current repeating
    408      *   request if this burst is set to be repeating.
    409      */
    410     public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) {
    411         if (requestList == null || requestList.length == 0) {
    412             Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
    413             throw new ServiceSpecificException(BAD_VALUE,
    414                     "submitRequestList - Empty/null requests are not allowed");
    415         }
    416 
    417         List<Long> surfaceIds;
    418 
    419         try {
    420             surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
    421                     getSurfaceIds(mConfiguredSurfaces);
    422         } catch (BufferQueueAbandonedException e) {
    423             throw new ServiceSpecificException(BAD_VALUE,
    424                     "submitRequestList - configured surface is abandoned.");
    425         }
    426 
    427         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
    428         for (CaptureRequest request : requestList) {
    429             if (request.getTargets().isEmpty()) {
    430                 Log.e(TAG, "submitRequestList - "
    431                         + "Each request must have at least one Surface target");
    432                 throw new ServiceSpecificException(BAD_VALUE,
    433                         "submitRequestList - "
    434                         + "Each request must have at least one Surface target");
    435             }
    436 
    437             for (Surface surface : request.getTargets()) {
    438                 if (surface == null) {
    439                     Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
    440                     throw new ServiceSpecificException(BAD_VALUE,
    441                             "submitRequestList - Null Surface targets are not allowed");
    442                 } else if (mConfiguredSurfaces == null) {
    443                     Log.e(TAG, "submitRequestList - must configure " +
    444                             " device with valid surfaces before submitting requests");
    445                     throw new ServiceSpecificException(INVALID_OPERATION,
    446                             "submitRequestList - must configure " +
    447                             " device with valid surfaces before submitting requests");
    448                 } else if (!containsSurfaceId(surface, surfaceIds)) {
    449                     Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
    450                     throw new ServiceSpecificException(BAD_VALUE,
    451                             "submitRequestList - cannot use a surface that wasn't configured");
    452                 }
    453             }
    454         }
    455 
    456         // TODO: further validation of request here
    457         mIdle.close();
    458         return mRequestThreadManager.submitCaptureRequests(requestList, repeating);
    459     }
    460 
    461     /**
    462      * Submit a single capture request.
    463      *
    464      * @param request the capture request to execute.
    465      * @param repeating {@code true} if this request is repeating.
    466      * @return the submission info, including the new request id, and the last frame number, which
    467      *   contains either the frame number of the last frame that will be returned for this request,
    468      *   or the frame number of the last frame that will be returned for the current repeating
    469      *   request if this burst is set to be repeating.
    470      */
    471     public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) {
    472         CaptureRequest[] requestList = { request };
    473         return submitRequestList(requestList, repeating);
    474     }
    475 
    476     /**
    477      * Cancel the repeating request with the given request id.
    478      *
    479      * @param requestId the request id of the request to cancel.
    480      * @return the last frame number to be returned from the HAL for the given repeating request, or
    481      *          {@code INVALID_FRAME} if none exists.
    482      */
    483     public long cancelRequest(int requestId) {
    484         return mRequestThreadManager.cancelRepeating(requestId);
    485     }
    486 
    487     /**
    488      * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
    489      */
    490     public void waitUntilIdle()  {
    491         mIdle.block();
    492     }
    493 
    494     /**
    495      * Flush any pending requests.
    496      *
    497      * @return the last frame number.
    498      */
    499     public long flush() {
    500         long lastFrame = mRequestThreadManager.flush();
    501         waitUntilIdle();
    502         return lastFrame;
    503     }
    504 
    505     /**
    506      * Return {@code true} if the device has been closed.
    507      */
    508     public boolean isClosed() {
    509         return mClosed;
    510     }
    511 
    512     @Override
    513     public void close() {
    514         mRequestThreadManager.quit();
    515         mCallbackHandlerThread.quitSafely();
    516         mResultThread.quitSafely();
    517 
    518         try {
    519             mCallbackHandlerThread.join();
    520         } catch (InterruptedException e) {
    521             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
    522                     mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
    523         }
    524 
    525         try {
    526             mResultThread.join();
    527         } catch (InterruptedException e) {
    528             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
    529                     mResultThread.getName(), mResultThread.getId()));
    530         }
    531 
    532         mClosed = true;
    533     }
    534 
    535     @Override
    536     protected void finalize() throws Throwable {
    537         try {
    538             close();
    539         } catch (ServiceSpecificException e) {
    540             Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
    541         } finally {
    542             super.finalize();
    543         }
    544     }
    545 
    546     static long findEuclidDistSquare(Size a, Size b) {
    547         long d0 = a.getWidth() - b.getWidth();
    548         long d1 = a.getHeight() - b.getHeight();
    549         return d0 * d0 + d1 * d1;
    550     }
    551 
    552     // Keep up to date with rounding behavior in
    553     // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
    554     static Size findClosestSize(Size size, Size[] supportedSizes) {
    555         if (size == null || supportedSizes == null) {
    556             return null;
    557         }
    558         Size bestSize = null;
    559         for (Size s : supportedSizes) {
    560             if (s.equals(size)) {
    561                 return size;
    562             } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
    563                     LegacyCameraDevice.findEuclidDistSquare(size, s) <
    564                     LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
    565                 bestSize = s;
    566             }
    567         }
    568         return bestSize;
    569     }
    570 
    571     /**
    572      * Query the surface for its currently configured default buffer size.
    573      * @param surface a non-{@code null} {@code Surface}
    574      * @return the width and height of the surface
    575      *
    576      * @throws NullPointerException if the {@code surface} was {@code null}
    577      * @throws BufferQueueAbandonedException if the {@code surface} was invalid
    578      */
    579     public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
    580         checkNotNull(surface);
    581 
    582         int[] dimens = new int[2];
    583         LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
    584 
    585         return new Size(dimens[0], dimens[1]);
    586     }
    587 
    588     public static boolean isFlexibleConsumer(Surface output) {
    589         int usageFlags = detectSurfaceUsageFlags(output);
    590 
    591         // Keep up to date with allowed consumer types in
    592         // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
    593         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
    594         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
    595             GRALLOC_USAGE_HW_COMPOSER;
    596         boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
    597                 (usageFlags & allowedFlags) != 0);
    598         return flexibleConsumer;
    599     }
    600 
    601     public static boolean isPreviewConsumer(Surface output) {
    602         int usageFlags = detectSurfaceUsageFlags(output);
    603         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
    604                 GRALLOC_USAGE_SW_READ_OFTEN;
    605         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
    606                 GRALLOC_USAGE_HW_RENDER;
    607         boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
    608                 (usageFlags & allowedFlags) != 0);
    609         int surfaceFormat = ImageFormat.UNKNOWN;
    610         try {
    611             surfaceFormat = detectSurfaceType(output);
    612         } catch(BufferQueueAbandonedException e) {
    613             throw new IllegalArgumentException("Surface was abandoned", e);
    614         }
    615 
    616         return previewConsumer;
    617     }
    618 
    619     public static boolean isVideoEncoderConsumer(Surface output) {
    620         int usageFlags = detectSurfaceUsageFlags(output);
    621         int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
    622                 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
    623         int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
    624         boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
    625                 (usageFlags & allowedFlags) != 0);
    626 
    627         int surfaceFormat = ImageFormat.UNKNOWN;
    628         try {
    629             surfaceFormat = detectSurfaceType(output);
    630         } catch(BufferQueueAbandonedException e) {
    631             throw new IllegalArgumentException("Surface was abandoned", e);
    632         }
    633 
    634         return videoEncoderConsumer;
    635     }
    636 
    637     /**
    638      * Query the surface for its currently configured usage flags
    639      */
    640     static int detectSurfaceUsageFlags(Surface surface) {
    641         checkNotNull(surface);
    642         return nativeDetectSurfaceUsageFlags(surface);
    643     }
    644 
    645     /**
    646      * Query the surface for its currently configured format
    647      */
    648     public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
    649         checkNotNull(surface);
    650         int surfaceType = nativeDetectSurfaceType(surface);
    651 
    652         // TODO: remove this override since the default format should be
    653         // ImageFormat.PRIVATE. b/9487482
    654         if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
    655                 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
    656             surfaceType = ImageFormat.PRIVATE;
    657         }
    658 
    659         return LegacyExceptionUtils.throwOnError(surfaceType);
    660     }
    661 
    662     /**
    663      * Query the surface for its currently configured dataspace
    664      */
    665     public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException {
    666         checkNotNull(surface);
    667         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
    668     }
    669 
    670     static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
    671         checkNotNull(surface);
    672 
    673         LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
    674     }
    675 
    676     static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
    677         if (surface == null) return;
    678 
    679         LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
    680     }
    681 
    682     static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
    683                              int height, int pixelFormat)
    684             throws BufferQueueAbandonedException {
    685         checkNotNull(surface);
    686         checkNotNull(pixelBuffer);
    687         checkArgumentPositive(width, "width must be positive.");
    688         checkArgumentPositive(height, "height must be positive.");
    689 
    690         LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
    691                 pixelFormat));
    692     }
    693 
    694     static void setSurfaceFormat(Surface surface, int pixelFormat)
    695             throws BufferQueueAbandonedException {
    696         checkNotNull(surface);
    697 
    698         LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
    699     }
    700 
    701     static void setSurfaceDimens(Surface surface, int width, int height)
    702             throws BufferQueueAbandonedException {
    703         checkNotNull(surface);
    704         checkArgumentPositive(width, "width must be positive.");
    705         checkArgumentPositive(height, "height must be positive.");
    706 
    707         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
    708     }
    709 
    710     static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
    711         checkNotNull(surface);
    712         try {
    713             return nativeGetSurfaceId(surface);
    714         } catch (IllegalArgumentException e) {
    715             throw new BufferQueueAbandonedException();
    716         }
    717     }
    718 
    719     static List<Long> getSurfaceIds(SparseArray<Surface> surfaces)
    720             throws BufferQueueAbandonedException {
    721         if (surfaces == null) {
    722             throw new NullPointerException("Null argument surfaces");
    723         }
    724         List<Long> surfaceIds = new ArrayList<>();
    725         int count = surfaces.size();
    726         for (int i = 0; i < count; i++) {
    727             long id = getSurfaceId(surfaces.valueAt(i));
    728             if (id == 0) {
    729                 throw new IllegalStateException(
    730                         "Configured surface had null native GraphicBufferProducer pointer!");
    731             }
    732             surfaceIds.add(id);
    733         }
    734         return surfaceIds;
    735     }
    736 
    737     static List<Long> getSurfaceIds(Collection<Surface> surfaces)
    738             throws BufferQueueAbandonedException {
    739         if (surfaces == null) {
    740             throw new NullPointerException("Null argument surfaces");
    741         }
    742         List<Long> surfaceIds = new ArrayList<>();
    743         for (Surface s : surfaces) {
    744             long id = getSurfaceId(s);
    745             if (id == 0) {
    746                 throw new IllegalStateException(
    747                         "Configured surface had null native GraphicBufferProducer pointer!");
    748             }
    749             surfaceIds.add(id);
    750         }
    751         return surfaceIds;
    752     }
    753 
    754     static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
    755         long id = 0;
    756         try {
    757             id = getSurfaceId(s);
    758         } catch (BufferQueueAbandonedException e) {
    759             // If surface is abandoned, return false.
    760             return false;
    761         }
    762         return ids.contains(id);
    763     }
    764 
    765     static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
    766             throws BufferQueueAbandonedException {
    767         checkNotNull(surface);
    768         LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
    769                 sensorOrientation));
    770     }
    771 
    772     static Size getTextureSize(SurfaceTexture surfaceTexture)
    773             throws BufferQueueAbandonedException {
    774         checkNotNull(surfaceTexture);
    775 
    776         int[] dimens = new int[2];
    777         LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
    778                 /*out*/dimens));
    779 
    780         return new Size(dimens[0], dimens[1]);
    781     }
    782 
    783     static void setNextTimestamp(Surface surface, long timestamp)
    784             throws BufferQueueAbandonedException {
    785         checkNotNull(surface);
    786         LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
    787     }
    788 
    789     static void setScalingMode(Surface surface, int mode)
    790             throws BufferQueueAbandonedException {
    791         checkNotNull(surface);
    792         LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode));
    793     }
    794 
    795 
    796     private static native int nativeDetectSurfaceType(Surface surface);
    797 
    798     private static native int nativeDetectSurfaceDataspace(Surface surface);
    799 
    800     private static native int nativeDetectSurfaceDimens(Surface surface,
    801             /*out*/int[/*2*/] dimens);
    802 
    803     private static native int nativeConnectSurface(Surface surface);
    804 
    805     private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
    806                                                     int height, int pixelFormat);
    807 
    808     private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
    809 
    810     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
    811 
    812     private static native long nativeGetSurfaceId(Surface surface);
    813 
    814     private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
    815                                                              int sensorOrientation);
    816 
    817     private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
    818             /*out*/int[/*2*/] dimens);
    819 
    820     private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
    821 
    822     private static native int nativeDetectSurfaceUsageFlags(Surface surface);
    823 
    824     private static native int nativeSetScalingMode(Surface surface, int scalingMode);
    825 
    826     private static native int nativeDisconnectSurface(Surface surface);
    827 
    828     static native int nativeGetJpegFooterSize();
    829 }
    830