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