Home | History | Annotate | Download | only in camera2
      1 /*
      2  * Copyright (C) 2013 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;
     18 
     19 import android.content.Context;
     20 import android.hardware.ICameraService;
     21 import android.hardware.ICameraServiceListener;
     22 import android.hardware.CameraInfo;
     23 import android.hardware.camera2.impl.CameraMetadataNative;
     24 import android.hardware.camera2.legacy.CameraDeviceUserShim;
     25 import android.hardware.camera2.legacy.LegacyMetadataMapper;
     26 import android.hardware.camera2.utils.CameraServiceBinderDecorator;
     27 import android.hardware.camera2.utils.CameraRuntimeException;
     28 import android.hardware.camera2.utils.BinderHolder;
     29 import android.os.IBinder;
     30 import android.os.Handler;
     31 import android.os.Looper;
     32 import android.os.RemoteException;
     33 import android.os.ServiceManager;
     34 import android.util.Log;
     35 import android.util.ArrayMap;
     36 
     37 import java.util.ArrayList;
     38 
     39 /**
     40  * <p>A system service manager for detecting, characterizing, and connecting to
     41  * {@link CameraDevice CameraDevices}.</p>
     42  *
     43  * <p>You can get an instance of this class by calling
     44  * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p>
     45  *
     46  * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre>
     47  *
     48  * <p>For more details about communicating with camera devices, read the Camera
     49  * developer guide or the {@link android.hardware.camera2 camera2}
     50  * package documentation.</p>
     51  */
     52 public final class CameraManager {
     53 
     54     private static final String TAG = "CameraManager";
     55     private final boolean DEBUG;
     56 
     57     private static final int USE_CALLING_UID = -1;
     58 
     59     @SuppressWarnings("unused")
     60     private static final int API_VERSION_1 = 1;
     61     private static final int API_VERSION_2 = 2;
     62 
     63     private ArrayList<String> mDeviceIdList;
     64 
     65     private final Context mContext;
     66     private final Object mLock = new Object();
     67 
     68     /**
     69      * @hide
     70      */
     71     public CameraManager(Context context) {
     72         DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     73         synchronized(mLock) {
     74             mContext = context;
     75         }
     76     }
     77 
     78     /**
     79      * Return the list of currently connected camera devices by
     80      * identifier.
     81      *
     82      * <p>Non-removable cameras use integers starting at 0 for their
     83      * identifiers, while removable cameras have a unique identifier for each
     84      * individual device, even if they are the same model.</p>
     85      *
     86      * @return The list of currently connected camera devices.
     87      */
     88     public String[] getCameraIdList() throws CameraAccessException {
     89         synchronized (mLock) {
     90             // ID list creation handles various known failures in device enumeration, so only
     91             // exceptions it'll throw are unexpected, and should be propagated upward.
     92             return getOrCreateDeviceIdListLocked().toArray(new String[0]);
     93         }
     94     }
     95 
     96     /**
     97      * Register a callback to be notified about camera device availability.
     98      *
     99      * <p>Registering the same callback again will replace the handler with the
    100      * new one provided.</p>
    101      *
    102      * <p>The first time a callback is registered, it is immediately called
    103      * with the availability status of all currently known camera devices.</p>
    104      *
    105      * <p>Since this callback will be registered with the camera service, remember to unregister it
    106      * once it is no longer needed; otherwise the callback will continue to receive events
    107      * indefinitely and it may prevent other resources from being released. Specifically, the
    108      * callbacks will be invoked independently of the general activity lifecycle and independently
    109      * of the state of individual CameraManager instances.</p>
    110      *
    111      * @param callback the new callback to send camera availability notices to
    112      * @param handler The handler on which the callback should be invoked, or
    113      * {@code null} to use the current thread's {@link android.os.Looper looper}.
    114      */
    115     public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
    116         if (handler == null) {
    117             Looper looper = Looper.myLooper();
    118             if (looper == null) {
    119                 throw new IllegalArgumentException(
    120                         "No handler given, and current thread has no looper!");
    121             }
    122             handler = new Handler(looper);
    123         }
    124 
    125         CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
    126     }
    127 
    128     /**
    129      * Remove a previously-added callback; the callback will no longer receive connection and
    130      * disconnection callbacks.
    131      *
    132      * <p>Removing a callback that isn't registered has no effect.</p>
    133      *
    134      * @param callback The callback to remove from the notification list
    135      */
    136     public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
    137         CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
    138     }
    139 
    140     /**
    141      * <p>Query the capabilities of a camera device. These capabilities are
    142      * immutable for a given camera.</p>
    143      *
    144      * @param cameraId The id of the camera device to query
    145      * @return The properties of the given camera
    146      *
    147      * @throws IllegalArgumentException if the cameraId does not match any
    148      *         known camera device.
    149      * @throws CameraAccessException if the camera is disabled by device policy, or
    150      *         the camera device has been disconnected.
    151      * @throws SecurityException if the application does not have permission to
    152      *         access the camera
    153      *
    154      * @see #getCameraIdList
    155      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
    156      */
    157     public CameraCharacteristics getCameraCharacteristics(String cameraId)
    158             throws CameraAccessException {
    159         CameraCharacteristics characteristics = null;
    160 
    161         synchronized (mLock) {
    162             if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
    163                 throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
    164                         " currently connected camera device", cameraId));
    165             }
    166 
    167             int id = Integer.valueOf(cameraId);
    168 
    169             /*
    170              * Get the camera characteristics from the camera service directly if it supports it,
    171              * otherwise get them from the legacy shim instead.
    172              */
    173 
    174             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
    175             if (cameraService == null) {
    176                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    177                         "Camera service is currently unavailable");
    178             }
    179             try {
    180                 if (!supportsCamera2ApiLocked(cameraId)) {
    181                     // Legacy backwards compatibility path; build static info from the camera
    182                     // parameters
    183                     String[] outParameters = new String[1];
    184 
    185                     cameraService.getLegacyParameters(id, /*out*/outParameters);
    186                     String parameters = outParameters[0];
    187 
    188                     CameraInfo info = new CameraInfo();
    189                     cameraService.getCameraInfo(id, /*out*/info);
    190 
    191                     characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
    192                 } else {
    193                     // Normal path: Get the camera characteristics directly from the camera service
    194                     CameraMetadataNative info = new CameraMetadataNative();
    195 
    196                     cameraService.getCameraCharacteristics(id, info);
    197 
    198                     characteristics = new CameraCharacteristics(info);
    199                 }
    200             } catch (CameraRuntimeException e) {
    201                 throw e.asChecked();
    202             } catch (RemoteException e) {
    203                 // Camera service died - act as if the camera was disconnected
    204                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    205                         "Camera service is currently unavailable", e);
    206             }
    207         }
    208         return characteristics;
    209     }
    210 
    211     /**
    212      * Helper for openning a connection to a camera with the given ID.
    213      *
    214      * @param cameraId The unique identifier of the camera device to open
    215      * @param callback The callback for the camera. Must not be null.
    216      * @param handler  The handler to invoke the callback on. Must not be null.
    217      *
    218      * @throws CameraAccessException if the camera is disabled by device policy,
    219      * or too many camera devices are already open, or the cameraId does not match
    220      * any currently available camera device.
    221      *
    222      * @throws SecurityException if the application does not have permission to
    223      * access the camera
    224      * @throws IllegalArgumentException if callback or handler is null.
    225      * @return A handle to the newly-created camera device.
    226      *
    227      * @see #getCameraIdList
    228      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
    229      */
    230     private CameraDevice openCameraDeviceUserAsync(String cameraId,
    231             CameraDevice.StateCallback callback, Handler handler)
    232             throws CameraAccessException {
    233         CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
    234         CameraDevice device = null;
    235         try {
    236 
    237             synchronized (mLock) {
    238 
    239                 ICameraDeviceUser cameraUser = null;
    240 
    241                 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
    242                         new android.hardware.camera2.impl.CameraDeviceImpl(
    243                                 cameraId,
    244                                 callback,
    245                                 handler,
    246                                 characteristics);
    247 
    248                 BinderHolder holder = new BinderHolder();
    249 
    250                 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
    251                 int id = Integer.parseInt(cameraId);
    252                 try {
    253                     if (supportsCamera2ApiLocked(cameraId)) {
    254                         // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
    255                         ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
    256                         if (cameraService == null) {
    257                             throw new CameraRuntimeException(
    258                                 CameraAccessException.CAMERA_DISCONNECTED,
    259                                 "Camera service is currently unavailable");
    260                         }
    261                         cameraService.connectDevice(callbacks, id,
    262                                 mContext.getPackageName(), USE_CALLING_UID, holder);
    263                         cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
    264                     } else {
    265                         // Use legacy camera implementation for HAL1 devices
    266                         Log.i(TAG, "Using legacy camera HAL.");
    267                         cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
    268                     }
    269                 } catch (CameraRuntimeException e) {
    270                     if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
    271                         throw new AssertionError("Should've gone down the shim path");
    272                     } else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
    273                             e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
    274                             e.getReason() == CameraAccessException.CAMERA_DISABLED ||
    275                             e.getReason() == CameraAccessException.CAMERA_DISCONNECTED ||
    276                             e.getReason() == CameraAccessException.CAMERA_ERROR) {
    277                         // Received one of the known connection errors
    278                         // The remote camera device cannot be connected to, so
    279                         // set the local camera to the startup error state
    280                         deviceImpl.setRemoteFailure(e);
    281 
    282                         if (e.getReason() == CameraAccessException.CAMERA_DISABLED ||
    283                                 e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
    284                             // Per API docs, these failures call onError and throw
    285                             throw e.asChecked();
    286                         }
    287                     } else {
    288                         // Unexpected failure - rethrow
    289                         throw e;
    290                     }
    291                 } catch (RemoteException e) {
    292                     // Camera service died - act as if it's a CAMERA_DISCONNECTED case
    293                     CameraRuntimeException ce = new CameraRuntimeException(
    294                         CameraAccessException.CAMERA_DISCONNECTED,
    295                         "Camera service is currently unavailable", e);
    296                     deviceImpl.setRemoteFailure(ce);
    297                     throw ce.asChecked();
    298                 }
    299 
    300                 // TODO: factor out callback to be non-nested, then move setter to constructor
    301                 // For now, calling setRemoteDevice will fire initial
    302                 // onOpened/onUnconfigured callbacks.
    303                 deviceImpl.setRemoteDevice(cameraUser);
    304                 device = deviceImpl;
    305             }
    306 
    307         } catch (NumberFormatException e) {
    308             throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
    309                     + cameraId);
    310         } catch (CameraRuntimeException e) {
    311             throw e.asChecked();
    312         }
    313         return device;
    314     }
    315 
    316     /**
    317      * Open a connection to a camera with the given ID.
    318      *
    319      * <p>Use {@link #getCameraIdList} to get the list of available camera
    320      * devices. Note that even if an id is listed, open may fail if the device
    321      * is disconnected between the calls to {@link #getCameraIdList} and
    322      * {@link #openCamera}.</p>
    323      *
    324      * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
    325      * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
    326      * for operation by calling {@link CameraDevice#createCaptureSession} and
    327      * {@link CameraDevice#createCaptureRequest}</p>
    328      *
    329      * <!--
    330      * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
    331      * on the returned CameraDevice instance will be queued up until the device startup has
    332      * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is
    333      * called. The pending operations are then processed in order.</p>
    334      * -->
    335      * <p>If the camera becomes disconnected during initialization
    336      * after this function call returns,
    337      * {@link CameraDevice.StateCallback#onDisconnected} with a
    338      * {@link CameraDevice} in the disconnected state (and
    339      * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p>
    340      *
    341      * <p>If opening the camera device fails, then the device callback's
    342      * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent
    343      * calls on the camera device will throw a {@link CameraAccessException}.</p>
    344      *
    345      * @param cameraId
    346      *             The unique identifier of the camera device to open
    347      * @param callback
    348      *             The callback which is invoked once the camera is opened
    349      * @param handler
    350      *             The handler on which the callback should be invoked, or
    351      *             {@code null} to use the current thread's {@link android.os.Looper looper}.
    352      *
    353      * @throws CameraAccessException if the camera is disabled by device policy,
    354      * or the camera has become or was disconnected.
    355      *
    356      * @throws IllegalArgumentException if cameraId or the callback was null,
    357      * or the cameraId does not match any currently or previously available
    358      * camera device.
    359      *
    360      * @throws SecurityException if the application does not have permission to
    361      * access the camera
    362      *
    363      * @see #getCameraIdList
    364      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
    365      */
    366     public void openCamera(String cameraId, final CameraDevice.StateCallback callback,
    367             Handler handler)
    368             throws CameraAccessException {
    369 
    370         if (cameraId == null) {
    371             throw new IllegalArgumentException("cameraId was null");
    372         } else if (callback == null) {
    373             throw new IllegalArgumentException("callback was null");
    374         } else if (handler == null) {
    375             if (Looper.myLooper() != null) {
    376                 handler = new Handler();
    377             } else {
    378                 throw new IllegalArgumentException(
    379                         "Looper doesn't exist in the calling thread");
    380             }
    381         }
    382 
    383         openCameraDeviceUserAsync(cameraId, callback, handler);
    384     }
    385 
    386     /**
    387      * A callback for camera devices becoming available or
    388      * unavailable to open.
    389      *
    390      * <p>Cameras become available when they are no longer in use, or when a new
    391      * removable camera is connected. They become unavailable when some
    392      * application or service starts using a camera, or when a removable camera
    393      * is disconnected.</p>
    394      *
    395      * <p>Extend this callback and pass an instance of the subclass to
    396      * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability
    397      * changes.</p>
    398      *
    399      * @see registerAvailabilityCallback
    400      */
    401     public static abstract class AvailabilityCallback {
    402 
    403         /**
    404          * A new camera has become available to use.
    405          *
    406          * <p>The default implementation of this method does nothing.</p>
    407          *
    408          * @param cameraId The unique identifier of the new camera.
    409          */
    410         public void onCameraAvailable(String cameraId) {
    411             // default empty implementation
    412         }
    413 
    414         /**
    415          * A previously-available camera has become unavailable for use.
    416          *
    417          * <p>If an application had an active CameraDevice instance for the
    418          * now-disconnected camera, that application will receive a
    419          * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p>
    420          *
    421          * <p>The default implementation of this method does nothing.</p>
    422          *
    423          * @param cameraId The unique identifier of the disconnected camera.
    424          */
    425         public void onCameraUnavailable(String cameraId) {
    426             // default empty implementation
    427         }
    428     }
    429 
    430     /**
    431      * Return or create the list of currently connected camera devices.
    432      *
    433      * <p>In case of errors connecting to the camera service, will return an empty list.</p>
    434      */
    435     private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
    436         if (mDeviceIdList == null) {
    437             int numCameras = 0;
    438             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
    439             ArrayList<String> deviceIdList = new ArrayList<>();
    440 
    441             // If no camera service, then no devices
    442             if (cameraService == null) {
    443                 return deviceIdList;
    444             }
    445 
    446             try {
    447                 numCameras = cameraService.getNumberOfCameras();
    448             } catch(CameraRuntimeException e) {
    449                 throw e.asChecked();
    450             } catch (RemoteException e) {
    451                 // camera service just died - if no camera service, then no devices
    452                 return deviceIdList;
    453             }
    454 
    455             CameraMetadataNative info = new CameraMetadataNative();
    456             for (int i = 0; i < numCameras; ++i) {
    457                 // Non-removable cameras use integers starting at 0 for their
    458                 // identifiers
    459                 boolean isDeviceSupported = false;
    460                 try {
    461                     cameraService.getCameraCharacteristics(i, info);
    462                     if (!info.isEmpty()) {
    463                         isDeviceSupported = true;
    464                     } else {
    465                         throw new AssertionError("Expected to get non-empty characteristics");
    466                     }
    467                 } catch(IllegalArgumentException  e) {
    468                     // Got a BAD_VALUE from service, meaning that this
    469                     // device is not supported.
    470                 } catch(CameraRuntimeException e) {
    471                     // DISCONNECTED means that the HAL reported an low-level error getting the
    472                     // device info; skip listing the device.  Other errors,
    473                     // propagate exception onward
    474                     if (e.getReason() != CameraAccessException.CAMERA_DISCONNECTED) {
    475                         throw e.asChecked();
    476                     }
    477                 } catch(RemoteException e) {
    478                     // Camera service died - no devices to list
    479                     deviceIdList.clear();
    480                     return deviceIdList;
    481                 }
    482 
    483                 if (isDeviceSupported) {
    484                     deviceIdList.add(String.valueOf(i));
    485                 } else {
    486                     Log.w(TAG, "Error querying camera device " + i + " for listing.");
    487                 }
    488 
    489             }
    490             mDeviceIdList = deviceIdList;
    491         }
    492         return mDeviceIdList;
    493     }
    494 
    495     /**
    496      * Queries the camera service if it supports the camera2 api directly, or needs a shim.
    497      *
    498      * @param cameraId a non-{@code null} camera identifier
    499      * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
    500      */
    501     private boolean supportsCamera2ApiLocked(String cameraId) {
    502         return supportsCameraApiLocked(cameraId, API_VERSION_2);
    503     }
    504 
    505     /**
    506      * Queries the camera service if it supports a camera api directly, or needs a shim.
    507      *
    508      * @param cameraId a non-{@code null} camera identifier
    509      * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
    510      * @return {@code true} if connecting will work for that device version.
    511      */
    512     private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
    513         int id = Integer.parseInt(cameraId);
    514 
    515         /*
    516          * Possible return values:
    517          * - NO_ERROR => CameraX API is supported
    518          * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
    519          * - Remote exception => If the camera service died
    520          *
    521          * Anything else is an unexpected error we don't want to recover from.
    522          */
    523         try {
    524             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
    525             // If no camera service, no support
    526             if (cameraService == null) return false;
    527 
    528             int res = cameraService.supportsCameraApi(id, apiVersion);
    529 
    530             if (res != CameraServiceBinderDecorator.NO_ERROR) {
    531                 throw new AssertionError("Unexpected value " + res);
    532             }
    533             return true;
    534         } catch (CameraRuntimeException e) {
    535             if (e.getReason() != CameraAccessException.CAMERA_DEPRECATED_HAL) {
    536                 throw e;
    537             }
    538             // API level is not supported
    539         } catch (RemoteException e) {
    540             // Camera service is now down, no support for any API level
    541         }
    542         return false;
    543     }
    544 
    545     /**
    546      * A per-process global camera manager instance, to retain a connection to the camera service,
    547      * and to distribute camera availability notices to API-registered callbacks
    548      */
    549     private static final class CameraManagerGlobal extends ICameraServiceListener.Stub
    550             implements IBinder.DeathRecipient {
    551 
    552         private static final String TAG = "CameraManagerGlobal";
    553         private final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    554 
    555         // Singleton instance
    556         private static final CameraManagerGlobal gCameraManager =
    557             new CameraManagerGlobal();
    558 
    559         /**
    560          * This must match the ICameraService definition
    561          */
    562         private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
    563 
    564         // Keep up-to-date with ICameraServiceListener.h
    565 
    566         // Device physically unplugged
    567         public static final int STATUS_NOT_PRESENT = 0;
    568         // Device physically has been plugged in
    569         // and the camera can be used exclusively
    570         public static final int STATUS_PRESENT = 1;
    571         // Device physically has been plugged in
    572         // but it will not be connect-able until enumeration is complete
    573         public static final int STATUS_ENUMERATING = 2;
    574         // Camera is in use by another app and cannot be used exclusively
    575         public static final int STATUS_NOT_AVAILABLE = 0x80000000;
    576 
    577         // End enums shared with ICameraServiceListener.h
    578 
    579         // Camera ID -> Status map
    580         private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
    581 
    582         // Registered availablility callbacks and their handlers
    583         private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
    584             new ArrayMap<AvailabilityCallback, Handler>();
    585 
    586         private final Object mLock = new Object();
    587 
    588         // Access only through getCameraService to deal with binder death
    589         private ICameraService mCameraService;
    590 
    591         // Singleton, don't allow construction
    592         private CameraManagerGlobal() {
    593         }
    594 
    595         public static CameraManagerGlobal get() {
    596             return gCameraManager;
    597         }
    598 
    599         @Override
    600         public IBinder asBinder() {
    601             return this;
    602         }
    603 
    604         /**
    605          * Return a best-effort ICameraService.
    606          *
    607          * <p>This will be null if the camera service is not currently available. If the camera
    608          * service has died since the last use of the camera service, will try to reconnect to the
    609          * service.</p>
    610          */
    611         public ICameraService getCameraService() {
    612             synchronized(mLock) {
    613                 if (mCameraService == null) {
    614                     Log.i(TAG, "getCameraService: Reconnecting to camera service");
    615                     connectCameraServiceLocked();
    616                     if (mCameraService == null) {
    617                         Log.e(TAG, "Camera service is unavailable");
    618                     }
    619                 }
    620                 return mCameraService;
    621             }
    622         }
    623 
    624         /**
    625          * Connect to the camera service if it's available, and set up listeners.
    626          *
    627          * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
    628          */
    629         private void connectCameraServiceLocked() {
    630             mCameraService = null;
    631             IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
    632             if (cameraServiceBinder == null) {
    633                 // Camera service is now down, leave mCameraService as null
    634                 return;
    635             }
    636             try {
    637                 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
    638             } catch (RemoteException e) {
    639                 // Camera service is now down, leave mCameraService as null
    640                 return;
    641             }
    642 
    643             ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
    644 
    645             /**
    646              * Wrap the camera service in a decorator which automatically translates return codes
    647              * into exceptions.
    648              */
    649             ICameraService cameraService =
    650                 CameraServiceBinderDecorator.newInstance(cameraServiceRaw);
    651 
    652             try {
    653                 CameraServiceBinderDecorator.throwOnError(
    654                         CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
    655             } catch (CameraRuntimeException e) {
    656                 handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
    657             }
    658 
    659             try {
    660                 cameraService.addListener(this);
    661                 mCameraService = cameraService;
    662             } catch(CameraRuntimeException e) {
    663                 // Unexpected failure
    664                 throw new IllegalStateException("Failed to register a camera service listener",
    665                         e.asChecked());
    666             } catch (RemoteException e) {
    667                 // Camera service is now down, leave mCameraService as null
    668             }
    669         }
    670 
    671         private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) {
    672             int problem = e.getReason();
    673             switch (problem) {
    674             case CameraAccessException.CAMERA_DISCONNECTED:
    675                 String errorMsg = CameraAccessException.getDefaultMessage(problem);
    676                 Log.w(TAG, msg + ": " + errorMsg);
    677                 break;
    678             default:
    679                 throw new IllegalStateException(msg, e.asChecked());
    680             }
    681         }
    682 
    683         private boolean isAvailable(int status) {
    684             switch (status) {
    685                 case STATUS_PRESENT:
    686                     return true;
    687                 default:
    688                     return false;
    689             }
    690         }
    691 
    692         private boolean validStatus(int status) {
    693             switch (status) {
    694                 case STATUS_NOT_PRESENT:
    695                 case STATUS_PRESENT:
    696                 case STATUS_ENUMERATING:
    697                 case STATUS_NOT_AVAILABLE:
    698                     return true;
    699                 default:
    700                     return false;
    701             }
    702         }
    703 
    704         private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
    705                 final String id, final int status) {
    706             if (isAvailable(status)) {
    707                 handler.post(
    708                     new Runnable() {
    709                         @Override
    710                         public void run() {
    711                             callback.onCameraAvailable(id);
    712                         }
    713                     });
    714             } else {
    715                 handler.post(
    716                     new Runnable() {
    717                         @Override
    718                         public void run() {
    719                             callback.onCameraUnavailable(id);
    720                         }
    721                     });
    722             }
    723         }
    724 
    725         /**
    726          * Send the state of all known cameras to the provided listener, to initialize
    727          * the listener's knowledge of camera state.
    728          */
    729         private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
    730             for (int i = 0; i < mDeviceStatus.size(); i++) {
    731                 String id = mDeviceStatus.keyAt(i);
    732                 Integer status = mDeviceStatus.valueAt(i);
    733                 postSingleUpdate(callback, handler, id, status);
    734             }
    735         }
    736 
    737         private void onStatusChangedLocked(int status, String id) {
    738             if (DEBUG) {
    739                 Log.v(TAG,
    740                         String.format("Camera id %s has status changed to 0x%x", id, status));
    741             }
    742 
    743             if (!validStatus(status)) {
    744                 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id,
    745                                 status));
    746                 return;
    747             }
    748 
    749             Integer oldStatus = mDeviceStatus.put(id, status);
    750 
    751             if (oldStatus != null && oldStatus == status) {
    752                 if (DEBUG) {
    753                     Log.v(TAG, String.format(
    754                         "Device status changed to 0x%x, which is what it already was",
    755                         status));
    756                 }
    757                 return;
    758             }
    759 
    760             // TODO: consider abstracting out this state minimization + transition
    761             // into a separate
    762             // more easily testable class
    763             // i.e. (new State()).addState(STATE_AVAILABLE)
    764             //                   .addState(STATE_NOT_AVAILABLE)
    765             //                   .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
    766             //                   .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
    767             //                   .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
    768             //                   .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
    769 
    770             // Translate all the statuses to either 'available' or 'not available'
    771             //  available -> available         => no new update
    772             //  not available -> not available => no new update
    773             if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
    774                 if (DEBUG) {
    775                     Log.v(TAG,
    776                             String.format(
    777                                 "Device status was previously available (%d), " +
    778                                 " and is now again available (%d)" +
    779                                 "so no new client visible update will be sent",
    780                                 isAvailable(status), isAvailable(status)));
    781                 }
    782                 return;
    783             }
    784 
    785             final int callbackCount = mCallbackMap.size();
    786             for (int i = 0; i < callbackCount; i++) {
    787                 Handler handler = mCallbackMap.valueAt(i);
    788                 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
    789 
    790                 postSingleUpdate(callback, handler, id, status);
    791             }
    792         } // onStatusChangedLocked
    793 
    794         /**
    795          * Register a callback to be notified about camera device availability with the
    796          * global listener singleton.
    797          *
    798          * @param callback the new callback to send camera availability notices to
    799          * @param handler The handler on which the callback should be invoked. May not be null.
    800          */
    801         public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
    802             synchronized (mLock) {
    803                 Handler oldHandler = mCallbackMap.put(callback, handler);
    804                 // For new callbacks, provide initial availability information
    805                 if (oldHandler == null) {
    806                     updateCallbackLocked(callback, handler);
    807                 }
    808             }
    809         }
    810 
    811         /**
    812          * Remove a previously-added callback; the callback will no longer receive connection and
    813          * disconnection callbacks, and is no longer referenced by the global listener singleton.
    814          *
    815          * @param callback The callback to remove from the notification list
    816          */
    817         public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
    818             synchronized (mLock) {
    819                 mCallbackMap.remove(callback);
    820             }
    821         }
    822 
    823         /**
    824          * Callback from camera service notifying the process about camera availability changes
    825          */
    826         @Override
    827         public void onStatusChanged(int status, int cameraId) throws RemoteException {
    828             synchronized(mLock) {
    829                 onStatusChangedLocked(status, String.valueOf(cameraId));
    830             }
    831         }
    832 
    833         /**
    834          * Listener for camera service death.
    835          *
    836          * <p>The camera service isn't supposed to die under any normal circumstances, but can be
    837          * turned off during debug, or crash due to bugs.  So detect that and null out the interface
    838          * object, so that the next calls to the manager can try to reconnect.</p>
    839          */
    840         public void binderDied() {
    841             synchronized(mLock) {
    842                 // Only do this once per service death
    843                 if (mCameraService == null) return;
    844 
    845                 mCameraService = null;
    846 
    847                 // Tell listeners that the cameras are _available_, because any existing clients
    848                 // will have gotten disconnected. This is optimistic under the assumption that
    849                 // the service will be back shortly.
    850                 //
    851                 // Without this, a camera service crash while a camera is open will never signal
    852                 // to listeners that previously in-use cameras are now available.
    853                 for (int i = 0; i < mDeviceStatus.size(); i++) {
    854                     String cameraId = mDeviceStatus.keyAt(i);
    855                     onStatusChangedLocked(STATUS_PRESENT, cameraId);
    856                 }
    857             }
    858         }
    859 
    860     } // CameraManagerGlobal
    861 
    862 } // CameraManager
    863