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.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.annotation.RequiresPermission;
     22 import android.annotation.SystemService;
     23 import android.content.Context;
     24 import android.hardware.CameraInfo;
     25 import android.hardware.CameraStatus;
     26 import android.hardware.ICameraService;
     27 import android.hardware.ICameraServiceListener;
     28 import android.hardware.camera2.impl.CameraMetadataNative;
     29 import android.hardware.camera2.legacy.CameraDeviceUserShim;
     30 import android.hardware.camera2.legacy.LegacyMetadataMapper;
     31 import android.os.Binder;
     32 import android.os.DeadObjectException;
     33 import android.os.Handler;
     34 import android.os.IBinder;
     35 import android.os.Looper;
     36 import android.os.RemoteException;
     37 import android.os.ServiceManager;
     38 import android.os.ServiceSpecificException;
     39 import android.os.SystemProperties;
     40 import android.util.ArrayMap;
     41 import android.util.Log;
     42 
     43 import java.util.ArrayList;
     44 
     45 /**
     46  * <p>A system service manager for detecting, characterizing, and connecting to
     47  * {@link CameraDevice CameraDevices}.</p>
     48  *
     49  * <p>For more details about communicating with camera devices, read the Camera
     50  * developer guide or the {@link android.hardware.camera2 camera2}
     51  * package documentation.</p>
     52  */
     53 @SystemService(Context.CAMERA_SERVICE)
     54 public final class CameraManager {
     55 
     56     private static final String TAG = "CameraManager";
     57     private final boolean DEBUG = false;
     58 
     59     private static final int USE_CALLING_UID = -1;
     60 
     61     @SuppressWarnings("unused")
     62     private static final int API_VERSION_1 = 1;
     63     private static final int API_VERSION_2 = 2;
     64 
     65     private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0;
     66     private static final int CAMERA_TYPE_ALL = 1;
     67 
     68     private ArrayList<String> mDeviceIdList;
     69 
     70     private final Context mContext;
     71     private final Object mLock = new Object();
     72 
     73     /**
     74      * @hide
     75      */
     76     public CameraManager(Context context) {
     77         synchronized(mLock) {
     78             mContext = context;
     79         }
     80     }
     81 
     82     /**
     83      * Return the list of currently connected camera devices by identifier, including
     84      * cameras that may be in use by other camera API clients.
     85      *
     86      * <p>Non-removable cameras use integers starting at 0 for their
     87      * identifiers, while removable cameras have a unique identifier for each
     88      * individual device, even if they are the same model.</p>
     89      *
     90      * @return The list of currently connected camera devices.
     91      */
     92     @NonNull
     93     public String[] getCameraIdList() throws CameraAccessException {
     94         return CameraManagerGlobal.get().getCameraIdList();
     95     }
     96 
     97     /**
     98      * Register a callback to be notified about camera device availability.
     99      *
    100      * <p>Registering the same callback again will replace the handler with the
    101      * new one provided.</p>
    102      *
    103      * <p>The first time a callback is registered, it is immediately called
    104      * with the availability status of all currently known camera devices.</p>
    105      *
    106      * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera
    107      * device is opened by any camera API client. As of API level 23, other camera API clients may
    108      * still be able to open such a camera device, evicting the existing client if they have higher
    109      * priority than the existing client of a camera device. See open() for more details.</p>
    110      *
    111      * <p>Since this callback will be registered with the camera service, remember to unregister it
    112      * once it is no longer needed; otherwise the callback will continue to receive events
    113      * indefinitely and it may prevent other resources from being released. Specifically, the
    114      * callbacks will be invoked independently of the general activity lifecycle and independently
    115      * of the state of individual CameraManager instances.</p>
    116      *
    117      * @param callback the new callback to send camera availability notices to
    118      * @param handler The handler on which the callback should be invoked, or {@code null} to use
    119      *             the current thread's {@link android.os.Looper looper}.
    120      *
    121      * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
    122      *             no looper.
    123      */
    124     public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
    125             @Nullable Handler handler) {
    126         if (handler == null) {
    127             Looper looper = Looper.myLooper();
    128             if (looper == null) {
    129                 throw new IllegalArgumentException(
    130                         "No handler given, and current thread has no looper!");
    131             }
    132             handler = new Handler(looper);
    133         }
    134 
    135         CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
    136     }
    137 
    138     /**
    139      * Remove a previously-added callback; the callback will no longer receive connection and
    140      * disconnection callbacks.
    141      *
    142      * <p>Removing a callback that isn't registered has no effect.</p>
    143      *
    144      * @param callback The callback to remove from the notification list
    145      */
    146     public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) {
    147         CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
    148     }
    149 
    150     /**
    151      * Register a callback to be notified about torch mode status.
    152      *
    153      * <p>Registering the same callback again will replace the handler with the
    154      * new one provided.</p>
    155      *
    156      * <p>The first time a callback is registered, it is immediately called
    157      * with the torch mode status of all currently known camera devices with a flash unit.</p>
    158      *
    159      * <p>Since this callback will be registered with the camera service, remember to unregister it
    160      * once it is no longer needed; otherwise the callback will continue to receive events
    161      * indefinitely and it may prevent other resources from being released. Specifically, the
    162      * callbacks will be invoked independently of the general activity lifecycle and independently
    163      * of the state of individual CameraManager instances.</p>
    164      *
    165      * @param callback The new callback to send torch mode status to
    166      * @param handler The handler on which the callback should be invoked, or {@code null} to use
    167      *             the current thread's {@link android.os.Looper looper}.
    168      *
    169      * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
    170      *             no looper.
    171      */
    172     public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
    173         if (handler == null) {
    174             Looper looper = Looper.myLooper();
    175             if (looper == null) {
    176                 throw new IllegalArgumentException(
    177                         "No handler given, and current thread has no looper!");
    178             }
    179             handler = new Handler(looper);
    180         }
    181         CameraManagerGlobal.get().registerTorchCallback(callback, handler);
    182     }
    183 
    184     /**
    185      * Remove a previously-added callback; the callback will no longer receive torch mode status
    186      * callbacks.
    187      *
    188      * <p>Removing a callback that isn't registered has no effect.</p>
    189      *
    190      * @param callback The callback to remove from the notification list
    191      */
    192     public void unregisterTorchCallback(@NonNull TorchCallback callback) {
    193         CameraManagerGlobal.get().unregisterTorchCallback(callback);
    194     }
    195 
    196     /**
    197      * <p>Query the capabilities of a camera device. These capabilities are
    198      * immutable for a given camera.</p>
    199      *
    200      * @param cameraId The id of the camera device to query
    201      * @return The properties of the given camera
    202      *
    203      * @throws IllegalArgumentException if the cameraId does not match any
    204      *         known camera device.
    205      * @throws CameraAccessException if the camera device has been disconnected.
    206      *
    207      * @see #getCameraIdList
    208      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
    209      */
    210     @NonNull
    211     public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
    212             throws CameraAccessException {
    213         CameraCharacteristics characteristics = null;
    214         if (CameraManagerGlobal.sCameraServiceDisabled) {
    215             throw new IllegalArgumentException("No cameras available on device");
    216         }
    217         synchronized (mLock) {
    218             /*
    219              * Get the camera characteristics from the camera service directly if it supports it,
    220              * otherwise get them from the legacy shim instead.
    221              */
    222             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
    223             if (cameraService == null) {
    224                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    225                         "Camera service is currently unavailable");
    226             }
    227             try {
    228                 if (!supportsCamera2ApiLocked(cameraId)) {
    229                     // Legacy backwards compatibility path; build static info from the camera
    230                     // parameters
    231                     int id = Integer.parseInt(cameraId);
    232 
    233                     String parameters = cameraService.getLegacyParameters(id);
    234 
    235                     CameraInfo info = cameraService.getCameraInfo(id);
    236 
    237                     characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
    238                 } else {
    239                     // Normal path: Get the camera characteristics directly from the camera service
    240                     CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
    241 
    242                     characteristics = new CameraCharacteristics(info);
    243                 }
    244             } catch (ServiceSpecificException e) {
    245                 throwAsPublicException(e);
    246             } catch (RemoteException e) {
    247                 // Camera service died - act as if the camera was disconnected
    248                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    249                         "Camera service is currently unavailable", e);
    250             }
    251         }
    252         return characteristics;
    253     }
    254 
    255     /**
    256      * Helper for opening a connection to a camera with the given ID.
    257      *
    258      * @param cameraId The unique identifier of the camera device to open
    259      * @param callback The callback for the camera. Must not be null.
    260      * @param handler  The handler to invoke the callback on. Must not be null.
    261      * @param uid      The UID of the application actually opening the camera.
    262      *                 Must be USE_CALLING_UID unless the caller is a service
    263      *                 that is trusted to open the device on behalf of an
    264      *                 application and to forward the real UID.
    265      *
    266      * @throws CameraAccessException if the camera is disabled by device policy,
    267      * too many camera devices are already open, or the cameraId does not match
    268      * any currently available camera device.
    269      *
    270      * @throws SecurityException if the application does not have permission to
    271      * access the camera
    272      * @throws IllegalArgumentException if callback or handler is null.
    273      * @return A handle to the newly-created camera device.
    274      *
    275      * @see #getCameraIdList
    276      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
    277      */
    278     private CameraDevice openCameraDeviceUserAsync(String cameraId,
    279             CameraDevice.StateCallback callback, Handler handler, final int uid)
    280             throws CameraAccessException {
    281         CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
    282         CameraDevice device = null;
    283 
    284         synchronized (mLock) {
    285 
    286             ICameraDeviceUser cameraUser = null;
    287 
    288             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
    289                     new android.hardware.camera2.impl.CameraDeviceImpl(
    290                         cameraId,
    291                         callback,
    292                         handler,
    293                         characteristics,
    294                         mContext.getApplicationInfo().targetSdkVersion);
    295 
    296             ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
    297 
    298             try {
    299                 if (supportsCamera2ApiLocked(cameraId)) {
    300                     // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
    301                     ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
    302                     if (cameraService == null) {
    303                         throw new ServiceSpecificException(
    304                             ICameraService.ERROR_DISCONNECTED,
    305                             "Camera service is currently unavailable");
    306                     }
    307                     cameraUser = cameraService.connectDevice(callbacks, cameraId,
    308                             mContext.getOpPackageName(), uid);
    309                 } else {
    310                     // Use legacy camera implementation for HAL1 devices
    311                     int id;
    312                     try {
    313                         id = Integer.parseInt(cameraId);
    314                     } catch (NumberFormatException e) {
    315                         throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
    316                                 + cameraId);
    317                     }
    318 
    319                     Log.i(TAG, "Using legacy camera HAL.");
    320                     cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
    321                 }
    322             } catch (ServiceSpecificException e) {
    323                 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
    324                     throw new AssertionError("Should've gone down the shim path");
    325                 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
    326                         e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
    327                         e.errorCode == ICameraService.ERROR_DISABLED ||
    328                         e.errorCode == ICameraService.ERROR_DISCONNECTED ||
    329                         e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
    330                     // Received one of the known connection errors
    331                     // The remote camera device cannot be connected to, so
    332                     // set the local camera to the startup error state
    333                     deviceImpl.setRemoteFailure(e);
    334 
    335                     if (e.errorCode == ICameraService.ERROR_DISABLED ||
    336                             e.errorCode == ICameraService.ERROR_DISCONNECTED ||
    337                             e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
    338                         // Per API docs, these failures call onError and throw
    339                         throwAsPublicException(e);
    340                     }
    341                 } else {
    342                     // Unexpected failure - rethrow
    343                     throwAsPublicException(e);
    344                 }
    345             } catch (RemoteException e) {
    346                 // Camera service died - act as if it's a CAMERA_DISCONNECTED case
    347                 ServiceSpecificException sse = new ServiceSpecificException(
    348                     ICameraService.ERROR_DISCONNECTED,
    349                     "Camera service is currently unavailable");
    350                 deviceImpl.setRemoteFailure(sse);
    351                 throwAsPublicException(sse);
    352             }
    353 
    354             // TODO: factor out callback to be non-nested, then move setter to constructor
    355             // For now, calling setRemoteDevice will fire initial
    356             // onOpened/onUnconfigured callbacks.
    357             // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
    358             // cameraUser dies during setup.
    359             deviceImpl.setRemoteDevice(cameraUser);
    360             device = deviceImpl;
    361         }
    362 
    363         return device;
    364     }
    365 
    366     /**
    367      * Open a connection to a camera with the given ID.
    368      *
    369      * <p>Use {@link #getCameraIdList} to get the list of available camera
    370      * devices. Note that even if an id is listed, open may fail if the device
    371      * is disconnected between the calls to {@link #getCameraIdList} and
    372      * {@link #openCamera}, or if a higher-priority camera API client begins using the
    373      * camera device.</p>
    374      *
    375      * <p>As of API level 23, devices for which the
    376      * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the
    377      * device being in use by a lower-priority, background camera API client can still potentially
    378      * be opened by calling this method when the calling camera API client has a higher priority
    379      * than the current camera API client using this device.  In general, if the top, foreground
    380      * activity is running within your application process, your process will be given the highest
    381      * priority when accessing the camera, and this method will succeed even if the camera device is
    382      * in use by another camera API client. Any lower-priority application that loses control of the
    383      * camera in this way will receive an
    384      * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p>
    385      *
    386      * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
    387      * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
    388      * for operation by calling {@link CameraDevice#createCaptureSession} and
    389      * {@link CameraDevice#createCaptureRequest}</p>
    390      *
    391      * <!--
    392      * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
    393      * on the returned CameraDevice instance will be queued up until the device startup has
    394      * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is
    395      * called. The pending operations are then processed in order.</p>
    396      * -->
    397      * <p>If the camera becomes disconnected during initialization
    398      * after this function call returns,
    399      * {@link CameraDevice.StateCallback#onDisconnected} with a
    400      * {@link CameraDevice} in the disconnected state (and
    401      * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p>
    402      *
    403      * <p>If opening the camera device fails, then the device callback's
    404      * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent
    405      * calls on the camera device will throw a {@link CameraAccessException}.</p>
    406      *
    407      * @param cameraId
    408      *             The unique identifier of the camera device to open
    409      * @param callback
    410      *             The callback which is invoked once the camera is opened
    411      * @param handler
    412      *             The handler on which the callback should be invoked, or
    413      *             {@code null} to use the current thread's {@link android.os.Looper looper}.
    414      *
    415      * @throws CameraAccessException if the camera is disabled by device policy,
    416      * has been disconnected, or is being used by a higher-priority camera API client.
    417      *
    418      * @throws IllegalArgumentException if cameraId or the callback was null,
    419      * or the cameraId does not match any currently or previously available
    420      * camera device.
    421      *
    422      * @throws SecurityException if the application does not have permission to
    423      * access the camera
    424      *
    425      * @see #getCameraIdList
    426      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
    427      */
    428     @RequiresPermission(android.Manifest.permission.CAMERA)
    429     public void openCamera(@NonNull String cameraId,
    430             @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
    431             throws CameraAccessException {
    432 
    433         openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
    434     }
    435 
    436     /**
    437      * Open a connection to a camera with the given ID, on behalf of another application
    438      * specified by clientUid.
    439      *
    440      * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
    441      * the caller to specify the UID to use for permission/etc verification. This can only be
    442      * done by services trusted by the camera subsystem to act on behalf of applications and
    443      * to forward the real UID.</p>
    444      *
    445      * @param clientUid
    446      *             The UID of the application on whose behalf the camera is being opened.
    447      *             Must be USE_CALLING_UID unless the caller is a trusted service.
    448      *
    449      * @hide
    450      */
    451     public void openCameraForUid(@NonNull String cameraId,
    452             @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
    453             int clientUid)
    454             throws CameraAccessException {
    455 
    456         if (cameraId == null) {
    457             throw new IllegalArgumentException("cameraId was null");
    458         } else if (callback == null) {
    459             throw new IllegalArgumentException("callback was null");
    460         } else if (handler == null) {
    461             if (Looper.myLooper() != null) {
    462                 handler = new Handler();
    463             } else {
    464                 throw new IllegalArgumentException(
    465                         "Handler argument is null, but no looper exists in the calling thread");
    466             }
    467         }
    468         if (CameraManagerGlobal.sCameraServiceDisabled) {
    469             throw new IllegalArgumentException("No cameras available on device");
    470         }
    471 
    472         openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
    473     }
    474 
    475     /**
    476      * Set the flash unit's torch mode of the camera of the given ID without opening the camera
    477      * device.
    478      *
    479      * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use
    480      * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit.
    481      * Note that even if a camera device has a flash unit, turning on the torch mode may fail
    482      * if the camera device or other camera resources needed to turn on the torch mode are in use.
    483      * </p>
    484      *
    485      * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully,
    486      * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked.
    487      * However, even if turning on the torch mode is successful, the application does not have the
    488      * exclusive ownership of the flash unit or the camera device. The torch mode will be turned
    489      * off and becomes unavailable when the camera device that the flash unit belongs to becomes
    490      * unavailable or when other camera resources to keep the torch on become unavailable (
    491      * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also,
    492      * other applications are free to call {@link #setTorchMode} to turn off the torch mode (
    493      * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest
    494      * application that turned on the torch mode exits, the torch mode will be turned off.
    495      *
    496      * @param cameraId
    497      *             The unique identifier of the camera device that the flash unit belongs to.
    498      * @param enabled
    499      *             The desired state of the torch mode for the target camera device. Set to
    500      *             {@code true} to turn on the torch mode. Set to {@code false} to turn off the
    501      *             torch mode.
    502      *
    503      * @throws CameraAccessException if it failed to access the flash unit.
    504      *             {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
    505      *             is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
    506      *             other camera resources needed to turn on the torch mode are in use.
    507      *             {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera
    508      *             service is not available.
    509      *
    510      * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
    511      *             or previously available camera device, or the camera device doesn't have a
    512      *             flash unit.
    513      */
    514     public void setTorchMode(@NonNull String cameraId, boolean enabled)
    515             throws CameraAccessException {
    516         if (CameraManagerGlobal.sCameraServiceDisabled) {
    517             throw new IllegalArgumentException("No cameras available on device");
    518         }
    519         CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
    520     }
    521 
    522     /**
    523      * A callback for camera devices becoming available or unavailable to open.
    524      *
    525      * <p>Cameras become available when they are no longer in use, or when a new
    526      * removable camera is connected. They become unavailable when some
    527      * application or service starts using a camera, or when a removable camera
    528      * is disconnected.</p>
    529      *
    530      * <p>Extend this callback and pass an instance of the subclass to
    531      * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability
    532      * changes.</p>
    533      *
    534      * @see #registerAvailabilityCallback
    535      */
    536     public static abstract class AvailabilityCallback {
    537 
    538         /**
    539          * A new camera has become available to use.
    540          *
    541          * <p>The default implementation of this method does nothing.</p>
    542          *
    543          * @param cameraId The unique identifier of the new camera.
    544          */
    545         public void onCameraAvailable(@NonNull String cameraId) {
    546             // default empty implementation
    547         }
    548 
    549         /**
    550          * A previously-available camera has become unavailable for use.
    551          *
    552          * <p>If an application had an active CameraDevice instance for the
    553          * now-disconnected camera, that application will receive a
    554          * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p>
    555          *
    556          * <p>The default implementation of this method does nothing.</p>
    557          *
    558          * @param cameraId The unique identifier of the disconnected camera.
    559          */
    560         public void onCameraUnavailable(@NonNull String cameraId) {
    561             // default empty implementation
    562         }
    563     }
    564 
    565     /**
    566      * A callback for camera flash torch modes becoming unavailable, disabled, or enabled.
    567      *
    568      * <p>The torch mode becomes unavailable when the camera device it belongs to becomes
    569      * unavailable or other camera resources it needs become busy due to other higher priority
    570      * camera activities. The torch mode becomes disabled when it was turned off or when the camera
    571      * device it belongs to is no longer in use and other camera resources it needs are no longer
    572      * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to
    573      * turn off the camera's torch mode, or when an application turns on another camera's torch mode
    574      * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes
    575      * enabled when it is turned on via {@link #setTorchMode}.</p>
    576      *
    577      * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled
    578      * or enabled state.</p>
    579      *
    580      * <p>Extend this callback and pass an instance of the subclass to
    581      * {@link CameraManager#registerTorchCallback} to be notified of such status changes.
    582      * </p>
    583      *
    584      * @see #registerTorchCallback
    585      */
    586     public static abstract class TorchCallback {
    587         /**
    588          * A camera's torch mode has become unavailable to set via {@link #setTorchMode}.
    589          *
    590          * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be
    591          * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is
    592          * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or
    593          * enabled state again.</p>
    594          *
    595          * <p>The default implementation of this method does nothing.</p>
    596          *
    597          * @param cameraId The unique identifier of the camera whose torch mode has become
    598          *                 unavailable.
    599          */
    600         public void onTorchModeUnavailable(@NonNull String cameraId) {
    601             // default empty implementation
    602         }
    603 
    604         /**
    605          * A camera's torch mode has become enabled or disabled and can be changed via
    606          * {@link #setTorchMode}.
    607          *
    608          * <p>The default implementation of this method does nothing.</p>
    609          *
    610          * @param cameraId The unique identifier of the camera whose torch mode has been changed.
    611          *
    612          * @param enabled The state that the torch mode of the camera has been changed to.
    613          *                {@code true} when the torch mode has become on and available to be turned
    614          *                off. {@code false} when the torch mode has becomes off and available to
    615          *                be turned on.
    616          */
    617         public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
    618             // default empty implementation
    619         }
    620     }
    621 
    622     /**
    623      * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces
    624      * into the correct public exceptions.
    625      *
    626      * @hide
    627      */
    628     public static void throwAsPublicException(Throwable t) throws CameraAccessException {
    629         if (t instanceof ServiceSpecificException) {
    630             ServiceSpecificException e = (ServiceSpecificException) t;
    631             int reason = CameraAccessException.CAMERA_ERROR;
    632             switch(e.errorCode) {
    633                 case ICameraService.ERROR_DISCONNECTED:
    634                     reason = CameraAccessException.CAMERA_DISCONNECTED;
    635                     break;
    636                 case ICameraService.ERROR_DISABLED:
    637                     reason = CameraAccessException.CAMERA_DISABLED;
    638                     break;
    639                 case ICameraService.ERROR_CAMERA_IN_USE:
    640                     reason = CameraAccessException.CAMERA_IN_USE;
    641                     break;
    642                 case ICameraService.ERROR_MAX_CAMERAS_IN_USE:
    643                     reason = CameraAccessException.MAX_CAMERAS_IN_USE;
    644                     break;
    645                 case ICameraService.ERROR_DEPRECATED_HAL:
    646                     reason = CameraAccessException.CAMERA_DEPRECATED_HAL;
    647                     break;
    648                 case ICameraService.ERROR_ILLEGAL_ARGUMENT:
    649                 case ICameraService.ERROR_ALREADY_EXISTS:
    650                     throw new IllegalArgumentException(e.getMessage(), e);
    651                 case ICameraService.ERROR_PERMISSION_DENIED:
    652                     throw new SecurityException(e.getMessage(), e);
    653                 case ICameraService.ERROR_TIMED_OUT:
    654                 case ICameraService.ERROR_INVALID_OPERATION:
    655                 default:
    656                     reason = CameraAccessException.CAMERA_ERROR;
    657             }
    658             throw new CameraAccessException(reason, e.getMessage(), e);
    659         } else if (t instanceof DeadObjectException) {
    660             throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    661                     "Camera service has died unexpectedly",
    662                     t);
    663         } else if (t instanceof RemoteException) {
    664             throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
    665                     " which should never happen.", t);
    666         } else if (t instanceof RuntimeException) {
    667             RuntimeException e = (RuntimeException) t;
    668             throw e;
    669         }
    670     }
    671 
    672     /**
    673      * Queries the camera service if it supports the camera2 api directly, or needs a shim.
    674      *
    675      * @param cameraId a non-{@code null} camera identifier
    676      * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
    677      */
    678     private boolean supportsCamera2ApiLocked(String cameraId) {
    679         return supportsCameraApiLocked(cameraId, API_VERSION_2);
    680     }
    681 
    682     /**
    683      * Queries the camera service if it supports a camera api directly, or needs a shim.
    684      *
    685      * @param cameraId a non-{@code null} camera identifier
    686      * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
    687      * @return {@code true} if connecting will work for that device version.
    688      */
    689     private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
    690         /*
    691          * Possible return values:
    692          * - NO_ERROR => CameraX API is supported
    693          * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
    694          * - Remote exception => If the camera service died
    695          *
    696          * Anything else is an unexpected error we don't want to recover from.
    697          */
    698         try {
    699             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
    700             // If no camera service, no support
    701             if (cameraService == null) return false;
    702 
    703             return cameraService.supportsCameraApi(cameraId, apiVersion);
    704         } catch (RemoteException e) {
    705             // Camera service is now down, no support for any API level
    706         }
    707         return false;
    708     }
    709 
    710     /**
    711      * A per-process global camera manager instance, to retain a connection to the camera service,
    712      * and to distribute camera availability notices to API-registered callbacks
    713      */
    714     private static final class CameraManagerGlobal extends ICameraServiceListener.Stub
    715             implements IBinder.DeathRecipient {
    716 
    717         private static final String TAG = "CameraManagerGlobal";
    718         private final boolean DEBUG = false;
    719 
    720         private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000;
    721 
    722         // Singleton instance
    723         private static final CameraManagerGlobal gCameraManager =
    724             new CameraManagerGlobal();
    725 
    726         /**
    727          * This must match the ICameraService definition
    728          */
    729         private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
    730 
    731         // Camera ID -> Status map
    732         private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
    733 
    734         // Registered availablility callbacks and their handlers
    735         private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
    736             new ArrayMap<AvailabilityCallback, Handler>();
    737 
    738         // torch client binder to set the torch mode with.
    739         private Binder mTorchClientBinder = new Binder();
    740 
    741         // Camera ID -> Torch status map
    742         private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
    743 
    744         // Registered torch callbacks and their handlers
    745         private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
    746                 new ArrayMap<TorchCallback, Handler>();
    747 
    748         private final Object mLock = new Object();
    749 
    750         // Access only through getCameraService to deal with binder death
    751         private ICameraService mCameraService;
    752 
    753         // Singleton, don't allow construction
    754         private CameraManagerGlobal() {
    755         }
    756 
    757         public static final boolean sCameraServiceDisabled =
    758                 SystemProperties.getBoolean("config.disable_cameraservice", false);
    759 
    760         public static CameraManagerGlobal get() {
    761             return gCameraManager;
    762         }
    763 
    764         @Override
    765         public IBinder asBinder() {
    766             return this;
    767         }
    768 
    769         /**
    770          * Return a best-effort ICameraService.
    771          *
    772          * <p>This will be null if the camera service is not currently available. If the camera
    773          * service has died since the last use of the camera service, will try to reconnect to the
    774          * service.</p>
    775          */
    776         public ICameraService getCameraService() {
    777             synchronized(mLock) {
    778                 connectCameraServiceLocked();
    779                 if (mCameraService == null && !sCameraServiceDisabled) {
    780                     Log.e(TAG, "Camera service is unavailable");
    781                 }
    782                 return mCameraService;
    783             }
    784         }
    785 
    786         /**
    787          * Connect to the camera service if it's available, and set up listeners.
    788          * If the service is already connected, do nothing.
    789          *
    790          * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
    791          */
    792         private void connectCameraServiceLocked() {
    793             // Only reconnect if necessary
    794             if (mCameraService != null || sCameraServiceDisabled) return;
    795 
    796             Log.i(TAG, "Connecting to camera service");
    797 
    798             IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
    799             if (cameraServiceBinder == null) {
    800                 // Camera service is now down, leave mCameraService as null
    801                 return;
    802             }
    803             try {
    804                 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
    805             } catch (RemoteException e) {
    806                 // Camera service is now down, leave mCameraService as null
    807                 return;
    808             }
    809 
    810             ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
    811 
    812             try {
    813                 CameraMetadataNative.setupGlobalVendorTagDescriptor();
    814             } catch (ServiceSpecificException e) {
    815                 handleRecoverableSetupErrors(e);
    816             }
    817 
    818             try {
    819                 CameraStatus[] cameraStatuses = cameraService.addListener(this);
    820                 for (CameraStatus c : cameraStatuses) {
    821                     onStatusChangedLocked(c.status, c.cameraId);
    822                 }
    823                 mCameraService = cameraService;
    824             } catch(ServiceSpecificException e) {
    825                 // Unexpected failure
    826                 throw new IllegalStateException("Failed to register a camera service listener", e);
    827             } catch (RemoteException e) {
    828                 // Camera service is now down, leave mCameraService as null
    829             }
    830         }
    831 
    832         /**
    833          * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
    834          * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
    835          */
    836         public String[] getCameraIdList() {
    837             String[] cameraIds = null;
    838             synchronized(mLock) {
    839                 // Try to make sure we have an up-to-date list of camera devices.
    840                 connectCameraServiceLocked();
    841 
    842                 int idCount = 0;
    843                 for (int i = 0; i < mDeviceStatus.size(); i++) {
    844                     int status = mDeviceStatus.valueAt(i);
    845                     if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
    846                             status == ICameraServiceListener.STATUS_ENUMERATING) continue;
    847                     idCount++;
    848                 }
    849                 cameraIds = new String[idCount];
    850                 idCount = 0;
    851                 for (int i = 0; i < mDeviceStatus.size(); i++) {
    852                     int status = mDeviceStatus.valueAt(i);
    853                     if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
    854                             status == ICameraServiceListener.STATUS_ENUMERATING) continue;
    855                     cameraIds[idCount] = mDeviceStatus.keyAt(i);
    856                     idCount++;
    857                 }
    858             }
    859             return cameraIds;
    860         }
    861 
    862         public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
    863             synchronized(mLock) {
    864 
    865                 if (cameraId == null) {
    866                     throw new IllegalArgumentException("cameraId was null");
    867                 }
    868 
    869                 ICameraService cameraService = getCameraService();
    870                 if (cameraService == null) {
    871                     throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    872                         "Camera service is currently unavailable");
    873                 }
    874 
    875                 try {
    876                     cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder);
    877                 } catch(ServiceSpecificException e) {
    878                     throwAsPublicException(e);
    879                 } catch (RemoteException e) {
    880                     throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    881                             "Camera service is currently unavailable");
    882                 }
    883             }
    884         }
    885 
    886         private void handleRecoverableSetupErrors(ServiceSpecificException e) {
    887             switch (e.errorCode) {
    888                 case ICameraService.ERROR_DISCONNECTED:
    889                     Log.w(TAG, e.getMessage());
    890                     break;
    891                 default:
    892                     throw new IllegalStateException(e);
    893             }
    894         }
    895 
    896         private boolean isAvailable(int status) {
    897             switch (status) {
    898                 case ICameraServiceListener.STATUS_PRESENT:
    899                     return true;
    900                 default:
    901                     return false;
    902             }
    903         }
    904 
    905         private boolean validStatus(int status) {
    906             switch (status) {
    907                 case ICameraServiceListener.STATUS_NOT_PRESENT:
    908                 case ICameraServiceListener.STATUS_PRESENT:
    909                 case ICameraServiceListener.STATUS_ENUMERATING:
    910                 case ICameraServiceListener.STATUS_NOT_AVAILABLE:
    911                     return true;
    912                 default:
    913                     return false;
    914             }
    915         }
    916 
    917         private boolean validTorchStatus(int status) {
    918             switch (status) {
    919                 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE:
    920                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
    921                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
    922                     return true;
    923                 default:
    924                     return false;
    925             }
    926         }
    927 
    928         private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
    929                 final String id, final int status) {
    930             if (isAvailable(status)) {
    931                 handler.post(
    932                     new Runnable() {
    933                         @Override
    934                         public void run() {
    935                             callback.onCameraAvailable(id);
    936                         }
    937                     });
    938             } else {
    939                 handler.post(
    940                     new Runnable() {
    941                         @Override
    942                         public void run() {
    943                             callback.onCameraUnavailable(id);
    944                         }
    945                     });
    946             }
    947         }
    948 
    949         private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
    950                 final String id, final int status) {
    951             switch(status) {
    952                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
    953                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
    954                     handler.post(
    955                             new Runnable() {
    956                                 @Override
    957                                 public void run() {
    958                                     callback.onTorchModeChanged(id, status ==
    959                                             ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
    960                                 }
    961                             });
    962                     break;
    963                 default:
    964                     handler.post(
    965                             new Runnable() {
    966                                 @Override
    967                                 public void run() {
    968                                     callback.onTorchModeUnavailable(id);
    969                                 }
    970                             });
    971                     break;
    972             }
    973         }
    974 
    975         /**
    976          * Send the state of all known cameras to the provided listener, to initialize
    977          * the listener's knowledge of camera state.
    978          */
    979         private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
    980             for (int i = 0; i < mDeviceStatus.size(); i++) {
    981                 String id = mDeviceStatus.keyAt(i);
    982                 Integer status = mDeviceStatus.valueAt(i);
    983                 postSingleUpdate(callback, handler, id, status);
    984             }
    985         }
    986 
    987         private void onStatusChangedLocked(int status, String id) {
    988             if (DEBUG) {
    989                 Log.v(TAG,
    990                         String.format("Camera id %s has status changed to 0x%x", id, status));
    991             }
    992 
    993             if (!validStatus(status)) {
    994                 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id,
    995                                 status));
    996                 return;
    997             }
    998 
    999             Integer oldStatus = mDeviceStatus.put(id, status);
   1000 
   1001             if (oldStatus != null && oldStatus == status) {
   1002                 if (DEBUG) {
   1003                     Log.v(TAG, String.format(
   1004                         "Device status changed to 0x%x, which is what it already was",
   1005                         status));
   1006                 }
   1007                 return;
   1008             }
   1009 
   1010             // TODO: consider abstracting out this state minimization + transition
   1011             // into a separate
   1012             // more easily testable class
   1013             // i.e. (new State()).addState(STATE_AVAILABLE)
   1014             //                   .addState(STATE_NOT_AVAILABLE)
   1015             //                   .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
   1016             //                   .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
   1017             //                   .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
   1018             //                   .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
   1019 
   1020             // Translate all the statuses to either 'available' or 'not available'
   1021             //  available -> available         => no new update
   1022             //  not available -> not available => no new update
   1023             if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
   1024                 if (DEBUG) {
   1025                     Log.v(TAG,
   1026                             String.format(
   1027                                 "Device status was previously available (%b), " +
   1028                                 " and is now again available (%b)" +
   1029                                 "so no new client visible update will be sent",
   1030                                 isAvailable(oldStatus), isAvailable(status)));
   1031                 }
   1032                 return;
   1033             }
   1034 
   1035             final int callbackCount = mCallbackMap.size();
   1036             for (int i = 0; i < callbackCount; i++) {
   1037                 Handler handler = mCallbackMap.valueAt(i);
   1038                 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
   1039 
   1040                 postSingleUpdate(callback, handler, id, status);
   1041             }
   1042         } // onStatusChangedLocked
   1043 
   1044         private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
   1045             for (int i = 0; i < mTorchStatus.size(); i++) {
   1046                 String id = mTorchStatus.keyAt(i);
   1047                 Integer status = mTorchStatus.valueAt(i);
   1048                 postSingleTorchUpdate(callback, handler, id, status);
   1049             }
   1050         }
   1051 
   1052         private void onTorchStatusChangedLocked(int status, String id) {
   1053             if (DEBUG) {
   1054                 Log.v(TAG,
   1055                         String.format("Camera id %s has torch status changed to 0x%x", id, status));
   1056             }
   1057 
   1058             if (!validTorchStatus(status)) {
   1059                 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id,
   1060                                 status));
   1061                 return;
   1062             }
   1063 
   1064             Integer oldStatus = mTorchStatus.put(id, status);
   1065             if (oldStatus != null && oldStatus == status) {
   1066                 if (DEBUG) {
   1067                     Log.v(TAG, String.format(
   1068                         "Torch status changed to 0x%x, which is what it already was",
   1069                         status));
   1070                 }
   1071                 return;
   1072             }
   1073 
   1074             final int callbackCount = mTorchCallbackMap.size();
   1075             for (int i = 0; i < callbackCount; i++) {
   1076                 final Handler handler = mTorchCallbackMap.valueAt(i);
   1077                 final TorchCallback callback = mTorchCallbackMap.keyAt(i);
   1078                 postSingleTorchUpdate(callback, handler, id, status);
   1079             }
   1080         } // onTorchStatusChangedLocked
   1081 
   1082         /**
   1083          * Register a callback to be notified about camera device availability with the
   1084          * global listener singleton.
   1085          *
   1086          * @param callback the new callback to send camera availability notices to
   1087          * @param handler The handler on which the callback should be invoked. May not be null.
   1088          */
   1089         public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
   1090             synchronized (mLock) {
   1091                 connectCameraServiceLocked();
   1092 
   1093                 Handler oldHandler = mCallbackMap.put(callback, handler);
   1094                 // For new callbacks, provide initial availability information
   1095                 if (oldHandler == null) {
   1096                     updateCallbackLocked(callback, handler);
   1097                 }
   1098 
   1099                 // If not connected to camera service, schedule a reconnect to camera service.
   1100                 if (mCameraService == null) {
   1101                     scheduleCameraServiceReconnectionLocked();
   1102                 }
   1103             }
   1104         }
   1105 
   1106         /**
   1107          * Remove a previously-added callback; the callback will no longer receive connection and
   1108          * disconnection callbacks, and is no longer referenced by the global listener singleton.
   1109          *
   1110          * @param callback The callback to remove from the notification list
   1111          */
   1112         public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
   1113             synchronized (mLock) {
   1114                 mCallbackMap.remove(callback);
   1115             }
   1116         }
   1117 
   1118         public void registerTorchCallback(TorchCallback callback, Handler handler) {
   1119             synchronized(mLock) {
   1120                 connectCameraServiceLocked();
   1121 
   1122                 Handler oldHandler = mTorchCallbackMap.put(callback, handler);
   1123                 // For new callbacks, provide initial torch information
   1124                 if (oldHandler == null) {
   1125                     updateTorchCallbackLocked(callback, handler);
   1126                 }
   1127 
   1128                 // If not connected to camera service, schedule a reconnect to camera service.
   1129                 if (mCameraService == null) {
   1130                     scheduleCameraServiceReconnectionLocked();
   1131                 }
   1132             }
   1133         }
   1134 
   1135         public void unregisterTorchCallback(TorchCallback callback) {
   1136             synchronized(mLock) {
   1137                 mTorchCallbackMap.remove(callback);
   1138             }
   1139         }
   1140 
   1141         /**
   1142          * Callback from camera service notifying the process about camera availability changes
   1143          */
   1144         @Override
   1145         public void onStatusChanged(int status, String cameraId) throws RemoteException {
   1146             synchronized(mLock) {
   1147                 onStatusChangedLocked(status, cameraId);
   1148             }
   1149         }
   1150 
   1151         @Override
   1152         public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
   1153             synchronized (mLock) {
   1154                 onTorchStatusChangedLocked(status, cameraId);
   1155             }
   1156         }
   1157 
   1158         /**
   1159          * Try to connect to camera service after some delay if any client registered camera
   1160          * availability callback or torch status callback.
   1161          */
   1162         private void scheduleCameraServiceReconnectionLocked() {
   1163             final Handler handler;
   1164 
   1165             if (mCallbackMap.size() > 0) {
   1166                 handler = mCallbackMap.valueAt(0);
   1167             } else if (mTorchCallbackMap.size() > 0) {
   1168                 handler = mTorchCallbackMap.valueAt(0);
   1169             } else {
   1170                 // Not necessary to reconnect camera service if no client registers a callback.
   1171                 return;
   1172             }
   1173 
   1174             if (DEBUG) {
   1175                 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS +
   1176                         " ms");
   1177             }
   1178 
   1179             handler.postDelayed(
   1180                     new Runnable() {
   1181                         @Override
   1182                         public void run() {
   1183                             ICameraService cameraService = getCameraService();
   1184                             if (cameraService == null) {
   1185                                 synchronized(mLock) {
   1186                                     if (DEBUG) {
   1187                                         Log.v(TAG, "Reconnecting Camera Service failed.");
   1188                                     }
   1189                                     scheduleCameraServiceReconnectionLocked();
   1190                                 }
   1191                             }
   1192                         }
   1193                     },
   1194                     CAMERA_SERVICE_RECONNECT_DELAY_MS);
   1195         }
   1196 
   1197         /**
   1198          * Listener for camera service death.
   1199          *
   1200          * <p>The camera service isn't supposed to die under any normal circumstances, but can be
   1201          * turned off during debug, or crash due to bugs.  So detect that and null out the interface
   1202          * object, so that the next calls to the manager can try to reconnect.</p>
   1203          */
   1204         public void binderDied() {
   1205             synchronized(mLock) {
   1206                 // Only do this once per service death
   1207                 if (mCameraService == null) return;
   1208 
   1209                 mCameraService = null;
   1210 
   1211                 // Tell listeners that the cameras and torch modes are unavailable and schedule a
   1212                 // reconnection to camera service. When camera service is reconnected, the camera
   1213                 // and torch statuses will be updated.
   1214                 for (int i = 0; i < mDeviceStatus.size(); i++) {
   1215                     String cameraId = mDeviceStatus.keyAt(i);
   1216                     onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId);
   1217                 }
   1218                 for (int i = 0; i < mTorchStatus.size(); i++) {
   1219                     String cameraId = mTorchStatus.keyAt(i);
   1220                     onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE,
   1221                             cameraId);
   1222                 }
   1223 
   1224                 scheduleCameraServiceReconnectionLocked();
   1225             }
   1226         }
   1227 
   1228     } // CameraManagerGlobal
   1229 
   1230 } // CameraManager
   1231