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