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                         if (surfaceType == ImageFormat.PRIVATE) {
352 
353                             // YUV_420_888 is always present in LEGACY for all
354                             // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
355                             // API (i.e. {@code #getOutputSizes} works here).
356                             sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
357                         } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
358                             sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
359                         }
360                     }
361 
362                     if (!ArrayUtils.contains(sizes, s)) {
363                         if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
364                             sizedSurfaces.add(new Pair<>(output, s));
365                         } else {
366                             String reason = (sizes == null) ? "format is invalid." :
367                                     ("size not in valid set: " + Arrays.toString(sizes));
368                             Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
369                                     "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
370                                     surfaceType, reason));
371                             return BAD_VALUE;
372                         }
373                     } else {
374                         sizedSurfaces.add(new Pair<>(output, s));
375                     }
376                     // Lock down the size before configuration
377                     setSurfaceDimens(output, s.getWidth(), s.getHeight());
378                 } catch (BufferQueueAbandonedException e) {
379                     Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
380                     return BAD_VALUE;
381                 }
382 
383             }
384         }
385 
386         boolean success = false;
387         if (mDeviceState.setConfiguring()) {
388             mRequestThreadManager.configure(sizedSurfaces);
389             success = mDeviceState.setIdle();
390         }
391 
392         if (success) {
393             mConfiguredSurfaces = outputs;
394         } else {
395             return LegacyExceptionUtils.INVALID_OPERATION;
396         }
397         return LegacyExceptionUtils.NO_ERROR;
398     }
399 
400     /**
401      * Submit a burst of capture requests.
402      *
403      * @param requestList a list of capture requests to execute.
404      * @param repeating {@code true} if this burst is repeating.
405      * @return the submission info, including the new request id, and the last frame number, which
406      *   contains either the frame number of the last frame that will be returned for this request,
407      *   or the frame number of the last frame that will be returned for the current repeating
408      *   request if this burst is set to be repeating.
409      */
submitRequestList(CaptureRequest[] requestList, boolean repeating)410     public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) {
411         if (requestList == null || requestList.length == 0) {
412             Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
413             throw new ServiceSpecificException(BAD_VALUE,
414                     "submitRequestList - Empty/null requests are not allowed");
415         }
416 
417         List<Long> surfaceIds;
418 
419         try {
420             surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
421                     getSurfaceIds(mConfiguredSurfaces);
422         } catch (BufferQueueAbandonedException e) {
423             throw new ServiceSpecificException(BAD_VALUE,
424                     "submitRequestList - configured surface is abandoned.");
425         }
426 
427         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
428         for (CaptureRequest request : requestList) {
429             if (request.getTargets().isEmpty()) {
430                 Log.e(TAG, "submitRequestList - "
431                         + "Each request must have at least one Surface target");
432                 throw new ServiceSpecificException(BAD_VALUE,
433                         "submitRequestList - "
434                         + "Each request must have at least one Surface target");
435             }
436 
437             for (Surface surface : request.getTargets()) {
438                 if (surface == null) {
439                     Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
440                     throw new ServiceSpecificException(BAD_VALUE,
441                             "submitRequestList - Null Surface targets are not allowed");
442                 } else if (mConfiguredSurfaces == null) {
443                     Log.e(TAG, "submitRequestList - must configure " +
444                             " device with valid surfaces before submitting requests");
445                     throw new ServiceSpecificException(INVALID_OPERATION,
446                             "submitRequestList - must configure " +
447                             " device with valid surfaces before submitting requests");
448                 } else if (!containsSurfaceId(surface, surfaceIds)) {
449                     Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
450                     throw new ServiceSpecificException(BAD_VALUE,
451                             "submitRequestList - cannot use a surface that wasn't configured");
452                 }
453             }
454         }
455 
456         // TODO: further validation of request here
457         mIdle.close();
458         return mRequestThreadManager.submitCaptureRequests(requestList, repeating);
459     }
460 
461     /**
462      * Submit a single capture request.
463      *
464      * @param request the capture request to execute.
465      * @param repeating {@code true} if this request is repeating.
466      * @return the submission info, including the new request id, and the last frame number, which
467      *   contains either the frame number of the last frame that will be returned for this request,
468      *   or the frame number of the last frame that will be returned for the current repeating
469      *   request if this burst is set to be repeating.
470      */
submitRequest(CaptureRequest request, boolean repeating)471     public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) {
472         CaptureRequest[] requestList = { request };
473         return submitRequestList(requestList, repeating);
474     }
475 
476     /**
477      * Cancel the repeating request with the given request id.
478      *
479      * @param requestId the request id of the request to cancel.
480      * @return the last frame number to be returned from the HAL for the given repeating request, or
481      *          {@code INVALID_FRAME} if none exists.
482      */
cancelRequest(int requestId)483     public long cancelRequest(int requestId) {
484         return mRequestThreadManager.cancelRepeating(requestId);
485     }
486 
487     /**
488      * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
489      */
waitUntilIdle()490     public void waitUntilIdle()  {
491         mIdle.block();
492     }
493 
494     /**
495      * Flush any pending requests.
496      *
497      * @return the last frame number.
498      */
flush()499     public long flush() {
500         long lastFrame = mRequestThreadManager.flush();
501         waitUntilIdle();
502         return lastFrame;
503     }
504 
505     /**
506      * Return {@code true} if the device has been closed.
507      */
isClosed()508     public boolean isClosed() {
509         return mClosed;
510     }
511 
512     @Override
close()513     public void close() {
514         mRequestThreadManager.quit();
515         mCallbackHandlerThread.quitSafely();
516         mResultThread.quitSafely();
517 
518         try {
519             mCallbackHandlerThread.join();
520         } catch (InterruptedException e) {
521             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
522                     mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
523         }
524 
525         try {
526             mResultThread.join();
527         } catch (InterruptedException e) {
528             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
529                     mResultThread.getName(), mResultThread.getId()));
530         }
531 
532         mClosed = true;
533     }
534 
535     @Override
finalize()536     protected void finalize() throws Throwable {
537         try {
538             close();
539         } catch (ServiceSpecificException e) {
540             Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
541         } finally {
542             super.finalize();
543         }
544     }
545 
findEuclidDistSquare(Size a, Size b)546     static long findEuclidDistSquare(Size a, Size b) {
547         long d0 = a.getWidth() - b.getWidth();
548         long d1 = a.getHeight() - b.getHeight();
549         return d0 * d0 + d1 * d1;
550     }
551 
552     // Keep up to date with rounding behavior in
553     // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
findClosestSize(Size size, Size[] supportedSizes)554     static Size findClosestSize(Size size, Size[] supportedSizes) {
555         if (size == null || supportedSizes == null) {
556             return null;
557         }
558         Size bestSize = null;
559         for (Size s : supportedSizes) {
560             if (s.equals(size)) {
561                 return size;
562             } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
563                     LegacyCameraDevice.findEuclidDistSquare(size, s) <
564                     LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
565                 bestSize = s;
566             }
567         }
568         return bestSize;
569     }
570 
571     /**
572      * Query the surface for its currently configured default buffer size.
573      * @param surface a non-{@code null} {@code Surface}
574      * @return the width and height of the surface
575      *
576      * @throws NullPointerException if the {@code surface} was {@code null}
577      * @throws BufferQueueAbandonedException if the {@code surface} was invalid
578      */
getSurfaceSize(Surface surface)579     public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
580         checkNotNull(surface);
581 
582         int[] dimens = new int[2];
583         LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
584 
585         return new Size(dimens[0], dimens[1]);
586     }
587 
isFlexibleConsumer(Surface output)588     public static boolean isFlexibleConsumer(Surface output) {
589         int usageFlags = detectSurfaceUsageFlags(output);
590 
591         // Keep up to date with allowed consumer types in
592         // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
593         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
594         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
595             GRALLOC_USAGE_HW_COMPOSER;
596         boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
597                 (usageFlags & allowedFlags) != 0);
598         return flexibleConsumer;
599     }
600 
isPreviewConsumer(Surface output)601     public static boolean isPreviewConsumer(Surface output) {
602         int usageFlags = detectSurfaceUsageFlags(output);
603         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
604                 GRALLOC_USAGE_SW_READ_OFTEN;
605         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
606                 GRALLOC_USAGE_HW_RENDER;
607         boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
608                 (usageFlags & allowedFlags) != 0);
609         int surfaceFormat = ImageFormat.UNKNOWN;
610         try {
611             surfaceFormat = detectSurfaceType(output);
612         } catch(BufferQueueAbandonedException e) {
613             throw new IllegalArgumentException("Surface was abandoned", e);
614         }
615 
616         return previewConsumer;
617     }
618 
isVideoEncoderConsumer(Surface output)619     public static boolean isVideoEncoderConsumer(Surface output) {
620         int usageFlags = detectSurfaceUsageFlags(output);
621         int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
622                 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
623         int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
624         boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
625                 (usageFlags & allowedFlags) != 0);
626 
627         int surfaceFormat = ImageFormat.UNKNOWN;
628         try {
629             surfaceFormat = detectSurfaceType(output);
630         } catch(BufferQueueAbandonedException e) {
631             throw new IllegalArgumentException("Surface was abandoned", e);
632         }
633 
634         return videoEncoderConsumer;
635     }
636 
637     /**
638      * Query the surface for its currently configured usage flags
639      */
detectSurfaceUsageFlags(Surface surface)640     static int detectSurfaceUsageFlags(Surface surface) {
641         checkNotNull(surface);
642         return nativeDetectSurfaceUsageFlags(surface);
643     }
644 
645     /**
646      * Query the surface for its currently configured format
647      */
detectSurfaceType(Surface surface)648     public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
649         checkNotNull(surface);
650         int surfaceType = nativeDetectSurfaceType(surface);
651 
652         // TODO: remove this override since the default format should be
653         // ImageFormat.PRIVATE. b/9487482
654         if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
655                 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
656             surfaceType = ImageFormat.PRIVATE;
657         }
658 
659         return LegacyExceptionUtils.throwOnError(surfaceType);
660     }
661 
662     /**
663      * Query the surface for its currently configured dataspace
664      */
detectSurfaceDataspace(Surface surface)665     public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException {
666         checkNotNull(surface);
667         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
668     }
669 
connectSurface(Surface surface)670     static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
671         checkNotNull(surface);
672 
673         LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
674     }
675 
disconnectSurface(Surface surface)676     static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
677         if (surface == null) return;
678 
679         LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
680     }
681 
produceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)682     static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
683                              int height, int pixelFormat)
684             throws BufferQueueAbandonedException {
685         checkNotNull(surface);
686         checkNotNull(pixelBuffer);
687         checkArgumentPositive(width, "width must be positive.");
688         checkArgumentPositive(height, "height must be positive.");
689 
690         LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
691                 pixelFormat));
692     }
693 
setSurfaceFormat(Surface surface, int pixelFormat)694     static void setSurfaceFormat(Surface surface, int pixelFormat)
695             throws BufferQueueAbandonedException {
696         checkNotNull(surface);
697 
698         LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
699     }
700 
setSurfaceDimens(Surface surface, int width, int height)701     static void setSurfaceDimens(Surface surface, int width, int height)
702             throws BufferQueueAbandonedException {
703         checkNotNull(surface);
704         checkArgumentPositive(width, "width must be positive.");
705         checkArgumentPositive(height, "height must be positive.");
706 
707         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
708     }
709 
getSurfaceId(Surface surface)710     static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
711         checkNotNull(surface);
712         try {
713             return nativeGetSurfaceId(surface);
714         } catch (IllegalArgumentException e) {
715             throw new BufferQueueAbandonedException();
716         }
717     }
718 
getSurfaceIds(SparseArray<Surface> surfaces)719     static List<Long> getSurfaceIds(SparseArray<Surface> surfaces)
720             throws BufferQueueAbandonedException {
721         if (surfaces == null) {
722             throw new NullPointerException("Null argument surfaces");
723         }
724         List<Long> surfaceIds = new ArrayList<>();
725         int count = surfaces.size();
726         for (int i = 0; i < count; i++) {
727             long id = getSurfaceId(surfaces.valueAt(i));
728             if (id == 0) {
729                 throw new IllegalStateException(
730                         "Configured surface had null native GraphicBufferProducer pointer!");
731             }
732             surfaceIds.add(id);
733         }
734         return surfaceIds;
735     }
736 
getSurfaceIds(Collection<Surface> surfaces)737     static List<Long> getSurfaceIds(Collection<Surface> surfaces)
738             throws BufferQueueAbandonedException {
739         if (surfaces == null) {
740             throw new NullPointerException("Null argument surfaces");
741         }
742         List<Long> surfaceIds = new ArrayList<>();
743         for (Surface s : surfaces) {
744             long id = getSurfaceId(s);
745             if (id == 0) {
746                 throw new IllegalStateException(
747                         "Configured surface had null native GraphicBufferProducer pointer!");
748             }
749             surfaceIds.add(id);
750         }
751         return surfaceIds;
752     }
753 
containsSurfaceId(Surface s, Collection<Long> ids)754     static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
755         long id = 0;
756         try {
757             id = getSurfaceId(s);
758         } catch (BufferQueueAbandonedException e) {
759             // If surface is abandoned, return false.
760             return false;
761         }
762         return ids.contains(id);
763     }
764 
setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)765     static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
766             throws BufferQueueAbandonedException {
767         checkNotNull(surface);
768         LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
769                 sensorOrientation));
770     }
771 
getTextureSize(SurfaceTexture surfaceTexture)772     static Size getTextureSize(SurfaceTexture surfaceTexture)
773             throws BufferQueueAbandonedException {
774         checkNotNull(surfaceTexture);
775 
776         int[] dimens = new int[2];
777         LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
778                 /*out*/dimens));
779 
780         return new Size(dimens[0], dimens[1]);
781     }
782 
setNextTimestamp(Surface surface, long timestamp)783     static void setNextTimestamp(Surface surface, long timestamp)
784             throws BufferQueueAbandonedException {
785         checkNotNull(surface);
786         LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
787     }
788 
setScalingMode(Surface surface, int mode)789     static void setScalingMode(Surface surface, int mode)
790             throws BufferQueueAbandonedException {
791         checkNotNull(surface);
792         LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode));
793     }
794 
795 
nativeDetectSurfaceType(Surface surface)796     private static native int nativeDetectSurfaceType(Surface surface);
797 
nativeDetectSurfaceDataspace(Surface surface)798     private static native int nativeDetectSurfaceDataspace(Surface surface);
799 
nativeDetectSurfaceDimens(Surface surface, int[ ] dimens)800     private static native int nativeDetectSurfaceDimens(Surface surface,
801             /*out*/int[/*2*/] dimens);
802 
nativeConnectSurface(Surface surface)803     private static native int nativeConnectSurface(Surface surface);
804 
nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)805     private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
806                                                     int height, int pixelFormat);
807 
nativeSetSurfaceFormat(Surface surface, int pixelFormat)808     private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
809 
nativeSetSurfaceDimens(Surface surface, int width, int height)810     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
811 
nativeGetSurfaceId(Surface surface)812     private static native long nativeGetSurfaceId(Surface surface);
813 
nativeSetSurfaceOrientation(Surface surface, int facing, int sensorOrientation)814     private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
815                                                              int sensorOrientation);
816 
nativeDetectTextureDimens(SurfaceTexture surfaceTexture, int[ ] dimens)817     private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
818             /*out*/int[/*2*/] dimens);
819 
nativeSetNextTimestamp(Surface surface, long timestamp)820     private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
821 
nativeDetectSurfaceUsageFlags(Surface surface)822     private static native int nativeDetectSurfaceUsageFlags(Surface surface);
823 
nativeSetScalingMode(Surface surface, int scalingMode)824     private static native int nativeSetScalingMode(Surface surface, int scalingMode);
825 
nativeDisconnectSurface(Surface surface)826     private static native int nativeDisconnectSurface(Surface surface);
827 
nativeGetJpegFooterSize()828     static native int nativeGetJpegFooterSize();
829 }
830