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 
getExtrasFromRequest(RequestHolder holder)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      */
needsConversion(Surface s)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      */
LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, ICameraDeviceCallbacks callbacks)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      */
configureOutputs(List<Surface> outputs)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      */
submitRequestList(List<CaptureRequest> requestList, boolean repeating, LongParcelable frameNumber)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      */
submitRequest(CaptureRequest request, boolean repeating, LongParcelable frameNumber)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      */
cancelRequest(int requestId)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      */
waitUntilIdle()438     public void waitUntilIdle()  {
439         mIdle.block();
440     }
441 
442     /**
443      * Flush any pending requests.
444      *
445      * @return the last frame number.
446      */
flush()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      */
isClosed()456     public boolean isClosed() {
457         return mClosed;
458     }
459 
460     @Override
close()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
finalize()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 
findEuclidDistSquare(Size a, Size b)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
findClosestSize(Size size, Size[] supportedSizes)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      */
getSurfaceSize(Surface surface)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 
isFlexibleConsumer(Surface output)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      */
detectSurfaceUsageFlags(Surface surface)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      */
detectSurfaceType(Surface surface)560     public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
561         checkNotNull(surface);
562         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
563     }
564 
configureSurface(Surface surface, int width, int height, int pixelFormat)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 
produceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)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 
setSurfaceFormat(Surface surface, int pixelFormat)587     static void setSurfaceFormat(Surface surface, int pixelFormat)
588             throws BufferQueueAbandonedException {
589         checkNotNull(surface);
590 
591         LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
592     }
593 
setSurfaceDimens(Surface surface, int width, int height)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 
getSurfaceId(Surface surface)603     static long getSurfaceId(Surface surface) {
604         checkNotNull(surface);
605         return nativeGetSurfaceId(surface);
606     }
607 
getSurfaceIds(Collection<Surface> surfaces)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 
containsSurfaceId(Surface s, Collection<Long> ids)624     static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
625         long id = getSurfaceId(s);
626         return ids.contains(id);
627     }
628 
setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)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 
getTextureSize(SurfaceTexture surfaceTexture)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 
setNextTimestamp(Surface surface, long timestamp)647     static void setNextTimestamp(Surface surface, long timestamp)
648             throws BufferQueueAbandonedException {
649         checkNotNull(surface);
650         LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
651     }
652 
nativeDetectSurfaceType(Surface surface)653     private static native int nativeDetectSurfaceType(Surface surface);
654 
nativeDetectSurfaceDimens(Surface surface, int[ ] dimens)655     private static native int nativeDetectSurfaceDimens(Surface surface,
656             /*out*/int[/*2*/] dimens);
657 
nativeConfigureSurface(Surface surface, int width, int height, int pixelFormat)658     private static native int nativeConfigureSurface(Surface surface, int width, int height,
659                                                         int pixelFormat);
660 
nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)661     private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
662                                                     int height, int pixelFormat);
663 
nativeSetSurfaceFormat(Surface surface, int pixelFormat)664     private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
665 
nativeSetSurfaceDimens(Surface surface, int width, int height)666     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
667 
nativeGetSurfaceId(Surface surface)668     private static native long nativeGetSurfaceId(Surface surface);
669 
nativeSetSurfaceOrientation(Surface surface, int facing, int sensorOrientation)670     private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
671                                                              int sensorOrientation);
672 
nativeDetectTextureDimens(SurfaceTexture surfaceTexture, int[ ] dimens)673     private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
674             /*out*/int[/*2*/] dimens);
675 
nativeSetNextTimestamp(Surface surface, long timestamp)676     private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
677 
nativeDetectSurfaceUsageFlags(Surface surface)678     private static native int nativeDetectSurfaceUsageFlags(Surface surface);
679 
nativeGetJpegFooterSize()680     static native int nativeGetJpegFooterSize();
681 }
682