blob: cb59fd14f5c5bd0877e38349b9b85bb5a135563a [file] [log] [blame]
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001/*
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
17package android.hardware.camera2.legacy;
18
19import android.graphics.ImageFormat;
Ruben Brunk28c49c92014-06-16 18:43:59 -070020import android.graphics.SurfaceTexture;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070021import android.hardware.Camera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070022import android.hardware.camera2.CameraCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070023import android.hardware.camera2.CaptureRequest;
Igor Murashkin51dcfd652014-09-25 16:55:01 -070024import android.hardware.camera2.impl.CameraDeviceImpl;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070025import android.hardware.camera2.impl.CaptureResultExtras;
26import android.hardware.camera2.ICameraDeviceCallbacks;
Ruben Brunke663cb772014-09-16 13:18:31 -070027import android.hardware.camera2.params.StreamConfigurationMap;
28import android.hardware.camera2.utils.ArrayUtils;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080029import android.hardware.camera2.utils.SubmitInfo;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070030import android.hardware.camera2.impl.CameraMetadataNative;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070031import android.os.ConditionVariable;
32import android.os.Handler;
33import android.os.HandlerThread;
34import android.os.RemoteException;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080035import android.os.ServiceSpecificException;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070036import android.util.Log;
Ruben Brunkf4a637d2014-11-20 18:01:36 -080037import android.util.Pair;
Igor Murashkina296fec2014-06-23 14:44:09 -070038import android.util.Size;
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -070039import android.util.SparseArray;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070040import android.view.Surface;
41
42import java.util.ArrayList;
Ruben Brunke663cb772014-09-16 13:18:31 -070043import java.util.Arrays;
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -070044import java.util.Collection;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070045import java.util.List;
Igor Murashkin49b2b132014-06-18 19:03:00 -070046
Ruben Brunkef14da32014-06-24 16:06:54 -070047import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
Igor Murashkina296fec2014-06-23 14:44:09 -070048import static com.android.internal.util.Preconditions.*;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070049
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 */
60public class LegacyCameraDevice implements AutoCloseable {
Ruben Brunkfeb50af2014-05-09 19:58:49 -070061 private final String TAG;
62
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070063 private static final boolean DEBUG = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070064 private final int mCameraId;
Ruben Brunke663cb772014-09-16 13:18:31 -070065 private final CameraCharacteristics mStaticCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070066 private final ICameraDeviceCallbacks mDeviceCallbacks;
67 private final CameraDeviceState mDeviceState = new CameraDeviceState();
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -070068 private SparseArray<Surface> mConfiguredSurfaces;
Ruben Brunke663cb772014-09-16 13:18:31 -070069 private boolean mClosed = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070070
71 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070072
Ruben Brunkd85e1a62014-06-11 10:35:45 -070073 private final HandlerThread mResultThread = new HandlerThread("ResultThread");
74 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
Ruben Brunkfeb50af2014-05-09 19:58:49 -070075 private final Handler mCallbackHandler;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070076 private final Handler mResultHandler;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070077 private static final int ILLEGAL_VALUE = -1;
78
Ruben Brunkf4a637d2014-11-20 18:01:36 -080079 // 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;
Zhijun Hea7677722015-06-01 16:36:06 -070084 private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
Ruben Brunkf4a637d2014-11-20 18:01:36 -080085 private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
86
Ruben Brunk68e4fc82015-06-12 16:16:42 -070087 public static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding
Ruben Brunkf4a637d2014-11-20 18:01:36 -080088
Ruben Brunka94c6032015-06-10 16:44:28 -070089 // 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
Ruben Brunkfeb50af2014-05-09 19:58:49 -070092 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -070093 return getExtrasFromRequest(holder,
94 /*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null);
95 }
96
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 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700109 if (holder == null) {
110 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -0800111 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700112 }
113 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
Zhijun He83159152014-07-16 11:32:59 -0700114 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700115 /*partialResultCount*/1, errorStreamId);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700116 }
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
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700125 public void onError(final int errorCode, final Object errorArg, final RequestHolder holder) {
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700126 if (DEBUG) {
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700127 Log.d(TAG, "onError called, errorCode = " + errorCode + ", errorArg = " + errorArg);
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700128 }
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
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700145 final CaptureResultExtras extras = getExtrasFromRequest(holder, errorCode, errorArg);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700146 mResultHandler.post(new Runnable() {
147 @Override
148 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700149 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700150 Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
151 ", with error code " + errorCode);
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700152 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700153 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700154 mDeviceCallbacks.onDeviceError(errorCode, extras);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700155 } catch (RemoteException e) {
156 throw new IllegalStateException(
157 "Received remote exception during onCameraError callback: ", e);
158 }
159 }
160 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700161 }
162
163 @Override
164 public void onConfiguring() {
165 // Do nothing
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700166 if (DEBUG) {
167 Log.d(TAG, "doing onConfiguring callback.");
168 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700169 }
170
171 @Override
172 public void onIdle() {
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700173 if (DEBUG) {
174 Log.d(TAG, "onIdle called");
175 }
176
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700177 mIdle.open();
178
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700179 mResultHandler.post(new Runnable() {
180 @Override
181 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700182 if (DEBUG) {
183 Log.d(TAG, "doing onIdle callback.");
184 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700185 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700186 mDeviceCallbacks.onDeviceIdle();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700187 } catch (RemoteException e) {
188 throw new IllegalStateException(
189 "Received remote exception during onCameraIdle callback: ", e);
190 }
191 }
192 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700193 }
194
195 @Override
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700196 public void onBusy() {
197 mIdle.close();
198
199 if (DEBUG) {
200 Log.d(TAG, "onBusy called");
201 }
202 }
203
204 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700205 public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700206 final CaptureResultExtras extras = getExtrasFromRequest(holder);
207
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700208 mResultHandler.post(new Runnable() {
209 @Override
210 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700211 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700212 Log.d(TAG, "doing onCaptureStarted callback for request " +
213 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700214 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700215 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700216 mDeviceCallbacks.onCaptureStarted(extras, timestamp);
217 } catch (RemoteException e) {
218 throw new IllegalStateException(
219 "Received remote exception during onCameraError callback: ", e);
220 }
221 }
222 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700223 }
224
225 @Override
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700226 public void onRequestQueueEmpty() {
227 mResultHandler.post(new Runnable() {
228 @Override
229 public void run() {
230 if (DEBUG) {
231 Log.d(TAG, "doing onRequestQueueEmpty callback");
232 }
233 try {
234 mDeviceCallbacks.onRequestQueueEmpty();
235 } catch (RemoteException e) {
236 throw new IllegalStateException(
237 "Received remote exception during onRequestQueueEmpty callback: ",
238 e);
239 }
240 }
241 });
242 }
243
244 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700245 public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700246 final CaptureResultExtras extras = getExtrasFromRequest(holder);
247
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700248 mResultHandler.post(new Runnable() {
249 @Override
250 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700251 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700252 Log.d(TAG, "doing onCaptureResult callback for request " +
253 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700254 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700255 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700256 mDeviceCallbacks.onResultReceived(result, extras);
257 } catch (RemoteException e) {
258 throw new IllegalStateException(
259 "Received remote exception during onCameraError callback: ", e);
260 }
261 }
262 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700263 }
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700264
265 @Override
Yin-Chia Yeh8cd12e92017-09-05 18:14:21 -0700266 public void onRepeatingRequestError(final long lastFrameNumber,
267 final int repeatingRequestId) {
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700268 mResultHandler.post(new Runnable() {
269 @Override
270 public void run() {
271 if (DEBUG) {
272 Log.d(TAG, "doing onRepeatingRequestError callback.");
273 }
274 try {
Yin-Chia Yeh8cd12e92017-09-05 18:14:21 -0700275 mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber,
276 repeatingRequestId);
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700277 } catch (RemoteException e) {
278 throw new IllegalStateException(
279 "Received remote exception during onRepeatingRequestError " +
280 "callback: ", e);
281 }
282 }
283 });
284 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700285 };
286
287 private final RequestThreadManager mRequestThreadManager;
288
289 /**
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700290 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
291 * converted to this; YV12 and NV21 are the two currently supported formats.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700292 *
293 * @param s the surface to check.
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700294 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
295 * format.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700296 */
Ruben Brunkef14da32014-06-24 16:06:54 -0700297 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
298 int nativeType = detectSurfaceType(s);
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700299 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
300 nativeType == ImageFormat.NV21;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700301 }
302
303 /**
304 * Create a new emulated camera device from a given Camera 1 API camera.
305 *
306 * <p>
307 * The {@link Camera} provided to this constructor must already have been successfully opened,
308 * and ownership of the provided camera is passed to this object. No further calls to the
309 * camera methods should be made following this constructor.
310 * </p>
311 *
312 * @param cameraId the id of the camera.
313 * @param camera an open {@link Camera} device.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700314 * @param characteristics the static camera characteristics for this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700315 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
316 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700317 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
318 ICameraDeviceCallbacks callbacks) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700319 mCameraId = cameraId;
320 mDeviceCallbacks = callbacks;
321 TAG = String.format("CameraDevice-%d-LE", mCameraId);
322
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700323 mResultThread.start();
324 mResultHandler = new Handler(mResultThread.getLooper());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700325 mCallbackHandlerThread.start();
326 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
327 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
Ruben Brunke663cb772014-09-16 13:18:31 -0700328 mStaticCharacteristics = characteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700329 mRequestThreadManager =
Igor Murashkindf6242e2014-07-01 18:06:13 -0700330 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700331 mRequestThreadManager.start();
332 }
333
334 /**
335 * Configure the device with a set of output surfaces.
336 *
Igor Murashkin49b2b132014-06-18 19:03:00 -0700337 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
338 *
339 * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
340 *
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700341 * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
342 * list; it must not be modified by the caller once it's passed in.
Igor Murashkin49b2b132014-06-18 19:03:00 -0700343 * @return an error code for this binder operation, or {@link NO_ERROR}
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700344 * on success.
345 */
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700346 public int configureOutputs(SparseArray<Surface> outputs) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800347 List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
Igor Murashkin49b2b132014-06-18 19:03:00 -0700348 if (outputs != null) {
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700349 int count = outputs.size();
350 for (int i = 0; i < count; i++) {
351 Surface output = outputs.valueAt(i);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700352 if (output == null) {
353 Log.e(TAG, "configureOutputs - null outputs are not allowed");
354 return BAD_VALUE;
355 }
Ruben Brunk443ab2c2015-03-12 20:54:03 -0700356 if (!output.isValid()) {
357 Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed");
358 return BAD_VALUE;
359 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700360 StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
361 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
362
363 // Validate surface size and format.
364 try {
365 Size s = getSurfaceSize(output);
366 int surfaceType = detectSurfaceType(output);
Ruben Brunke663cb772014-09-16 13:18:31 -0700367
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800368 boolean flexibleConsumer = isFlexibleConsumer(output);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800369
370 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
Ruben Brunke663cb772014-09-16 13:18:31 -0700371 if (sizes == null) {
Shuzhen Wang71e6d622016-12-07 14:46:30 -0800372 if (surfaceType == ImageFormat.PRIVATE) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700373
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800374 // YUV_420_888 is always present in LEGACY for all
375 // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
376 // API (i.e. {@code #getOutputSizes} works here).
Ruben Brunke663cb772014-09-16 13:18:31 -0700377 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
378 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
379 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
380 }
381 }
382
383 if (!ArrayUtils.contains(sizes, s)) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800384 if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
385 sizedSurfaces.add(new Pair<>(output, s));
386 } else {
387 String reason = (sizes == null) ? "format is invalid." :
388 ("size not in valid set: " + Arrays.toString(sizes));
389 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
390 "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
391 surfaceType, reason));
392 return BAD_VALUE;
393 }
394 } else {
395 sizedSurfaces.add(new Pair<>(output, s));
Ruben Brunke663cb772014-09-16 13:18:31 -0700396 }
Eino-Ville Talvala315bc092015-08-21 16:30:27 -0700397 // Lock down the size before configuration
398 setSurfaceDimens(output, s.getWidth(), s.getHeight());
Ruben Brunke663cb772014-09-16 13:18:31 -0700399 } catch (BufferQueueAbandonedException e) {
400 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
401 return BAD_VALUE;
402 }
403
Igor Murashkin49b2b132014-06-18 19:03:00 -0700404 }
405 }
406
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700407 boolean success = false;
408 if (mDeviceState.setConfiguring()) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800409 mRequestThreadManager.configure(sizedSurfaces);
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700410 success = mDeviceState.setIdle();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700411 }
Igor Murashkin49b2b132014-06-18 19:03:00 -0700412
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700413 if (success) {
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700414 mConfiguredSurfaces = outputs;
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700415 } else {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800416 return LegacyExceptionUtils.INVALID_OPERATION;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700417 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800418 return LegacyExceptionUtils.NO_ERROR;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700419 }
420
421 /**
422 * Submit a burst of capture requests.
423 *
424 * @param requestList a list of capture requests to execute.
425 * @param repeating {@code true} if this burst is repeating.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800426 * @return the submission info, including the new request id, and the last frame number, which
427 * contains either the frame number of the last frame that will be returned for this request,
428 * or the frame number of the last frame that will be returned for the current repeating
429 * request if this burst is set to be repeating.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700430 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800431 public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) {
432 if (requestList == null || requestList.length == 0) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700433 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800434 throw new ServiceSpecificException(BAD_VALUE,
435 "submitRequestList - Empty/null requests are not allowed");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700436 }
437
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700438 List<Long> surfaceIds;
439
440 try {
441 surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
442 getSurfaceIds(mConfiguredSurfaces);
443 } catch (BufferQueueAbandonedException e) {
444 throw new ServiceSpecificException(BAD_VALUE,
445 "submitRequestList - configured surface is abandoned.");
446 }
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700447
Igor Murashkin49b2b132014-06-18 19:03:00 -0700448 // Make sure that there all requests have at least 1 surface; all surfaces are non-null
449 for (CaptureRequest request : requestList) {
450 if (request.getTargets().isEmpty()) {
451 Log.e(TAG, "submitRequestList - "
452 + "Each request must have at least one Surface target");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800453 throw new ServiceSpecificException(BAD_VALUE,
454 "submitRequestList - "
455 + "Each request must have at least one Surface target");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700456 }
457
458 for (Surface surface : request.getTargets()) {
459 if (surface == null) {
460 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800461 throw new ServiceSpecificException(BAD_VALUE,
462 "submitRequestList - Null Surface targets are not allowed");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700463 } else if (mConfiguredSurfaces == null) {
464 Log.e(TAG, "submitRequestList - must configure " +
465 " device with valid surfaces before submitting requests");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800466 throw new ServiceSpecificException(INVALID_OPERATION,
467 "submitRequestList - must configure " +
468 " device with valid surfaces before submitting requests");
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700469 } else if (!containsSurfaceId(surface, surfaceIds)) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700470 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800471 throw new ServiceSpecificException(BAD_VALUE,
472 "submitRequestList - cannot use a surface that wasn't configured");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700473 }
474 }
475 }
476
477 // TODO: further validation of request here
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700478 mIdle.close();
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800479 return mRequestThreadManager.submitCaptureRequests(requestList, repeating);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700480 }
481
482 /**
483 * Submit a single capture request.
484 *
485 * @param request the capture request to execute.
486 * @param repeating {@code true} if this request is repeating.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800487 * @return the submission info, including the new request id, and the last frame number, which
488 * contains either the frame number of the last frame that will be returned for this request,
489 * or the frame number of the last frame that will be returned for the current repeating
490 * request if this burst is set to be repeating.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700491 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800492 public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) {
493 CaptureRequest[] requestList = { request };
494 return submitRequestList(requestList, repeating);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700495 }
496
497 /**
498 * Cancel the repeating request with the given request id.
499 *
500 * @param requestId the request id of the request to cancel.
501 * @return the last frame number to be returned from the HAL for the given repeating request, or
502 * {@code INVALID_FRAME} if none exists.
503 */
504 public long cancelRequest(int requestId) {
505 return mRequestThreadManager.cancelRepeating(requestId);
506 }
507
508 /**
509 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
510 */
511 public void waitUntilIdle() {
512 mIdle.block();
513 }
514
Ruben Brunke663cb772014-09-16 13:18:31 -0700515 /**
516 * Flush any pending requests.
517 *
518 * @return the last frame number.
519 */
520 public long flush() {
521 long lastFrame = mRequestThreadManager.flush();
522 waitUntilIdle();
523 return lastFrame;
524 }
525
526 /**
527 * Return {@code true} if the device has been closed.
528 */
529 public boolean isClosed() {
530 return mClosed;
531 }
532
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700533 @Override
534 public void close() {
535 mRequestThreadManager.quit();
536 mCallbackHandlerThread.quitSafely();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700537 mResultThread.quitSafely();
538
539 try {
540 mCallbackHandlerThread.join();
541 } catch (InterruptedException e) {
542 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
543 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
544 }
545
546 try {
547 mResultThread.join();
548 } catch (InterruptedException e) {
549 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
550 mResultThread.getName(), mResultThread.getId()));
551 }
552
Ruben Brunke663cb772014-09-16 13:18:31 -0700553 mClosed = true;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700554 }
555
556 @Override
557 protected void finalize() throws Throwable {
558 try {
559 close();
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800560 } catch (ServiceSpecificException e) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700561 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
562 } finally {
563 super.finalize();
564 }
565 }
566
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800567 static long findEuclidDistSquare(Size a, Size b) {
568 long d0 = a.getWidth() - b.getWidth();
569 long d1 = a.getHeight() - b.getHeight();
570 return d0 * d0 + d1 * d1;
571 }
572
573 // Keep up to date with rounding behavior in
574 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
575 static Size findClosestSize(Size size, Size[] supportedSizes) {
576 if (size == null || supportedSizes == null) {
577 return null;
578 }
579 Size bestSize = null;
580 for (Size s : supportedSizes) {
581 if (s.equals(size)) {
582 return size;
583 } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
584 LegacyCameraDevice.findEuclidDistSquare(size, s) <
585 LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
586 bestSize = s;
587 }
588 }
589 return bestSize;
590 }
591
Igor Murashkina296fec2014-06-23 14:44:09 -0700592 /**
593 * Query the surface for its currently configured default buffer size.
594 * @param surface a non-{@code null} {@code Surface}
595 * @return the width and height of the surface
596 *
597 * @throws NullPointerException if the {@code surface} was {@code null}
Ruben Brunk443ab2c2015-03-12 20:54:03 -0700598 * @throws BufferQueueAbandonedException if the {@code surface} was invalid
Igor Murashkina296fec2014-06-23 14:44:09 -0700599 */
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800600 public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
Igor Murashkina296fec2014-06-23 14:44:09 -0700601 checkNotNull(surface);
602
603 int[] dimens = new int[2];
Ruben Brunkef14da32014-06-24 16:06:54 -0700604 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
Igor Murashkina296fec2014-06-23 14:44:09 -0700605
606 return new Size(dimens[0], dimens[1]);
607 }
608
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800609 public static boolean isFlexibleConsumer(Surface output) {
610 int usageFlags = detectSurfaceUsageFlags(output);
611
612 // Keep up to date with allowed consumer types in
613 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
614 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
615 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
616 GRALLOC_USAGE_HW_COMPOSER;
617 boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
618 (usageFlags & allowedFlags) != 0);
619 return flexibleConsumer;
620 }
621
Zhijun Hea7677722015-06-01 16:36:06 -0700622 public static boolean isPreviewConsumer(Surface output) {
623 int usageFlags = detectSurfaceUsageFlags(output);
624 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
625 GRALLOC_USAGE_SW_READ_OFTEN;
626 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
627 GRALLOC_USAGE_HW_RENDER;
628 boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
629 (usageFlags & allowedFlags) != 0);
630 int surfaceFormat = ImageFormat.UNKNOWN;
631 try {
632 surfaceFormat = detectSurfaceType(output);
633 } catch(BufferQueueAbandonedException e) {
634 throw new IllegalArgumentException("Surface was abandoned", e);
635 }
636
Zhijun He47ac3492015-06-10 14:41:57 -0700637 return previewConsumer;
Zhijun Hea7677722015-06-01 16:36:06 -0700638 }
639
640 public static boolean isVideoEncoderConsumer(Surface output) {
641 int usageFlags = detectSurfaceUsageFlags(output);
642 int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
643 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
644 int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
645 boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
646 (usageFlags & allowedFlags) != 0);
647
648 int surfaceFormat = ImageFormat.UNKNOWN;
649 try {
650 surfaceFormat = detectSurfaceType(output);
651 } catch(BufferQueueAbandonedException e) {
652 throw new IllegalArgumentException("Surface was abandoned", e);
653 }
654
Zhijun He47ac3492015-06-10 14:41:57 -0700655 return videoEncoderConsumer;
Zhijun Hea7677722015-06-01 16:36:06 -0700656 }
657
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800658 /**
659 * Query the surface for its currently configured usage flags
660 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800661 static int detectSurfaceUsageFlags(Surface surface) {
662 checkNotNull(surface);
663 return nativeDetectSurfaceUsageFlags(surface);
664 }
665
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800666 /**
667 * Query the surface for its currently configured format
668 */
669 public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
Ruben Brunkef14da32014-06-24 16:06:54 -0700670 checkNotNull(surface);
Shuzhen Wang71e6d622016-12-07 14:46:30 -0800671 int surfaceType = nativeDetectSurfaceType(surface);
672
673 // TODO: remove this override since the default format should be
674 // ImageFormat.PRIVATE. b/9487482
675 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
676 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
677 surfaceType = ImageFormat.PRIVATE;
678 }
679
680 return LegacyExceptionUtils.throwOnError(surfaceType);
Ruben Brunkef14da32014-06-24 16:06:54 -0700681 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700682
Eino-Ville Talvalae3651202015-06-19 17:29:14 -0700683 /**
684 * Query the surface for its currently configured dataspace
685 */
686 public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException {
687 checkNotNull(surface);
688 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
689 }
690
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800691 static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
Ruben Brunkef14da32014-06-24 16:06:54 -0700692 checkNotNull(surface);
Ruben Brunkef14da32014-06-24 16:06:54 -0700693
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800694 LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
695 }
696
697 static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
698 if (surface == null) return;
699
700 LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
Ruben Brunkef14da32014-06-24 16:06:54 -0700701 }
702
703 static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
704 int height, int pixelFormat)
705 throws BufferQueueAbandonedException {
706 checkNotNull(surface);
707 checkNotNull(pixelBuffer);
708 checkArgumentPositive(width, "width must be positive.");
709 checkArgumentPositive(height, "height must be positive.");
710
711 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
712 pixelFormat));
713 }
714
715 static void setSurfaceFormat(Surface surface, int pixelFormat)
716 throws BufferQueueAbandonedException {
717 checkNotNull(surface);
718
719 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
720 }
721
722 static void setSurfaceDimens(Surface surface, int width, int height)
723 throws BufferQueueAbandonedException {
724 checkNotNull(surface);
725 checkArgumentPositive(width, "width must be positive.");
726 checkArgumentPositive(height, "height must be positive.");
727
728 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
729 }
730
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700731 static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700732 checkNotNull(surface);
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700733 try {
734 return nativeGetSurfaceId(surface);
735 } catch (IllegalArgumentException e) {
736 throw new BufferQueueAbandonedException();
737 }
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700738 }
739
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700740 static List<Long> getSurfaceIds(SparseArray<Surface> surfaces)
741 throws BufferQueueAbandonedException {
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700742 if (surfaces == null) {
743 throw new NullPointerException("Null argument surfaces");
744 }
745 List<Long> surfaceIds = new ArrayList<>();
746 int count = surfaces.size();
747 for (int i = 0; i < count; i++) {
748 long id = getSurfaceId(surfaces.valueAt(i));
749 if (id == 0) {
750 throw new IllegalStateException(
751 "Configured surface had null native GraphicBufferProducer pointer!");
752 }
753 surfaceIds.add(id);
754 }
755 return surfaceIds;
756 }
757
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700758 static List<Long> getSurfaceIds(Collection<Surface> surfaces)
759 throws BufferQueueAbandonedException {
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700760 if (surfaces == null) {
761 throw new NullPointerException("Null argument surfaces");
762 }
763 List<Long> surfaceIds = new ArrayList<>();
764 for (Surface s : surfaces) {
765 long id = getSurfaceId(s);
766 if (id == 0) {
767 throw new IllegalStateException(
768 "Configured surface had null native GraphicBufferProducer pointer!");
769 }
770 surfaceIds.add(id);
771 }
772 return surfaceIds;
773 }
774
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700775 static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700776 long id = 0;
777 try {
778 id = getSurfaceId(s);
779 } catch (BufferQueueAbandonedException e) {
780 // If surface is abandoned, return false.
781 return false;
782 }
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700783 return ids.contains(id);
784 }
785
Ruben Brunk28c49c92014-06-16 18:43:59 -0700786 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
787 throws BufferQueueAbandonedException {
788 checkNotNull(surface);
789 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
790 sensorOrientation));
791 }
792
793 static Size getTextureSize(SurfaceTexture surfaceTexture)
794 throws BufferQueueAbandonedException {
795 checkNotNull(surfaceTexture);
796
797 int[] dimens = new int[2];
798 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
799 /*out*/dimens));
800
801 return new Size(dimens[0], dimens[1]);
802 }
803
Ruben Brunk91838de2014-07-16 17:24:17 -0700804 static void setNextTimestamp(Surface surface, long timestamp)
805 throws BufferQueueAbandonedException {
806 checkNotNull(surface);
807 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
808 }
809
Ruben Brunka94c6032015-06-10 16:44:28 -0700810 static void setScalingMode(Surface surface, int mode)
811 throws BufferQueueAbandonedException {
812 checkNotNull(surface);
813 LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode));
814 }
815
816
Ruben Brunkef14da32014-06-24 16:06:54 -0700817 private static native int nativeDetectSurfaceType(Surface surface);
818
Eino-Ville Talvalae3651202015-06-19 17:29:14 -0700819 private static native int nativeDetectSurfaceDataspace(Surface surface);
820
Ruben Brunkef14da32014-06-24 16:06:54 -0700821 private static native int nativeDetectSurfaceDimens(Surface surface,
Igor Murashkina296fec2014-06-23 14:44:09 -0700822 /*out*/int[/*2*/] dimens);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700823
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800824 private static native int nativeConnectSurface(Surface surface);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700825
Ruben Brunkef14da32014-06-24 16:06:54 -0700826 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700827 int height, int pixelFormat);
828
Ruben Brunkef14da32014-06-24 16:06:54 -0700829 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700830
Ruben Brunkef14da32014-06-24 16:06:54 -0700831 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700832
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700833 private static native long nativeGetSurfaceId(Surface surface);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700834
835 private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
836 int sensorOrientation);
837
838 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
839 /*out*/int[/*2*/] dimens);
840
Ruben Brunk91838de2014-07-16 17:24:17 -0700841 private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
Ruben Brunk1dc13262014-07-31 11:43:27 -0700842
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800843 private static native int nativeDetectSurfaceUsageFlags(Surface surface);
844
Ruben Brunka94c6032015-06-10 16:44:28 -0700845 private static native int nativeSetScalingMode(Surface surface, int scalingMode);
846
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800847 private static native int nativeDisconnectSurface(Surface surface);
848
Ruben Brunk1dc13262014-07-31 11:43:27 -0700849 static native int nativeGetJpegFooterSize();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700850}