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