• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.legacy;
18 
19 import android.graphics.ImageFormat;
20 import android.graphics.SurfaceTexture;
21 import android.hardware.Camera;
22 import android.hardware.camera2.CameraCharacteristics;
23 import android.hardware.camera2.CaptureRequest;
24 import android.hardware.camera2.impl.CameraDeviceImpl;
25 import android.hardware.camera2.impl.CaptureResultExtras;
26 import android.hardware.camera2.ICameraDeviceCallbacks;
27 import android.hardware.camera2.params.StreamConfigurationMap;
28 import android.hardware.camera2.utils.ArrayUtils;
29 import android.hardware.camera2.utils.SubmitInfo;
30 import android.hardware.camera2.impl.CameraMetadataNative;
31 import android.os.ConditionVariable;
32 import android.os.Handler;
33 import android.os.HandlerThread;
34 import android.os.RemoteException;
35 import android.os.ServiceSpecificException;
36 import android.util.Log;
37 import android.util.Pair;
38 import android.util.Size;
39 import android.util.SparseArray;
40 import android.view.Surface;
41 
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Collection;
45 import java.util.List;
46 
47 import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
48 import static 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  */
60 public 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 
getExtrasFromRequest(RequestHolder holder)92     private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
93         return getExtrasFromRequest(holder,
94                 /*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null);
95     }
96 
getExtrasFromRequest(RequestHolder holder, int errorCode, Object errorArg)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 onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
227             final CaptureResultExtras extras = getExtrasFromRequest(holder);
228 
229             mResultHandler.post(new Runnable() {
230                 @Override
231                 public void run() {
232                     if (DEBUG) {
233                         Log.d(TAG, "doing onCaptureResult callback for request " +
234                                 holder.getRequestId());
235                     }
236                     try {
237                         mDeviceCallbacks.onResultReceived(result, extras);
238                     } catch (RemoteException e) {
239                         throw new IllegalStateException(
240                                 "Received remote exception during onCameraError callback: ", e);
241                     }
242                 }
243             });
244         }
245 
246         @Override
247         public void onRepeatingRequestError(final long lastFrameNumber) {
248             mResultHandler.post(new Runnable() {
249                 @Override
250                 public void run() {
251                     if (DEBUG) {
252                         Log.d(TAG, "doing onRepeatingRequestError callback.");
253                     }
254                     try {
255                         mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber);
256                     } catch (RemoteException e) {
257                         throw new IllegalStateException(
258                                 "Received remote exception during onRepeatingRequestError " +
259                                 "callback: ", e);
260                     }
261                 }
262             });
263         }
264     };
265 
266     private final RequestThreadManager mRequestThreadManager;
267 
268     /**
269      * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
270      * converted to this; YV12 and NV21 are the two currently supported formats.
271      *
272      * @param s the surface to check.
273      * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
274      *          format.
275      */
needsConversion(Surface s)276     static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
277         int nativeType = detectSurfaceType(s);
278         return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
279                 nativeType == ImageFormat.NV21;
280     }
281 
282     /**
283      * Create a new emulated camera device from a given Camera 1 API camera.
284      *
285      * <p>
286      * The {@link Camera} provided to this constructor must already have been successfully opened,
287      * and ownership of the provided camera is passed to this object.  No further calls to the
288      * camera methods should be made following this constructor.
289      * </p>
290      *
291      * @param cameraId the id of the camera.
292      * @param camera an open {@link Camera} device.
293      * @param characteristics the static camera characteristics for this camera device
294      * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
295      */
LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, ICameraDeviceCallbacks callbacks)296     public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
297             ICameraDeviceCallbacks callbacks) {
298         mCameraId = cameraId;
299         mDeviceCallbacks = callbacks;
300         TAG = String.format("CameraDevice-%d-LE", mCameraId);
301 
302         mResultThread.start();
303         mResultHandler = new Handler(mResultThread.getLooper());
304         mCallbackHandlerThread.start();
305         mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
306         mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
307         mStaticCharacteristics = characteristics;
308         mRequestThreadManager =
309                 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
310         mRequestThreadManager.start();
311     }
312 
313     /**
314      * Configure the device with a set of output surfaces.
315      *
316      * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
317      *
318      * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
319      *
320      * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
321      *          list; it must not be modified by the caller once it's passed in.
322      * @return an error code for this binder operation, or {@link NO_ERROR}
323      *          on success.
324      */
configureOutputs(SparseArray<Surface> outputs)325     public int configureOutputs(SparseArray<Surface> outputs) {
326         List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
327         if (outputs != null) {
328             int count = outputs.size();
329             for (int i = 0; i < count; i++)  {
330                 Surface output = outputs.valueAt(i);
331                 if (output == null) {
332                     Log.e(TAG, "configureOutputs - null outputs are not allowed");
333                     return BAD_VALUE;
334                 }
335                 if (!output.isValid()) {
336                     Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed");
337                     return BAD_VALUE;
338                 }
339                 StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
340                         get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
341 
342                 // Validate surface size and format.
343                 try {
344                     Size s = getSurfaceSize(output);
345                     int surfaceType = detectSurfaceType(output);
346 
347                     boolean flexibleConsumer = isFlexibleConsumer(output);
348 
349                     Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
350                     if (sizes == null) {
351                         // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
352                         if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
353                             surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
354 
355                             // YUV_420_888 is always present in LEGACY for all
356                             // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
357                             // API (i.e. {@code #getOutputSizes} works here).
358                             sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
359                         } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
360                             sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
361                         }
362                     }
363 
364                     if (!ArrayUtils.contains(sizes, s)) {
365                         if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
366                             sizedSurfaces.add(new Pair<>(output, s));
367                         } else {
368                             String reason = (sizes == null) ? "format is invalid." :
369                                     ("size not in valid set: " + Arrays.toString(sizes));
370                             Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
371                                     "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
372                                     surfaceType, reason));
373                             return BAD_VALUE;
374                         }
375                     } else {
376                         sizedSurfaces.add(new Pair<>(output, s));
377                     }
378                     // Lock down the size before configuration
379                     setSurfaceDimens(output, s.getWidth(), s.getHeight());
380                 } catch (BufferQueueAbandonedException e) {
381                     Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
382                     return BAD_VALUE;
383                 }
384 
385             }
386         }
387 
388         boolean success = false;
389         if (mDeviceState.setConfiguring()) {
390             mRequestThreadManager.configure(sizedSurfaces);
391             success = mDeviceState.setIdle();
392         }
393 
394         if (success) {
395             mConfiguredSurfaces = outputs;
396         } else {
397             return LegacyExceptionUtils.INVALID_OPERATION;
398         }
399         return LegacyExceptionUtils.NO_ERROR;
400     }
401 
402     /**
403      * Submit a burst of capture requests.
404      *
405      * @param requestList a list of capture requests to execute.
406      * @param repeating {@code true} if this burst is repeating.
407      * @return the submission info, including the new request id, and the last frame number, which
408      *   contains either the frame number of the last frame that will be returned for this request,
409      *   or the frame number of the last frame that will be returned for the current repeating
410      *   request if this burst is set to be repeating.
411      */
submitRequestList(CaptureRequest[] requestList, boolean repeating)412     public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) {
413         if (requestList == null || requestList.length == 0) {
414             Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
415             throw new ServiceSpecificException(BAD_VALUE,
416                     "submitRequestList - Empty/null requests are not allowed");
417         }
418 
419         List<Long> surfaceIds;
420 
421         try {
422             surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
423                     getSurfaceIds(mConfiguredSurfaces);
424         } catch (BufferQueueAbandonedException e) {
425             throw new ServiceSpecificException(BAD_VALUE,
426                     "submitRequestList - configured surface is abandoned.");
427         }
428 
429         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
430         for (CaptureRequest request : requestList) {
431             if (request.getTargets().isEmpty()) {
432                 Log.e(TAG, "submitRequestList - "
433                         + "Each request must have at least one Surface target");
434                 throw new ServiceSpecificException(BAD_VALUE,
435                         "submitRequestList - "
436                         + "Each request must have at least one Surface target");
437             }
438 
439             for (Surface surface : request.getTargets()) {
440                 if (surface == null) {
441                     Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
442                     throw new ServiceSpecificException(BAD_VALUE,
443                             "submitRequestList - Null Surface targets are not allowed");
444                 } else if (mConfiguredSurfaces == null) {
445                     Log.e(TAG, "submitRequestList - must configure " +
446                             " device with valid surfaces before submitting requests");
447                     throw new ServiceSpecificException(INVALID_OPERATION,
448                             "submitRequestList - must configure " +
449                             " device with valid surfaces before submitting requests");
450                 } else if (!containsSurfaceId(surface, surfaceIds)) {
451                     Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
452                     throw new ServiceSpecificException(BAD_VALUE,
453                             "submitRequestList - cannot use a surface that wasn't configured");
454                 }
455             }
456         }
457 
458         // TODO: further validation of request here
459         mIdle.close();
460         return mRequestThreadManager.submitCaptureRequests(requestList, repeating);
461     }
462 
463     /**
464      * Submit a single capture request.
465      *
466      * @param request the capture request to execute.
467      * @param repeating {@code true} if this request is repeating.
468      * @return the submission info, including the new request id, and the last frame number, which
469      *   contains either the frame number of the last frame that will be returned for this request,
470      *   or the frame number of the last frame that will be returned for the current repeating
471      *   request if this burst is set to be repeating.
472      */
submitRequest(CaptureRequest request, boolean repeating)473     public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) {
474         CaptureRequest[] requestList = { request };
475         return submitRequestList(requestList, repeating);
476     }
477 
478     /**
479      * Cancel the repeating request with the given request id.
480      *
481      * @param requestId the request id of the request to cancel.
482      * @return the last frame number to be returned from the HAL for the given repeating request, or
483      *          {@code INVALID_FRAME} if none exists.
484      */
cancelRequest(int requestId)485     public long cancelRequest(int requestId) {
486         return mRequestThreadManager.cancelRepeating(requestId);
487     }
488 
489     /**
490      * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
491      */
waitUntilIdle()492     public void waitUntilIdle()  {
493         mIdle.block();
494     }
495 
496     /**
497      * Flush any pending requests.
498      *
499      * @return the last frame number.
500      */
flush()501     public long flush() {
502         long lastFrame = mRequestThreadManager.flush();
503         waitUntilIdle();
504         return lastFrame;
505     }
506 
507     /**
508      * Return {@code true} if the device has been closed.
509      */
isClosed()510     public boolean isClosed() {
511         return mClosed;
512     }
513 
514     @Override
close()515     public void close() {
516         mRequestThreadManager.quit();
517         mCallbackHandlerThread.quitSafely();
518         mResultThread.quitSafely();
519 
520         try {
521             mCallbackHandlerThread.join();
522         } catch (InterruptedException e) {
523             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
524                     mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
525         }
526 
527         try {
528             mResultThread.join();
529         } catch (InterruptedException e) {
530             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
531                     mResultThread.getName(), mResultThread.getId()));
532         }
533 
534         mClosed = true;
535     }
536 
537     @Override
finalize()538     protected void finalize() throws Throwable {
539         try {
540             close();
541         } catch (ServiceSpecificException e) {
542             Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
543         } finally {
544             super.finalize();
545         }
546     }
547 
findEuclidDistSquare(Size a, Size b)548     static long findEuclidDistSquare(Size a, Size b) {
549         long d0 = a.getWidth() - b.getWidth();
550         long d1 = a.getHeight() - b.getHeight();
551         return d0 * d0 + d1 * d1;
552     }
553 
554     // Keep up to date with rounding behavior in
555     // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
findClosestSize(Size size, Size[] supportedSizes)556     static Size findClosestSize(Size size, Size[] supportedSizes) {
557         if (size == null || supportedSizes == null) {
558             return null;
559         }
560         Size bestSize = null;
561         for (Size s : supportedSizes) {
562             if (s.equals(size)) {
563                 return size;
564             } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
565                     LegacyCameraDevice.findEuclidDistSquare(size, s) <
566                     LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
567                 bestSize = s;
568             }
569         }
570         return bestSize;
571     }
572 
573     /**
574      * Query the surface for its currently configured default buffer size.
575      * @param surface a non-{@code null} {@code Surface}
576      * @return the width and height of the surface
577      *
578      * @throws NullPointerException if the {@code surface} was {@code null}
579      * @throws BufferQueueAbandonedException if the {@code surface} was invalid
580      */
getSurfaceSize(Surface surface)581     public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
582         checkNotNull(surface);
583 
584         int[] dimens = new int[2];
585         LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
586 
587         return new Size(dimens[0], dimens[1]);
588     }
589 
isFlexibleConsumer(Surface output)590     public static boolean isFlexibleConsumer(Surface output) {
591         int usageFlags = detectSurfaceUsageFlags(output);
592 
593         // Keep up to date with allowed consumer types in
594         // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
595         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
596         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
597             GRALLOC_USAGE_HW_COMPOSER;
598         boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
599                 (usageFlags & allowedFlags) != 0);
600         return flexibleConsumer;
601     }
602 
isPreviewConsumer(Surface output)603     public static boolean isPreviewConsumer(Surface output) {
604         int usageFlags = detectSurfaceUsageFlags(output);
605         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
606                 GRALLOC_USAGE_SW_READ_OFTEN;
607         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
608                 GRALLOC_USAGE_HW_RENDER;
609         boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
610                 (usageFlags & allowedFlags) != 0);
611         int surfaceFormat = ImageFormat.UNKNOWN;
612         try {
613             surfaceFormat = detectSurfaceType(output);
614         } catch(BufferQueueAbandonedException e) {
615             throw new IllegalArgumentException("Surface was abandoned", e);
616         }
617 
618         return previewConsumer;
619     }
620 
isVideoEncoderConsumer(Surface output)621     public static boolean isVideoEncoderConsumer(Surface output) {
622         int usageFlags = detectSurfaceUsageFlags(output);
623         int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
624                 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
625         int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
626         boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
627                 (usageFlags & allowedFlags) != 0);
628 
629         int surfaceFormat = ImageFormat.UNKNOWN;
630         try {
631             surfaceFormat = detectSurfaceType(output);
632         } catch(BufferQueueAbandonedException e) {
633             throw new IllegalArgumentException("Surface was abandoned", e);
634         }
635 
636         return videoEncoderConsumer;
637     }
638 
639     /**
640      * Query the surface for its currently configured usage flags
641      */
detectSurfaceUsageFlags(Surface surface)642     static int detectSurfaceUsageFlags(Surface surface) {
643         checkNotNull(surface);
644         return nativeDetectSurfaceUsageFlags(surface);
645     }
646 
647     /**
648      * Query the surface for its currently configured format
649      */
detectSurfaceType(Surface surface)650     public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
651         checkNotNull(surface);
652         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
653     }
654 
655     /**
656      * Query the surface for its currently configured dataspace
657      */
detectSurfaceDataspace(Surface surface)658     public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException {
659         checkNotNull(surface);
660         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
661     }
662 
connectSurface(Surface surface)663     static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
664         checkNotNull(surface);
665 
666         LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
667     }
668 
disconnectSurface(Surface surface)669     static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
670         if (surface == null) return;
671 
672         LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
673     }
674 
produceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)675     static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
676                              int height, int pixelFormat)
677             throws BufferQueueAbandonedException {
678         checkNotNull(surface);
679         checkNotNull(pixelBuffer);
680         checkArgumentPositive(width, "width must be positive.");
681         checkArgumentPositive(height, "height must be positive.");
682 
683         LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
684                 pixelFormat));
685     }
686 
setSurfaceFormat(Surface surface, int pixelFormat)687     static void setSurfaceFormat(Surface surface, int pixelFormat)
688             throws BufferQueueAbandonedException {
689         checkNotNull(surface);
690 
691         LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
692     }
693 
setSurfaceDimens(Surface surface, int width, int height)694     static void setSurfaceDimens(Surface surface, int width, int height)
695             throws BufferQueueAbandonedException {
696         checkNotNull(surface);
697         checkArgumentPositive(width, "width must be positive.");
698         checkArgumentPositive(height, "height must be positive.");
699 
700         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
701     }
702 
getSurfaceId(Surface surface)703     static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
704         checkNotNull(surface);
705         try {
706             return nativeGetSurfaceId(surface);
707         } catch (IllegalArgumentException e) {
708             throw new BufferQueueAbandonedException();
709         }
710     }
711 
getSurfaceIds(SparseArray<Surface> surfaces)712     static List<Long> getSurfaceIds(SparseArray<Surface> surfaces)
713             throws BufferQueueAbandonedException {
714         if (surfaces == null) {
715             throw new NullPointerException("Null argument surfaces");
716         }
717         List<Long> surfaceIds = new ArrayList<>();
718         int count = surfaces.size();
719         for (int i = 0; i < count; i++) {
720             long id = getSurfaceId(surfaces.valueAt(i));
721             if (id == 0) {
722                 throw new IllegalStateException(
723                         "Configured surface had null native GraphicBufferProducer pointer!");
724             }
725             surfaceIds.add(id);
726         }
727         return surfaceIds;
728     }
729 
getSurfaceIds(Collection<Surface> surfaces)730     static List<Long> getSurfaceIds(Collection<Surface> surfaces)
731             throws BufferQueueAbandonedException {
732         if (surfaces == null) {
733             throw new NullPointerException("Null argument surfaces");
734         }
735         List<Long> surfaceIds = new ArrayList<>();
736         for (Surface s : surfaces) {
737             long id = getSurfaceId(s);
738             if (id == 0) {
739                 throw new IllegalStateException(
740                         "Configured surface had null native GraphicBufferProducer pointer!");
741             }
742             surfaceIds.add(id);
743         }
744         return surfaceIds;
745     }
746 
containsSurfaceId(Surface s, Collection<Long> ids)747     static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
748         long id = 0;
749         try {
750             id = getSurfaceId(s);
751         } catch (BufferQueueAbandonedException e) {
752             // If surface is abandoned, return false.
753             return false;
754         }
755         return ids.contains(id);
756     }
757 
setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)758     static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
759             throws BufferQueueAbandonedException {
760         checkNotNull(surface);
761         LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
762                 sensorOrientation));
763     }
764 
getTextureSize(SurfaceTexture surfaceTexture)765     static Size getTextureSize(SurfaceTexture surfaceTexture)
766             throws BufferQueueAbandonedException {
767         checkNotNull(surfaceTexture);
768 
769         int[] dimens = new int[2];
770         LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
771                 /*out*/dimens));
772 
773         return new Size(dimens[0], dimens[1]);
774     }
775 
setNextTimestamp(Surface surface, long timestamp)776     static void setNextTimestamp(Surface surface, long timestamp)
777             throws BufferQueueAbandonedException {
778         checkNotNull(surface);
779         LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
780     }
781 
setScalingMode(Surface surface, int mode)782     static void setScalingMode(Surface surface, int mode)
783             throws BufferQueueAbandonedException {
784         checkNotNull(surface);
785         LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode));
786     }
787 
788 
nativeDetectSurfaceType(Surface surface)789     private static native int nativeDetectSurfaceType(Surface surface);
790 
nativeDetectSurfaceDataspace(Surface surface)791     private static native int nativeDetectSurfaceDataspace(Surface surface);
792 
nativeDetectSurfaceDimens(Surface surface, int[ ] dimens)793     private static native int nativeDetectSurfaceDimens(Surface surface,
794             /*out*/int[/*2*/] dimens);
795 
nativeConnectSurface(Surface surface)796     private static native int nativeConnectSurface(Surface surface);
797 
nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)798     private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
799                                                     int height, int pixelFormat);
800 
nativeSetSurfaceFormat(Surface surface, int pixelFormat)801     private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
802 
nativeSetSurfaceDimens(Surface surface, int width, int height)803     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
804 
nativeGetSurfaceId(Surface surface)805     private static native long nativeGetSurfaceId(Surface surface);
806 
nativeSetSurfaceOrientation(Surface surface, int facing, int sensorOrientation)807     private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
808                                                              int sensorOrientation);
809 
nativeDetectTextureDimens(SurfaceTexture surfaceTexture, int[ ] dimens)810     private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
811             /*out*/int[/*2*/] dimens);
812 
nativeSetNextTimestamp(Surface surface, long timestamp)813     private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
814 
nativeDetectSurfaceUsageFlags(Surface surface)815     private static native int nativeDetectSurfaceUsageFlags(Surface surface);
816 
nativeSetScalingMode(Surface surface, int scalingMode)817     private static native int nativeSetScalingMode(Surface surface, int scalingMode);
818 
nativeDisconnectSurface(Surface surface)819     private static native int nativeDisconnectSurface(Surface surface);
820 
nativeGetJpegFooterSize()821     static native int nativeGetJpegFooterSize();
822 }
823