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