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