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.CameraBinderDecorator;
     30 import android.hardware.camera2.utils.LongParcelable;
     31 import android.hardware.camera2.impl.CameraMetadataNative;
     32 import android.hardware.camera2.utils.CameraRuntimeException;
     33 import android.os.ConditionVariable;
     34 import android.os.Handler;
     35 import android.os.HandlerThread;
     36 import android.os.RemoteException;
     37 import android.util.Log;
     38 import android.util.Pair;
     39 import android.util.Size;
     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 android.hardware.camera2.utils.CameraBinderDecorator.*;
     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     public static final String DEBUG_PROP = "HAL1ShimLogging";
     63     private final String TAG;
     64 
     65     private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
     66     private final int mCameraId;
     67     private final CameraCharacteristics mStaticCharacteristics;
     68     private final ICameraDeviceCallbacks mDeviceCallbacks;
     69     private final CameraDeviceState mDeviceState = new CameraDeviceState();
     70     private List<Surface> mConfiguredSurfaces;
     71     private boolean mClosed = false;
     72 
     73     private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
     74 
     75     private final HandlerThread mResultThread = new HandlerThread("ResultThread");
     76     private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
     77     private final Handler mCallbackHandler;
     78     private final Handler mResultHandler;
     79     private static final int ILLEGAL_VALUE = -1;
     80 
     81     // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h
     82     private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000;
     83     private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003;
     84     private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
     85     private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800;
     86     private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
     87 
     88     public static final int MAX_DIMEN_FOR_ROUNDING = 1080; // maximum allowed width for rounding
     89 
     90     private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
     91         if (holder == null) {
     92             return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
     93                     ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
     94         }
     95         return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
     96                 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
     97                 /*partialResultCount*/1);
     98     }
     99 
    100     /**
    101      * Listener for the camera device state machine.  Calls the appropriate
    102      * {@link ICameraDeviceCallbacks} for each state transition.
    103      */
    104     private final CameraDeviceState.CameraDeviceStateListener mStateListener =
    105             new CameraDeviceState.CameraDeviceStateListener() {
    106         @Override
    107         public void onError(final int errorCode, final RequestHolder holder) {
    108             if (DEBUG) {
    109                 Log.d(TAG, "onError called, errorCode = " + errorCode);
    110             }
    111             switch (errorCode) {
    112                 /*
    113                  * Only be considered idle if we hit a fatal error
    114                  * and no further requests can be processed.
    115                  */
    116                 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
    117                 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE:
    118                 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: {
    119                     mIdle.open();
    120 
    121                     if (DEBUG) {
    122                         Log.d(TAG, "onError - opening idle");
    123                     }
    124                 }
    125             }
    126 
    127             final CaptureResultExtras extras = getExtrasFromRequest(holder);
    128             mResultHandler.post(new Runnable() {
    129                 @Override
    130                 public void run() {
    131                     if (DEBUG) {
    132                         Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
    133                                 ", with error code " + errorCode);
    134                     }
    135                     try {
    136                         mDeviceCallbacks.onDeviceError(errorCode, extras);
    137                     } catch (RemoteException e) {
    138                         throw new IllegalStateException(
    139                                 "Received remote exception during onCameraError callback: ", e);
    140                     }
    141                 }
    142             });
    143         }
    144 
    145         @Override
    146         public void onConfiguring() {
    147             // Do nothing
    148             if (DEBUG) {
    149                 Log.d(TAG, "doing onConfiguring callback.");
    150             }
    151         }
    152 
    153         @Override
    154         public void onIdle() {
    155             if (DEBUG) {
    156                 Log.d(TAG, "onIdle called");
    157             }
    158 
    159             mIdle.open();
    160 
    161             mResultHandler.post(new Runnable() {
    162                 @Override
    163                 public void run() {
    164                     if (DEBUG) {
    165                         Log.d(TAG, "doing onIdle callback.");
    166                     }
    167                     try {
    168                         mDeviceCallbacks.onDeviceIdle();
    169                     } catch (RemoteException e) {
    170                         throw new IllegalStateException(
    171                                 "Received remote exception during onCameraIdle callback: ", e);
    172                     }
    173                 }
    174             });
    175         }
    176 
    177         @Override
    178         public void onBusy() {
    179             mIdle.close();
    180 
    181             if (DEBUG) {
    182                 Log.d(TAG, "onBusy called");
    183             }
    184         }
    185 
    186         @Override
    187         public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
    188             final CaptureResultExtras extras = getExtrasFromRequest(holder);
    189 
    190             mResultHandler.post(new Runnable() {
    191                 @Override
    192                 public void run() {
    193                     if (DEBUG) {
    194                         Log.d(TAG, "doing onCaptureStarted callback for request " +
    195                                 holder.getRequestId());
    196                     }
    197                     try {
    198                         mDeviceCallbacks.onCaptureStarted(extras, timestamp);
    199                     } catch (RemoteException e) {
    200                         throw new IllegalStateException(
    201                                 "Received remote exception during onCameraError callback: ", e);
    202                     }
    203                 }
    204             });
    205         }
    206 
    207         @Override
    208         public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
    209             final CaptureResultExtras extras = getExtrasFromRequest(holder);
    210 
    211             mResultHandler.post(new Runnable() {
    212                 @Override
    213                 public void run() {
    214                     if (DEBUG) {
    215                         Log.d(TAG, "doing onCaptureResult callback for request " +
    216                                 holder.getRequestId());
    217                     }
    218                     try {
    219                         mDeviceCallbacks.onResultReceived(result, extras);
    220                     } catch (RemoteException e) {
    221                         throw new IllegalStateException(
    222                                 "Received remote exception during onCameraError callback: ", e);
    223                     }
    224                 }
    225             });
    226         }
    227     };
    228 
    229     private final RequestThreadManager mRequestThreadManager;
    230 
    231     /**
    232      * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
    233      * converted to this; YV12 and NV21 are the two currently supported formats.
    234      *
    235      * @param s the surface to check.
    236      * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
    237      *          format.
    238      */
    239     static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
    240         int nativeType = detectSurfaceType(s);
    241         return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
    242                 nativeType == ImageFormat.NV21;
    243     }
    244 
    245     /**
    246      * Create a new emulated camera device from a given Camera 1 API camera.
    247      *
    248      * <p>
    249      * The {@link Camera} provided to this constructor must already have been successfully opened,
    250      * and ownership of the provided camera is passed to this object.  No further calls to the
    251      * camera methods should be made following this constructor.
    252      * </p>
    253      *
    254      * @param cameraId the id of the camera.
    255      * @param camera an open {@link Camera} device.
    256      * @param characteristics the static camera characteristics for this camera device
    257      * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
    258      */
    259     public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
    260             ICameraDeviceCallbacks callbacks) {
    261         mCameraId = cameraId;
    262         mDeviceCallbacks = callbacks;
    263         TAG = String.format("CameraDevice-%d-LE", mCameraId);
    264 
    265         mResultThread.start();
    266         mResultHandler = new Handler(mResultThread.getLooper());
    267         mCallbackHandlerThread.start();
    268         mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
    269         mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
    270         mStaticCharacteristics = characteristics;
    271         mRequestThreadManager =
    272                 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
    273         mRequestThreadManager.start();
    274     }
    275 
    276     /**
    277      * Configure the device with a set of output surfaces.
    278      *
    279      * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
    280      *
    281      * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
    282      *
    283      * @param outputs a list of surfaces to set.
    284      * @return an error code for this binder operation, or {@link NO_ERROR}
    285      *          on success.
    286      */
    287     public int configureOutputs(List<Surface> outputs) {
    288         List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
    289         if (outputs != null) {
    290             for (Surface output : outputs) {
    291                 if (output == null) {
    292                     Log.e(TAG, "configureOutputs - null outputs are not allowed");
    293                     return BAD_VALUE;
    294                 }
    295                 StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
    296                         get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    297 
    298                 // Validate surface size and format.
    299                 try {
    300                     Size s = getSurfaceSize(output);
    301                     int surfaceType = detectSurfaceType(output);
    302 
    303                     boolean flexibleConsumer = isFlexibleConsumer(output);
    304 
    305                     Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
    306                     if (sizes == null) {
    307                         // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
    308                         if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
    309                             surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
    310 
    311                             // YUV_420_888 is always present in LEGACY for all
    312                             // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
    313                             // API (i.e. {@code #getOutputSizes} works here).
    314                             sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
    315                         } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
    316                             sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
    317                         }
    318                     }
    319 
    320                     if (!ArrayUtils.contains(sizes, s)) {
    321                         if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
    322                             sizedSurfaces.add(new Pair<>(output, s));
    323                         } else {
    324                             String reason = (sizes == null) ? "format is invalid." :
    325                                     ("size not in valid set: " + Arrays.toString(sizes));
    326                             Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
    327                                     "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
    328                                     surfaceType, reason));
    329                             return BAD_VALUE;
    330                         }
    331                     } else {
    332                         sizedSurfaces.add(new Pair<>(output, s));
    333                     }
    334                 } catch (BufferQueueAbandonedException e) {
    335                     Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
    336                     return BAD_VALUE;
    337                 }
    338 
    339             }
    340         }
    341 
    342         boolean success = false;
    343         if (mDeviceState.setConfiguring()) {
    344             mRequestThreadManager.configure(sizedSurfaces);
    345             success = mDeviceState.setIdle();
    346         }
    347 
    348         if (success) {
    349             mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
    350         } else {
    351             return CameraBinderDecorator.INVALID_OPERATION;
    352         }
    353         return CameraBinderDecorator.NO_ERROR;
    354     }
    355 
    356     /**
    357      * Submit a burst of capture requests.
    358      *
    359      * @param requestList a list of capture requests to execute.
    360      * @param repeating {@code true} if this burst is repeating.
    361      * @param frameNumber an output argument that contains either the frame number of the last frame
    362      *                    that will be returned for this request, or the frame number of the last
    363      *                    frame that will be returned for the current repeating request if this
    364      *                    burst is set to be repeating.
    365      * @return the request id.
    366      */
    367     public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
    368             /*out*/LongParcelable frameNumber) {
    369         if (requestList == null || requestList.isEmpty()) {
    370             Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
    371             return BAD_VALUE;
    372         }
    373 
    374         List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
    375                 getSurfaceIds(mConfiguredSurfaces);
    376 
    377         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
    378         for (CaptureRequest request : requestList) {
    379             if (request.getTargets().isEmpty()) {
    380                 Log.e(TAG, "submitRequestList - "
    381                         + "Each request must have at least one Surface target");
    382                 return BAD_VALUE;
    383             }
    384 
    385             for (Surface surface : request.getTargets()) {
    386                 if (surface == null) {
    387                     Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
    388                     return BAD_VALUE;
    389                 } else if (mConfiguredSurfaces == null) {
    390                     Log.e(TAG, "submitRequestList - must configure " +
    391                             " device with valid surfaces before submitting requests");
    392                     return INVALID_OPERATION;
    393                 } else if (!containsSurfaceId(surface, surfaceIds)) {
    394                     Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
    395                     return BAD_VALUE;
    396                 }
    397             }
    398         }
    399 
    400         // TODO: further validation of request here
    401         mIdle.close();
    402         return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
    403                 frameNumber);
    404     }
    405 
    406     /**
    407      * Submit a single capture request.
    408      *
    409      * @param request the capture request to execute.
    410      * @param repeating {@code true} if this request is repeating.
    411      * @param frameNumber an output argument that contains either the frame number of the last frame
    412      *                    that will be returned for this request, or the frame number of the last
    413      *                    frame that will be returned for the current repeating request if this
    414      *                    request is set to be repeating.
    415      * @return the request id.
    416      */
    417     public int submitRequest(CaptureRequest request, boolean repeating,
    418             /*out*/LongParcelable frameNumber) {
    419         ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
    420         requestList.add(request);
    421         return submitRequestList(requestList, repeating, frameNumber);
    422     }
    423 
    424     /**
    425      * Cancel the repeating request with the given request id.
    426      *
    427      * @param requestId the request id of the request to cancel.
    428      * @return the last frame number to be returned from the HAL for the given repeating request, or
    429      *          {@code INVALID_FRAME} if none exists.
    430      */
    431     public long cancelRequest(int requestId) {
    432         return mRequestThreadManager.cancelRepeating(requestId);
    433     }
    434 
    435     /**
    436      * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
    437      */
    438     public void waitUntilIdle()  {
    439         mIdle.block();
    440     }
    441 
    442     /**
    443      * Flush any pending requests.
    444      *
    445      * @return the last frame number.
    446      */
    447     public long flush() {
    448         long lastFrame = mRequestThreadManager.flush();
    449         waitUntilIdle();
    450         return lastFrame;
    451     }
    452 
    453     /**
    454      * Return {@code true} if the device has been closed.
    455      */
    456     public boolean isClosed() {
    457         return mClosed;
    458     }
    459 
    460     @Override
    461     public void close() {
    462         mRequestThreadManager.quit();
    463         mCallbackHandlerThread.quitSafely();
    464         mResultThread.quitSafely();
    465 
    466         try {
    467             mCallbackHandlerThread.join();
    468         } catch (InterruptedException e) {
    469             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
    470                     mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
    471         }
    472 
    473         try {
    474             mResultThread.join();
    475         } catch (InterruptedException e) {
    476             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
    477                     mResultThread.getName(), mResultThread.getId()));
    478         }
    479 
    480         mClosed = true;
    481     }
    482 
    483     @Override
    484     protected void finalize() throws Throwable {
    485         try {
    486             close();
    487         } catch (CameraRuntimeException e) {
    488             Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
    489         } finally {
    490             super.finalize();
    491         }
    492     }
    493 
    494     static long findEuclidDistSquare(Size a, Size b) {
    495         long d0 = a.getWidth() - b.getWidth();
    496         long d1 = a.getHeight() - b.getHeight();
    497         return d0 * d0 + d1 * d1;
    498     }
    499 
    500     // Keep up to date with rounding behavior in
    501     // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
    502     static Size findClosestSize(Size size, Size[] supportedSizes) {
    503         if (size == null || supportedSizes == null) {
    504             return null;
    505         }
    506         Size bestSize = null;
    507         for (Size s : supportedSizes) {
    508             if (s.equals(size)) {
    509                 return size;
    510             } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
    511                     LegacyCameraDevice.findEuclidDistSquare(size, s) <
    512                     LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
    513                 bestSize = s;
    514             }
    515         }
    516         return bestSize;
    517     }
    518 
    519     /**
    520      * Query the surface for its currently configured default buffer size.
    521      * @param surface a non-{@code null} {@code Surface}
    522      * @return the width and height of the surface
    523      *
    524      * @throws NullPointerException if the {@code surface} was {@code null}
    525      * @throws IllegalStateException if the {@code surface} was invalid
    526      */
    527     public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
    528         checkNotNull(surface);
    529 
    530         int[] dimens = new int[2];
    531         LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
    532 
    533         return new Size(dimens[0], dimens[1]);
    534     }
    535 
    536     public static boolean isFlexibleConsumer(Surface output) {
    537         int usageFlags = detectSurfaceUsageFlags(output);
    538 
    539         // Keep up to date with allowed consumer types in
    540         // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
    541         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
    542         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
    543             GRALLOC_USAGE_HW_COMPOSER;
    544         boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
    545                 (usageFlags & allowedFlags) != 0);
    546         return flexibleConsumer;
    547     }
    548 
    549     /**
    550      * Query the surface for its currently configured usage flags
    551      */
    552     static int detectSurfaceUsageFlags(Surface surface) {
    553         checkNotNull(surface);
    554         return nativeDetectSurfaceUsageFlags(surface);
    555     }
    556 
    557     /**
    558      * Query the surface for its currently configured format
    559      */
    560     public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
    561         checkNotNull(surface);
    562         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
    563     }
    564 
    565     static void configureSurface(Surface surface, int width, int height,
    566                                  int pixelFormat) throws BufferQueueAbandonedException {
    567         checkNotNull(surface);
    568         checkArgumentPositive(width, "width must be positive.");
    569         checkArgumentPositive(height, "height must be positive.");
    570 
    571         LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
    572                 pixelFormat));
    573     }
    574 
    575     static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
    576                              int height, int pixelFormat)
    577             throws BufferQueueAbandonedException {
    578         checkNotNull(surface);
    579         checkNotNull(pixelBuffer);
    580         checkArgumentPositive(width, "width must be positive.");
    581         checkArgumentPositive(height, "height must be positive.");
    582 
    583         LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
    584                 pixelFormat));
    585     }
    586 
    587     static void setSurfaceFormat(Surface surface, int pixelFormat)
    588             throws BufferQueueAbandonedException {
    589         checkNotNull(surface);
    590 
    591         LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
    592     }
    593 
    594     static void setSurfaceDimens(Surface surface, int width, int height)
    595             throws BufferQueueAbandonedException {
    596         checkNotNull(surface);
    597         checkArgumentPositive(width, "width must be positive.");
    598         checkArgumentPositive(height, "height must be positive.");
    599 
    600         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
    601     }
    602 
    603     static long getSurfaceId(Surface surface) {
    604         checkNotNull(surface);
    605         return nativeGetSurfaceId(surface);
    606     }
    607 
    608     static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
    609         if (surfaces == null) {
    610             throw new NullPointerException("Null argument surfaces");
    611         }
    612         List<Long> surfaceIds = new ArrayList<>();
    613         for (Surface s : surfaces) {
    614             long id = getSurfaceId(s);
    615             if (id == 0) {
    616                 throw new IllegalStateException(
    617                         "Configured surface had null native GraphicBufferProducer pointer!");
    618             }
    619             surfaceIds.add(id);
    620         }
    621         return surfaceIds;
    622     }
    623 
    624     static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
    625         long id = getSurfaceId(s);
    626         return ids.contains(id);
    627     }
    628 
    629     static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
    630             throws BufferQueueAbandonedException {
    631         checkNotNull(surface);
    632         LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
    633                 sensorOrientation));
    634     }
    635 
    636     static Size getTextureSize(SurfaceTexture surfaceTexture)
    637             throws BufferQueueAbandonedException {
    638         checkNotNull(surfaceTexture);
    639 
    640         int[] dimens = new int[2];
    641         LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
    642                 /*out*/dimens));
    643 
    644         return new Size(dimens[0], dimens[1]);
    645     }
    646 
    647     static void setNextTimestamp(Surface surface, long timestamp)
    648             throws BufferQueueAbandonedException {
    649         checkNotNull(surface);
    650         LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
    651     }
    652 
    653     private static native int nativeDetectSurfaceType(Surface surface);
    654 
    655     private static native int nativeDetectSurfaceDimens(Surface surface,
    656             /*out*/int[/*2*/] dimens);
    657 
    658     private static native int nativeConfigureSurface(Surface surface, int width, int height,
    659                                                         int pixelFormat);
    660 
    661     private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
    662                                                     int height, int pixelFormat);
    663 
    664     private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
    665 
    666     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
    667 
    668     private static native long nativeGetSurfaceId(Surface surface);
    669 
    670     private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
    671                                                              int sensorOrientation);
    672 
    673     private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
    674             /*out*/int[/*2*/] dimens);
    675 
    676     private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
    677 
    678     private static native int nativeDetectSurfaceUsageFlags(Surface surface);
    679 
    680     static native int nativeGetJpegFooterSize();
    681 }
    682