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