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