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