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