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