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.hardware.ICameraService;
20 import android.hardware.Camera;
21 import android.hardware.Camera.CameraInfo;
22 import android.hardware.camera2.CameraAccessException;
23 import android.hardware.camera2.CameraCharacteristics;
24 import android.hardware.camera2.CaptureRequest;
25 import android.hardware.camera2.ICameraDeviceCallbacks;
26 import android.hardware.camera2.ICameraDeviceUser;
27 import android.hardware.camera2.impl.CameraMetadataNative;
28 import android.hardware.camera2.impl.CaptureResultExtras;
29 import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
30 import android.hardware.camera2.params.OutputConfiguration;
31 import android.hardware.camera2.utils.SubmitInfo;
32 import android.os.ConditionVariable;
33 import android.os.IBinder;
34 import android.os.Looper;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.Message;
38 import android.os.RemoteException;
39 import android.os.ServiceSpecificException;
40 import android.util.Log;
41 import android.util.SparseArray;
42 import android.view.Surface;
43 
44 import java.util.ArrayList;
45 import java.util.List;
46 
47 import static android.system.OsConstants.EACCES;
48 import static android.system.OsConstants.ENODEV;
49 
50 /**
51  * Compatibility implementation of the Camera2 API binder interface.
52  *
53  * <p>
54  * This is intended to be called from the same process as client
55  * {@link android.hardware.camera2.CameraDevice}, and wraps a
56  * {@link android.hardware.camera2.legacy.LegacyCameraDevice} that emulates Camera2 service using
57  * the Camera1 API.
58  * </p>
59  *
60  * <p>
61  * Keep up to date with ICameraDeviceUser.aidl.
62  * </p>
63  */
64 @SuppressWarnings("deprecation")
65 public class CameraDeviceUserShim implements ICameraDeviceUser {
66     private static final String TAG = "CameraDeviceUserShim";
67 
68     private static final boolean DEBUG = false;
69     private static final int OPEN_CAMERA_TIMEOUT_MS = 5000; // 5 sec (same as api1 cts timeout)
70 
71     private final LegacyCameraDevice mLegacyDevice;
72 
73     private final Object mConfigureLock = new Object();
74     private int mSurfaceIdCounter;
75     private boolean mConfiguring;
76     private final SparseArray<Surface> mSurfaces;
77     private final CameraCharacteristics mCameraCharacteristics;
78     private final CameraLooper mCameraInit;
79     private final CameraCallbackThread mCameraCallbacks;
80 
81 
CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera, CameraCharacteristics characteristics, CameraLooper cameraInit, CameraCallbackThread cameraCallbacks)82     protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera,
83             CameraCharacteristics characteristics, CameraLooper cameraInit,
84             CameraCallbackThread cameraCallbacks) {
85         mLegacyDevice = legacyCamera;
86         mConfiguring = false;
87         mSurfaces = new SparseArray<Surface>();
88         mCameraCharacteristics = characteristics;
89         mCameraInit = cameraInit;
90         mCameraCallbacks = cameraCallbacks;
91 
92         mSurfaceIdCounter = 0;
93     }
94 
translateErrorsFromCamera1(int errorCode)95     private static int translateErrorsFromCamera1(int errorCode) {
96         if (errorCode == -EACCES) {
97             return ICameraService.ERROR_PERMISSION_DENIED;
98         }
99 
100         return errorCode;
101     }
102 
103     /**
104      * Create a separate looper/thread for the camera to run on; open the camera.
105      *
106      * <p>Since the camera automatically latches on to the current thread's looper,
107      * it's important that we have our own thread with our own looper to guarantee
108      * that the camera callbacks get correctly posted to our own thread.</p>
109      */
110     private static class CameraLooper implements Runnable, AutoCloseable {
111         private final int mCameraId;
112         private Looper mLooper;
113         private volatile int mInitErrors;
114         private final Camera mCamera = Camera.openUninitialized();
115         private final ConditionVariable mStartDone = new ConditionVariable();
116         private final Thread mThread;
117 
118         /**
119          * Spin up a new thread, immediately open the camera in the background.
120          *
121          * <p>Use {@link #waitForOpen} to block until the camera is finished opening.</p>
122          *
123          * @param cameraId numeric camera Id
124          *
125          * @see #waitForOpen
126          */
CameraLooper(int cameraId)127         public CameraLooper(int cameraId) {
128             mCameraId = cameraId;
129 
130             mThread = new Thread(this);
131             mThread.start();
132         }
133 
getCamera()134         public Camera getCamera() {
135             return mCamera;
136         }
137 
138         @Override
run()139         public void run() {
140             // Set up a looper to be used by camera.
141             Looper.prepare();
142 
143             // Save the looper so that we can terminate this thread
144             // after we are done with it.
145             mLooper = Looper.myLooper();
146             mInitErrors = mCamera.cameraInitUnspecified(mCameraId);
147             mStartDone.open();
148             Looper.loop();  // Blocks forever until #close is called.
149         }
150 
151         /**
152          * Quit the looper safely; then join until the thread shuts down.
153          */
154         @Override
close()155         public void close() {
156             if (mLooper == null) {
157                 return;
158             }
159 
160             mLooper.quitSafely();
161             try {
162                 mThread.join();
163             } catch (InterruptedException e) {
164                 throw new AssertionError(e);
165             }
166 
167             mLooper = null;
168         }
169 
170         /**
171          * Block until the camera opens; then return its initialization error code (if any).
172          *
173          * @param timeoutMs timeout in milliseconds
174          *
175          * @return int error code
176          *
177          * @throws ServiceSpecificException if the camera open times out with ({@code CAMERA_ERROR})
178          */
waitForOpen(int timeoutMs)179         public int waitForOpen(int timeoutMs) {
180             // Block until the camera is open asynchronously
181             if (!mStartDone.block(timeoutMs)) {
182                 Log.e(TAG, "waitForOpen - Camera failed to open after timeout of "
183                         + OPEN_CAMERA_TIMEOUT_MS + " ms");
184                 try {
185                     mCamera.release();
186                 } catch (RuntimeException e) {
187                     Log.e(TAG, "connectBinderShim - Failed to release camera after timeout ", e);
188                 }
189 
190                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION);
191             }
192 
193             return mInitErrors;
194         }
195     }
196 
197     /**
198      * A thread to process callbacks to send back to the camera client.
199      *
200      * <p>This effectively emulates one-way binder semantics when in the same process as the
201      * callee.</p>
202      */
203     private static class CameraCallbackThread implements ICameraDeviceCallbacks {
204         private static final int CAMERA_ERROR = 0;
205         private static final int CAMERA_IDLE = 1;
206         private static final int CAPTURE_STARTED = 2;
207         private static final int RESULT_RECEIVED = 3;
208         private static final int PREPARED = 4;
209         private static final int REPEATING_REQUEST_ERROR = 5;
210         private static final int REQUEST_QUEUE_EMPTY = 6;
211 
212         private final HandlerThread mHandlerThread;
213         private Handler mHandler;
214 
215         private final ICameraDeviceCallbacks mCallbacks;
216 
CameraCallbackThread(ICameraDeviceCallbacks callbacks)217         public CameraCallbackThread(ICameraDeviceCallbacks callbacks) {
218             mCallbacks = callbacks;
219 
220             mHandlerThread = new HandlerThread("LegacyCameraCallback");
221             mHandlerThread.start();
222         }
223 
close()224         public void close() {
225             mHandlerThread.quitSafely();
226         }
227 
228         @Override
onDeviceError(final int errorCode, final CaptureResultExtras resultExtras)229         public void onDeviceError(final int errorCode, final CaptureResultExtras resultExtras) {
230             Message msg = getHandler().obtainMessage(CAMERA_ERROR,
231                 /*arg1*/ errorCode, /*arg2*/ 0,
232                 /*obj*/ resultExtras);
233             getHandler().sendMessage(msg);
234         }
235 
236         @Override
onDeviceIdle()237         public void onDeviceIdle() {
238             Message msg = getHandler().obtainMessage(CAMERA_IDLE);
239             getHandler().sendMessage(msg);
240         }
241 
242         @Override
onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)243         public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
244             Message msg = getHandler().obtainMessage(CAPTURE_STARTED,
245                     /*arg1*/ (int) (timestamp & 0xFFFFFFFFL),
246                     /*arg2*/ (int) ( (timestamp >> 32) & 0xFFFFFFFFL),
247                     /*obj*/ resultExtras);
248             getHandler().sendMessage(msg);
249         }
250 
251         @Override
onResultReceived(final CameraMetadataNative result, final CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])252         public void onResultReceived(final CameraMetadataNative result,
253                 final CaptureResultExtras resultExtras,
254                 PhysicalCaptureResultInfo physicalResults[]) {
255             Object[] resultArray = new Object[] { result, resultExtras };
256             Message msg = getHandler().obtainMessage(RESULT_RECEIVED,
257                     /*obj*/ resultArray);
258             getHandler().sendMessage(msg);
259         }
260 
261         @Override
onPrepared(int streamId)262         public void onPrepared(int streamId) {
263             Message msg = getHandler().obtainMessage(PREPARED,
264                     /*arg1*/ streamId, /*arg2*/ 0);
265             getHandler().sendMessage(msg);
266         }
267 
268         @Override
onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId)269         public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
270             Object[] objArray = new Object[] { lastFrameNumber, repeatingRequestId };
271             Message msg = getHandler().obtainMessage(REPEATING_REQUEST_ERROR,
272                     /*obj*/ objArray);
273             getHandler().sendMessage(msg);
274         }
275 
276         @Override
onRequestQueueEmpty()277         public void onRequestQueueEmpty() {
278             Message msg = getHandler().obtainMessage(REQUEST_QUEUE_EMPTY,
279                     /* arg1 */ 0, /* arg2 */ 0);
280             getHandler().sendMessage(msg);
281         }
282 
283         @Override
asBinder()284         public IBinder asBinder() {
285             // This is solely intended to be used for in-process binding.
286             return null;
287         }
288 
getHandler()289         private Handler getHandler() {
290             if (mHandler == null) {
291                 mHandler = new CallbackHandler(mHandlerThread.getLooper());
292             }
293             return mHandler;
294         }
295 
296         private class CallbackHandler extends Handler {
CallbackHandler(Looper l)297             public CallbackHandler(Looper l) {
298                 super(l);
299             }
300 
301             @Override
handleMessage(Message msg)302             public void handleMessage(Message msg) {
303                 try {
304                     switch (msg.what) {
305                         case CAMERA_ERROR: {
306                             int errorCode = msg.arg1;
307                             CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj;
308                             mCallbacks.onDeviceError(errorCode, resultExtras);
309                             break;
310                         }
311                         case CAMERA_IDLE:
312                             mCallbacks.onDeviceIdle();
313                             break;
314                         case CAPTURE_STARTED: {
315                             long timestamp = msg.arg2 & 0xFFFFFFFFL;
316                             timestamp = (timestamp << 32) | (msg.arg1 & 0xFFFFFFFFL);
317                             CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj;
318                             mCallbacks.onCaptureStarted(resultExtras, timestamp);
319                             break;
320                         }
321                         case RESULT_RECEIVED: {
322                             Object[] resultArray = (Object[]) msg.obj;
323                             CameraMetadataNative result = (CameraMetadataNative) resultArray[0];
324                             CaptureResultExtras resultExtras = (CaptureResultExtras) resultArray[1];
325                             mCallbacks.onResultReceived(result, resultExtras,
326                                     new PhysicalCaptureResultInfo[0]);
327                             break;
328                         }
329                         case PREPARED: {
330                             int streamId = msg.arg1;
331                             mCallbacks.onPrepared(streamId);
332                             break;
333                         }
334                         case REPEATING_REQUEST_ERROR: {
335                             Object[] objArray = (Object[]) msg.obj;
336                             long lastFrameNumber = (Long) objArray[0];
337                             int repeatingRequestId = (Integer) objArray[1];
338                             mCallbacks.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
339                             break;
340                         }
341                         case REQUEST_QUEUE_EMPTY: {
342                             mCallbacks.onRequestQueueEmpty();
343                             break;
344                         }
345                         default:
346                             throw new IllegalArgumentException(
347                                 "Unknown callback message " + msg.what);
348                     }
349                 } catch (RemoteException e) {
350                     throw new IllegalStateException(
351                         "Received remote exception during camera callback " + msg.what, e);
352                 }
353             }
354         }
355     }
356 
connectBinderShim(ICameraDeviceCallbacks callbacks, int cameraId)357     public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
358                                                          int cameraId) {
359         if (DEBUG) {
360             Log.d(TAG, "Opening shim Camera device");
361         }
362 
363         /*
364          * Put the camera open on a separate thread with its own looper; otherwise
365          * if the main thread is used then the callbacks might never get delivered
366          * (e.g. in CTS which run its own default looper only after tests)
367          */
368 
369         CameraLooper init = new CameraLooper(cameraId);
370 
371         CameraCallbackThread threadCallbacks = new CameraCallbackThread(callbacks);
372 
373         // TODO: Make this async instead of blocking
374         int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS);
375         Camera legacyCamera = init.getCamera();
376 
377         // Check errors old HAL initialization
378         LegacyExceptionUtils.throwOnServiceError(initErrors);
379 
380         // Disable shutter sounds (this will work unconditionally) for api2 clients
381         legacyCamera.disableShutterSound();
382 
383         CameraInfo info = new CameraInfo();
384         Camera.getCameraInfo(cameraId, info);
385 
386         Camera.Parameters legacyParameters = null;
387         try {
388             legacyParameters = legacyCamera.getParameters();
389         } catch (RuntimeException e) {
390             throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION,
391                     "Unable to get initial parameters: " + e.getMessage());
392         }
393 
394         CameraCharacteristics characteristics =
395                 LegacyMetadataMapper.createCharacteristics(legacyParameters, info);
396         LegacyCameraDevice device = new LegacyCameraDevice(
397                 cameraId, legacyCamera, characteristics, threadCallbacks);
398         return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks);
399     }
400 
401     @Override
disconnect()402     public void disconnect() {
403         if (DEBUG) {
404             Log.d(TAG, "disconnect called.");
405         }
406 
407         if (mLegacyDevice.isClosed()) {
408             Log.w(TAG, "Cannot disconnect, device has already been closed.");
409         }
410 
411         try {
412             mLegacyDevice.close();
413         } finally {
414             mCameraInit.close();
415             mCameraCallbacks.close();
416         }
417     }
418 
419     @Override
submitRequest(CaptureRequest request, boolean streaming)420     public SubmitInfo submitRequest(CaptureRequest request, boolean streaming) {
421         if (DEBUG) {
422             Log.d(TAG, "submitRequest called.");
423         }
424         if (mLegacyDevice.isClosed()) {
425             String err = "Cannot submit request, device has been closed.";
426             Log.e(TAG, err);
427             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
428         }
429 
430         synchronized(mConfigureLock) {
431             if (mConfiguring) {
432                 String err = "Cannot submit request, configuration change in progress.";
433                 Log.e(TAG, err);
434                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
435             }
436         }
437         return mLegacyDevice.submitRequest(request, streaming);
438     }
439 
440     @Override
submitRequestList(CaptureRequest[] request, boolean streaming)441     public SubmitInfo submitRequestList(CaptureRequest[] request, boolean streaming) {
442         if (DEBUG) {
443             Log.d(TAG, "submitRequestList called.");
444         }
445         if (mLegacyDevice.isClosed()) {
446             String err = "Cannot submit request list, device has been closed.";
447             Log.e(TAG, err);
448             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
449         }
450 
451         synchronized(mConfigureLock) {
452             if (mConfiguring) {
453                 String err = "Cannot submit request, configuration change in progress.";
454                 Log.e(TAG, err);
455                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
456             }
457         }
458         return mLegacyDevice.submitRequestList(request, streaming);
459     }
460 
461     @Override
cancelRequest(int requestId)462     public long cancelRequest(int requestId) {
463         if (DEBUG) {
464             Log.d(TAG, "cancelRequest called.");
465         }
466         if (mLegacyDevice.isClosed()) {
467             String err = "Cannot cancel request, device has been closed.";
468             Log.e(TAG, err);
469             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
470         }
471 
472         synchronized(mConfigureLock) {
473             if (mConfiguring) {
474                 String err = "Cannot cancel request, configuration change in progress.";
475                 Log.e(TAG, err);
476                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
477             }
478         }
479         return mLegacyDevice.cancelRequest(requestId);
480     }
481 
482     @Override
beginConfigure()483     public void beginConfigure() {
484         if (DEBUG) {
485             Log.d(TAG, "beginConfigure called.");
486         }
487         if (mLegacyDevice.isClosed()) {
488             String err = "Cannot begin configure, device has been closed.";
489             Log.e(TAG, err);
490             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
491         }
492 
493         synchronized(mConfigureLock) {
494             if (mConfiguring) {
495                 String err = "Cannot begin configure, configuration change already in progress.";
496                 Log.e(TAG, err);
497                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
498             }
499             mConfiguring = true;
500         }
501     }
502 
503     @Override
endConfigure(int operatingMode, CameraMetadataNative sessionParams)504     public void endConfigure(int operatingMode, CameraMetadataNative sessionParams) {
505         if (DEBUG) {
506             Log.d(TAG, "endConfigure called.");
507         }
508         if (mLegacyDevice.isClosed()) {
509             String err = "Cannot end configure, device has been closed.";
510             Log.e(TAG, err);
511             synchronized(mConfigureLock) {
512                 mConfiguring = false;
513             }
514             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
515         }
516 
517         if (operatingMode != ICameraDeviceUser.NORMAL_MODE) {
518             String err = "LEGACY devices do not support this operating mode";
519             Log.e(TAG, err);
520             synchronized(mConfigureLock) {
521                 mConfiguring = false;
522             }
523             throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
524         }
525 
526         SparseArray<Surface> surfaces = null;
527         synchronized(mConfigureLock) {
528             if (!mConfiguring) {
529                 String err = "Cannot end configure, no configuration change in progress.";
530                 Log.e(TAG, err);
531                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
532             }
533             if (mSurfaces != null) {
534                 surfaces = mSurfaces.clone();
535             }
536             mConfiguring = false;
537         }
538         mLegacyDevice.configureOutputs(surfaces);
539     }
540 
541     @Override
deleteStream(int streamId)542     public void deleteStream(int streamId) {
543         if (DEBUG) {
544             Log.d(TAG, "deleteStream called.");
545         }
546         if (mLegacyDevice.isClosed()) {
547             String err = "Cannot delete stream, device has been closed.";
548             Log.e(TAG, err);
549             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
550         }
551 
552         synchronized(mConfigureLock) {
553             if (!mConfiguring) {
554                 String err = "Cannot delete stream, no configuration change in progress.";
555                 Log.e(TAG, err);
556                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
557             }
558             int index = mSurfaces.indexOfKey(streamId);
559             if (index < 0) {
560                 String err = "Cannot delete stream, stream id " + streamId + " doesn't exist.";
561                 Log.e(TAG, err);
562                 throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
563             }
564             mSurfaces.removeAt(index);
565         }
566     }
567 
568     @Override
createStream(OutputConfiguration outputConfiguration)569     public int createStream(OutputConfiguration outputConfiguration) {
570         if (DEBUG) {
571             Log.d(TAG, "createStream called.");
572         }
573         if (mLegacyDevice.isClosed()) {
574             String err = "Cannot create stream, device has been closed.";
575             Log.e(TAG, err);
576             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
577         }
578 
579         synchronized(mConfigureLock) {
580             if (!mConfiguring) {
581                 String err = "Cannot create stream, beginConfigure hasn't been called yet.";
582                 Log.e(TAG, err);
583                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
584             }
585             if (outputConfiguration.getRotation() != OutputConfiguration.ROTATION_0) {
586                 String err = "Cannot create stream, stream rotation is not supported.";
587                 Log.e(TAG, err);
588                 throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
589             }
590             int id = ++mSurfaceIdCounter;
591             mSurfaces.put(id, outputConfiguration.getSurface());
592             return id;
593         }
594     }
595 
596     @Override
finalizeOutputConfigurations(int steamId, OutputConfiguration config)597     public void finalizeOutputConfigurations(int steamId, OutputConfiguration config) {
598         String err = "Finalizing output configuration is not supported on legacy devices";
599         Log.e(TAG, err);
600         throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
601     }
602 
603     @Override
createInputStream(int width, int height, int format)604     public int createInputStream(int width, int height, int format) {
605         String err = "Creating input stream is not supported on legacy devices";
606         Log.e(TAG, err);
607         throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
608     }
609 
610     @Override
getInputSurface()611     public Surface getInputSurface() {
612         String err = "Getting input surface is not supported on legacy devices";
613         Log.e(TAG, err);
614         throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
615     }
616 
617     @Override
createDefaultRequest(int templateId)618     public CameraMetadataNative createDefaultRequest(int templateId) {
619         if (DEBUG) {
620             Log.d(TAG, "createDefaultRequest called.");
621         }
622         if (mLegacyDevice.isClosed()) {
623             String err = "Cannot create default request, device has been closed.";
624             Log.e(TAG, err);
625             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
626         }
627 
628         CameraMetadataNative template;
629         try {
630             template =
631                     LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId);
632         } catch (IllegalArgumentException e) {
633             String err = "createDefaultRequest - invalid templateId specified";
634             Log.e(TAG, err);
635             throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
636         }
637 
638         return template;
639     }
640 
641     @Override
getCameraInfo()642     public CameraMetadataNative getCameraInfo() {
643         if (DEBUG) {
644             Log.d(TAG, "getCameraInfo called.");
645         }
646         // TODO: implement getCameraInfo.
647         Log.e(TAG, "getCameraInfo unimplemented.");
648         return null;
649     }
650 
651     @Override
updateOutputConfiguration(int streamId, OutputConfiguration config)652     public void updateOutputConfiguration(int streamId, OutputConfiguration config) {
653         // TODO: b/63912484 implement updateOutputConfiguration.
654     }
655 
656     @Override
waitUntilIdle()657     public void waitUntilIdle() throws RemoteException {
658         if (DEBUG) {
659             Log.d(TAG, "waitUntilIdle called.");
660         }
661         if (mLegacyDevice.isClosed()) {
662             String err = "Cannot wait until idle, device has been closed.";
663             Log.e(TAG, err);
664             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
665         }
666 
667         synchronized(mConfigureLock) {
668             if (mConfiguring) {
669                 String err = "Cannot wait until idle, configuration change in progress.";
670                 Log.e(TAG, err);
671                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
672             }
673         }
674         mLegacyDevice.waitUntilIdle();
675     }
676 
677     @Override
flush()678     public long flush() {
679         if (DEBUG) {
680             Log.d(TAG, "flush called.");
681         }
682         if (mLegacyDevice.isClosed()) {
683             String err = "Cannot flush, device has been closed.";
684             Log.e(TAG, err);
685             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
686         }
687 
688         synchronized(mConfigureLock) {
689             if (mConfiguring) {
690                 String err = "Cannot flush, configuration change in progress.";
691                 Log.e(TAG, err);
692                 throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
693             }
694         }
695         return mLegacyDevice.flush();
696     }
697 
prepare(int streamId)698     public void prepare(int streamId) {
699         if (DEBUG) {
700             Log.d(TAG, "prepare called.");
701         }
702         if (mLegacyDevice.isClosed()) {
703             String err = "Cannot prepare stream, device has been closed.";
704             Log.e(TAG, err);
705             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
706         }
707 
708         // LEGACY doesn't support actual prepare, just signal success right away
709         mCameraCallbacks.onPrepared(streamId);
710     }
711 
prepare2(int maxCount, int streamId)712     public void prepare2(int maxCount, int streamId) {
713         // We don't support this in LEGACY mode.
714         prepare(streamId);
715     }
716 
tearDown(int streamId)717     public void tearDown(int streamId) {
718         if (DEBUG) {
719             Log.d(TAG, "tearDown called.");
720         }
721         if (mLegacyDevice.isClosed()) {
722             String err = "Cannot tear down stream, device has been closed.";
723             Log.e(TAG, err);
724             throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
725         }
726 
727         // LEGACY doesn't support actual teardown, so just a no-op
728     }
729 
730     @Override
asBinder()731     public IBinder asBinder() {
732         // This is solely intended to be used for in-process binding.
733         return null;
734     }
735 }
736