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
17package android.hardware.camera2.legacy;
18
19import android.hardware.ICameraService;
20import android.hardware.Camera;
21import android.hardware.Camera.CameraInfo;
22import android.hardware.camera2.CameraAccessException;
23import android.hardware.camera2.CameraCharacteristics;
24import android.hardware.camera2.CaptureRequest;
25import android.hardware.camera2.ICameraDeviceCallbacks;
26import android.hardware.camera2.ICameraDeviceUser;
27import android.hardware.camera2.impl.CameraMetadataNative;
28import android.hardware.camera2.impl.CaptureResultExtras;
29import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
30import android.hardware.camera2.params.OutputConfiguration;
31import android.hardware.camera2.utils.SubmitInfo;
32import android.os.ConditionVariable;
33import android.os.IBinder;
34import android.os.Looper;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.Message;
38import android.os.RemoteException;
39import android.os.ServiceSpecificException;
40import android.util.Log;
41import android.util.SparseArray;
42import android.view.Surface;
43
44import java.util.ArrayList;
45import java.util.List;
46
47import static android.system.OsConstants.EACCES;
48import 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")
65public 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
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
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         */
127        public CameraLooper(int cameraId) {
128            mCameraId = cameraId;
129
130            mThread = new Thread(this);
131            mThread.start();
132        }
133
134        public Camera getCamera() {
135            return mCamera;
136        }
137
138        @Override
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
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         */
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
217        public CameraCallbackThread(ICameraDeviceCallbacks callbacks) {
218            mCallbacks = callbacks;
219
220            mHandlerThread = new HandlerThread("LegacyCameraCallback");
221            mHandlerThread.start();
222        }
223
224        public void close() {
225            mHandlerThread.quitSafely();
226        }
227
228        @Override
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
237        public void onDeviceIdle() {
238            Message msg = getHandler().obtainMessage(CAMERA_IDLE);
239            getHandler().sendMessage(msg);
240        }
241
242        @Override
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
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
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
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
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
284        public IBinder asBinder() {
285            // This is solely intended to be used for in-process binding.
286            return null;
287        }
288
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 {
297            public CallbackHandler(Looper l) {
298                super(l);
299            }
300
301            @Override
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
652    public void updateOutputConfiguration(int streamId, OutputConfiguration config) {
653        // TODO: b/63912484 implement updateOutputConfiguration.
654    }
655
656    @Override
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
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
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
712    public void prepare2(int maxCount, int streamId) {
713        // We don't support this in LEGACY mode.
714        prepare(streamId);
715    }
716
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
731    public IBinder asBinder() {
732        // This is solely intended to be used for in-process binding.
733        return null;
734    }
735}
736