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