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.SystemApi;
24 import android.annotation.SystemService;
25 import android.annotation.TestApi;
26 import android.content.Context;
27 import android.hardware.CameraStatus;
28 import android.hardware.ICameraService;
29 import android.hardware.ICameraServiceListener;
30 import android.hardware.camera2.impl.CameraDeviceImpl;
31 import android.hardware.camera2.impl.CameraInjectionSessionImpl;
32 import android.hardware.camera2.impl.CameraMetadataNative;
33 import android.hardware.camera2.params.ExtensionSessionConfiguration;
34 import android.hardware.camera2.params.SessionConfiguration;
35 import android.hardware.camera2.params.StreamConfiguration;
36 import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
37 import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
38 import android.hardware.devicestate.DeviceStateManager;
39 import android.hardware.display.DisplayManager;
40 import android.os.Binder;
41 import android.os.DeadObjectException;
42 import android.os.Handler;
43 import android.os.HandlerExecutor;
44 import android.os.HandlerThread;
45 import android.os.IBinder;
46 import android.os.RemoteException;
47 import android.os.ServiceManager;
48 import android.os.ServiceSpecificException;
49 import android.os.SystemProperties;
50 import android.util.ArrayMap;
51 import android.util.ArraySet;
52 import android.util.Log;
53 import android.util.Size;
54 import android.view.Display;
55 
56 import com.android.internal.annotations.GuardedBy;
57 import com.android.internal.util.ArrayUtils;
58 
59 import java.lang.ref.WeakReference;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.Comparator;
63 import java.util.HashMap;
64 import java.util.Map;
65 import java.util.Set;
66 import java.util.concurrent.Executor;
67 import java.util.concurrent.Executors;
68 import java.util.concurrent.RejectedExecutionException;
69 import java.util.concurrent.ScheduledExecutorService;
70 import java.util.concurrent.TimeUnit;
71 
72 /**
73  * <p>A system service manager for detecting, characterizing, and connecting to
74  * {@link CameraDevice CameraDevices}.</p>
75  *
76  * <p>For more details about communicating with camera devices, read the Camera
77  * developer guide or the {@link android.hardware.camera2 camera2}
78  * package documentation.</p>
79  */
80 @SystemService(Context.CAMERA_SERVICE)
81 public final class CameraManager {
82 
83     private static final String TAG = "CameraManager";
84     private final boolean DEBUG = false;
85 
86     private static final int USE_CALLING_UID = -1;
87 
88     @SuppressWarnings("unused")
89     private static final int API_VERSION_1 = 1;
90     private static final int API_VERSION_2 = 2;
91 
92     private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0;
93     private static final int CAMERA_TYPE_ALL = 1;
94 
95     private ArrayList<String> mDeviceIdList;
96 
97     private final Context mContext;
98     private final Object mLock = new Object();
99 
100     /**
101      * @hide
102      */
CameraManager(Context context)103     public CameraManager(Context context) {
104         synchronized(mLock) {
105             mContext = context;
106         }
107 
108         mHandlerThread = new HandlerThread(TAG);
109         mHandlerThread.start();
110         mHandler = new Handler(mHandlerThread.getLooper());
111         mFoldStateListener = new FoldStateListener(context);
112         try {
113             context.getSystemService(DeviceStateManager.class)
114                     .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
115         } catch (IllegalStateException e) {
116             Log.v(TAG, "Failed to register device state listener!");
117             Log.v(TAG, "Device state dependent characteristics updates will not be functional!");
118             mHandlerThread.quitSafely();
119             mHandler = null;
120             mFoldStateListener = null;
121         }
122     }
123 
124     private HandlerThread mHandlerThread;
125     private Handler mHandler;
126     private FoldStateListener mFoldStateListener;
127     @GuardedBy("mLock")
128     private ArrayList<WeakReference<DeviceStateListener>> mDeviceStateListeners = new ArrayList<>();
129     private boolean mFoldedDeviceState;
130 
131     /**
132      * @hide
133      */
134     public interface DeviceStateListener {
onDeviceStateChanged(boolean folded)135         void onDeviceStateChanged(boolean folded);
136     }
137 
138     private final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
139         private final int[] mFoldedDeviceStates;
140 
FoldStateListener(Context context)141         public FoldStateListener(Context context) {
142             mFoldedDeviceStates = context.getResources().getIntArray(
143                     com.android.internal.R.array.config_foldedDeviceStates);
144         }
145 
handleStateChange(int state)146         private void handleStateChange(int state) {
147             boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
148             synchronized (mLock) {
149                 mFoldedDeviceState = folded;
150                 ArrayList<WeakReference<DeviceStateListener>> invalidListeners = new ArrayList<>();
151                 for (WeakReference<DeviceStateListener> listener : mDeviceStateListeners) {
152                     DeviceStateListener callback = listener.get();
153                     if (callback != null) {
154                         callback.onDeviceStateChanged(folded);
155                     } else {
156                         invalidListeners.add(listener);
157                     }
158                 }
159                 if (!invalidListeners.isEmpty()) {
160                     mDeviceStateListeners.removeAll(invalidListeners);
161                 }
162             }
163         }
164 
165         @Override
onBaseStateChanged(int state)166         public final void onBaseStateChanged(int state) {
167             handleStateChange(state);
168         }
169 
170         @Override
onStateChanged(int state)171         public final void onStateChanged(int state) {
172             handleStateChange(state);
173         }
174     }
175 
176     /**
177      * Register a {@link CameraCharacteristics} device state listener
178      *
179      * @param chars Camera characteristics that need to receive device state updates
180      *
181      * @hide
182      */
registerDeviceStateListener(@onNull CameraCharacteristics chars)183     public void registerDeviceStateListener(@NonNull CameraCharacteristics chars) {
184         synchronized (mLock) {
185             DeviceStateListener listener = chars.getDeviceStateListener();
186             listener.onDeviceStateChanged(mFoldedDeviceState);
187             if (mFoldStateListener != null) {
188                 mDeviceStateListeners.add(new WeakReference<>(listener));
189             }
190         }
191     }
192 
193     /**
194      * Return the list of currently connected camera devices by identifier, including
195      * cameras that may be in use by other camera API clients.
196      *
197      * <p>Non-removable cameras use integers starting at 0 for their
198      * identifiers, while removable cameras have a unique identifier for each
199      * individual device, even if they are the same model.</p>
200      *
201      * <p>This list doesn't contain physical cameras that can only be used as part of a logical
202      * multi-camera device.</p>
203      *
204      * @return The list of currently connected camera devices.
205      */
206     @NonNull
getCameraIdList()207     public String[] getCameraIdList() throws CameraAccessException {
208         return CameraManagerGlobal.get().getCameraIdList();
209     }
210 
211     /**
212      * Similar to getCameraIdList(). However, getCamerIdListNoLazy() necessarily communicates with
213      * cameraserver in order to get the list of camera ids. This is to faciliate testing since some
214      * camera ids may go 'offline' without callbacks from cameraserver because of changes in
215      * SYSTEM_CAMERA permissions (though this is not a changeable permission, tests may call
216      * adopt(drop)ShellPermissionIdentity() and effectively change their permissions). This call
217      * affects the camera ids returned by getCameraIdList() as well. Tests which do adopt shell
218      * permission identity should not mix getCameraIdList() and getCameraListNoLazyCalls().
219      */
220     /** @hide */
221     @TestApi
getCameraIdListNoLazy()222     public String[] getCameraIdListNoLazy() throws CameraAccessException {
223         return CameraManagerGlobal.get().getCameraIdListNoLazy();
224     }
225 
226     /**
227      * Return the set of combinations of currently connected camera device identifiers, which
228      * support configuring camera device sessions concurrently.
229      *
230      * <p>The devices in these combinations can be concurrently configured by the same
231      * client camera application. Using these camera devices concurrently by two different
232      * applications is not guaranteed to be supported, however.</p>
233      *
234      * <p>For concurrent operation, in chronological order :
235      * - Applications must first close any open cameras that have sessions configured, using
236      *   {@link CameraDevice#close}.
237      * - All camera devices intended to be operated concurrently, must be opened using
238      *   {@link #openCamera}, before configuring sessions on any of the camera devices.</p>
239      *
240      * <p>Each device in a combination, is guaranteed to support stream combinations which may be
241      * obtained by querying {@link #getCameraCharacteristics} for the key
242      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}.</p>
243      *
244      * <p>For concurrent operation, if a camera device has a non null zoom ratio range as specified
245      * by
246      * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE},
247      * its complete zoom ratio range may not apply. Applications can use
248      * {@link android.hardware.camera2.CaptureRequest#CONTROL_ZOOM_RATIO} >=1 and  <=
249      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM}
250      * during concurrent operation.
251      * <p>
252      *
253      * <p>The set of combinations may include camera devices that may be in use by other camera API
254      * clients.</p>
255      *
256      * <p>Concurrent camera extension sessions {@link CameraExtensionSession} are not currently
257      * supported.</p>
258      *
259      * <p>The set of combinations doesn't contain physical cameras that can only be used as
260      * part of a logical multi-camera device.</p>
261      *
262      * <p> If a new camera id becomes available through
263      * {@link AvailabilityCallback#onCameraUnavailable(String)}, clients can call
264      * this method to check if new combinations of camera ids which can stream concurrently are
265      * available.
266      *
267      * @return The set of combinations of currently connected camera devices, that may have
268      *         sessions configured concurrently. The set of combinations will be empty if no such
269      *         combinations are supported by the camera subsystem.
270      *
271      * @throws CameraAccessException if the camera device has been disconnected.
272      */
273     @NonNull
getConcurrentCameraIds()274     public Set<Set<String>> getConcurrentCameraIds() throws CameraAccessException {
275         return CameraManagerGlobal.get().getConcurrentCameraIds();
276     }
277 
278     /**
279      * Checks whether the provided set of camera devices and their corresponding
280      * {@link SessionConfiguration} can be configured concurrently.
281      *
282      * <p>This method performs a runtime check of the given {@link SessionConfiguration} and camera
283      * id combinations. The result confirms whether or not the passed session configurations can be
284      * successfully used to create camera capture sessions concurrently, on the given camera
285      * devices using {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
286      * </p>
287      *
288      * <p>The method can be called at any point before, during and after active capture sessions.
289      * It will not impact normal camera behavior in any way and must complete significantly
290      * faster than creating a regular or constrained capture session.</p>
291      *
292      * <p>Although this method is faster than creating a new capture session, it is not intended
293      * to be used for exploring the entire space of supported concurrent stream combinations. The
294      * available mandatory concurrent stream combinations may be obtained by querying
295      * {@link #getCameraCharacteristics} for the key
296      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}. </p>
297      *
298      * <p>Note that session parameters will be ignored and calls to
299      * {@link SessionConfiguration#setSessionParameters} are not required.</p>
300      *
301      * @return {@code true} if the given combination of session configurations and corresponding
302      *                      camera ids are concurrently supported by the camera sub-system,
303      *         {@code false} otherwise OR if the set of camera devices provided is not a subset of
304      *                       those returned by {@link #getConcurrentCameraIds}.
305      *
306      * @throws CameraAccessException if one of the camera devices queried is no longer connected.
307      *
308      */
309     @RequiresPermission(android.Manifest.permission.CAMERA)
isConcurrentSessionConfigurationSupported( @onNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)310     public boolean isConcurrentSessionConfigurationSupported(
311             @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)
312             throws CameraAccessException {
313         return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported(
314                 cameraIdAndSessionConfig, mContext.getApplicationInfo().targetSdkVersion);
315     }
316 
317     /**
318      * Register a callback to be notified about camera device availability.
319      *
320      * <p>Registering the same callback again will replace the handler with the
321      * new one provided.</p>
322      *
323      * <p>The first time a callback is registered, it is immediately called
324      * with the availability status of all currently known camera devices.</p>
325      *
326      * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera
327      * device is opened by any camera API client. As of API level 23, other camera API clients may
328      * still be able to open such a camera device, evicting the existing client if they have higher
329      * priority than the existing client of a camera device. See open() for more details.</p>
330      *
331      * <p>Since this callback will be registered with the camera service, remember to unregister it
332      * once it is no longer needed; otherwise the callback will continue to receive events
333      * indefinitely and it may prevent other resources from being released. Specifically, the
334      * callbacks will be invoked independently of the general activity lifecycle and independently
335      * of the state of individual CameraManager instances.</p>
336      *
337      * @param callback the new callback to send camera availability notices to
338      * @param handler The handler on which the callback should be invoked, or {@code null} to use
339      *             the current thread's {@link android.os.Looper looper}.
340      *
341      * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
342      *             no looper.
343      */
registerAvailabilityCallback(@onNull AvailabilityCallback callback, @Nullable Handler handler)344     public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
345             @Nullable Handler handler) {
346         CameraManagerGlobal.get().registerAvailabilityCallback(callback,
347                 CameraDeviceImpl.checkAndWrapHandler(handler));
348     }
349 
350     /**
351      * Register a callback to be notified about camera device availability.
352      *
353      * <p>The behavior of this method matches that of
354      * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)},
355      * except that it uses {@link java.util.concurrent.Executor} as an argument
356      * instead of {@link android.os.Handler}.</p>
357      *
358      * @param executor The executor which will be used to invoke the callback.
359      * @param callback the new callback to send camera availability notices to
360      *
361      * @throws IllegalArgumentException if the executor is {@code null}.
362      */
registerAvailabilityCallback(@onNull @allbackExecutor Executor executor, @NonNull AvailabilityCallback callback)363     public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
364             @NonNull AvailabilityCallback callback) {
365         if (executor == null) {
366             throw new IllegalArgumentException("executor was null");
367         }
368         CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor);
369     }
370 
371     /**
372      * Remove a previously-added callback; the callback will no longer receive connection and
373      * disconnection callbacks.
374      *
375      * <p>Removing a callback that isn't registered has no effect.</p>
376      *
377      * @param callback The callback to remove from the notification list
378      */
unregisterAvailabilityCallback(@onNull AvailabilityCallback callback)379     public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) {
380         CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
381     }
382 
383     /**
384      * Register a callback to be notified about torch mode status.
385      *
386      * <p>Registering the same callback again will replace the handler with the
387      * new one provided.</p>
388      *
389      * <p>The first time a callback is registered, it is immediately called
390      * with the torch mode status of all currently known camera devices with a flash unit.</p>
391      *
392      * <p>Since this callback will be registered with the camera service, remember to unregister it
393      * once it is no longer needed; otherwise the callback will continue to receive events
394      * indefinitely and it may prevent other resources from being released. Specifically, the
395      * callbacks will be invoked independently of the general activity lifecycle and independently
396      * of the state of individual CameraManager instances.</p>
397      *
398      * @param callback The new callback to send torch mode status to
399      * @param handler The handler on which the callback should be invoked, or {@code null} to use
400      *             the current thread's {@link android.os.Looper looper}.
401      *
402      * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
403      *             no looper.
404      */
registerTorchCallback(@onNull TorchCallback callback, @Nullable Handler handler)405     public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
406         CameraManagerGlobal.get().registerTorchCallback(callback,
407                 CameraDeviceImpl.checkAndWrapHandler(handler));
408     }
409 
410     /**
411      * Register a callback to be notified about torch mode status.
412      *
413      * <p>The behavior of this method matches that of
414      * {@link #registerTorchCallback(TorchCallback, Handler)},
415      * except that it uses {@link java.util.concurrent.Executor} as an argument
416      * instead of {@link android.os.Handler}.</p>
417      *
418      * @param executor The executor which will be used to invoke the callback
419      * @param callback The new callback to send torch mode status to
420      *
421      * @throws IllegalArgumentException if the executor is {@code null}.
422      */
registerTorchCallback(@onNull @allbackExecutor Executor executor, @NonNull TorchCallback callback)423     public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor,
424             @NonNull TorchCallback callback) {
425         if (executor == null) {
426             throw new IllegalArgumentException("executor was null");
427         }
428         CameraManagerGlobal.get().registerTorchCallback(callback, executor);
429     }
430 
431     /**
432      * Remove a previously-added callback; the callback will no longer receive torch mode status
433      * callbacks.
434      *
435      * <p>Removing a callback that isn't registered has no effect.</p>
436      *
437      * @param callback The callback to remove from the notification list
438      */
unregisterTorchCallback(@onNull TorchCallback callback)439     public void unregisterTorchCallback(@NonNull TorchCallback callback) {
440         CameraManagerGlobal.get().unregisterTorchCallback(callback);
441     }
442 
443     // TODO(b/147726300): Investigate how to support foldables/multi-display devices.
getDisplaySize()444     private Size getDisplaySize() {
445         Size ret = new Size(0, 0);
446 
447         try {
448             DisplayManager displayManager =
449                     (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
450             Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
451             if (display != null) {
452                 int width = display.getWidth();
453                 int height = display.getHeight();
454 
455                 if (height > width) {
456                     height = width;
457                     width = display.getHeight();
458                 }
459 
460                 ret = new Size(width, height);
461             } else {
462                 Log.e(TAG, "Invalid default display!");
463             }
464         } catch (Exception e) {
465             Log.e(TAG, "getDisplaySize Failed. " + e.toString());
466         }
467 
468         return ret;
469     }
470 
471     /**
472      * Get all physical cameras' multi-resolution stream configuration map
473      *
474      * <p>For a logical multi-camera, query the map between physical camera id and
475      * the physical camera's multi-resolution stream configuration. This map is in turn
476      * combined to form the logical camera's multi-resolution stream configuration map.</p>
477      *
478      * <p>For an ultra high resolution camera, directly use
479      * android.scaler.physicalCameraMultiResolutionStreamConfigurations as the camera device's
480      * multi-resolution stream configuration map.</p>
481      */
getPhysicalCameraMultiResolutionConfigs( String cameraId, CameraMetadataNative info, ICameraService cameraService)482     private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs(
483             String cameraId, CameraMetadataNative info, ICameraService cameraService)
484             throws CameraAccessException {
485         HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations =
486                 new HashMap<String, StreamConfiguration[]>();
487 
488         Boolean multiResolutionStreamSupported = info.get(
489                 CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED);
490         if (multiResolutionStreamSupported == null || !multiResolutionStreamSupported) {
491             return multiResolutionStreamConfigurations;
492         }
493 
494         // Query the characteristics of all physical sub-cameras, and combine the multi-resolution
495         // stream configurations. Alternatively, for ultra-high resolution camera, direclty use
496         // its multi-resolution stream configurations. Note that framework derived formats such as
497         // HEIC and DEPTH_JPEG aren't supported as multi-resolution input or output formats.
498         Set<String> physicalCameraIds = info.getPhysicalCameraIds();
499         if (physicalCameraIds.size() == 0 && info.isUltraHighResolutionSensor()) {
500             StreamConfiguration[] configs = info.get(CameraCharacteristics.
501                     SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS);
502             if (configs != null) {
503                 multiResolutionStreamConfigurations.put(cameraId, configs);
504             }
505             return multiResolutionStreamConfigurations;
506         }
507         try {
508             for (String physicalCameraId : physicalCameraIds) {
509                 CameraMetadataNative physicalCameraInfo =
510                         cameraService.getCameraCharacteristics(physicalCameraId,
511                                 mContext.getApplicationInfo().targetSdkVersion);
512                 StreamConfiguration[] configs = physicalCameraInfo.get(
513                         CameraCharacteristics.
514                                 SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS);
515                 if (configs != null) {
516                     multiResolutionStreamConfigurations.put(physicalCameraId, configs);
517                 }
518             }
519         } catch (RemoteException e) {
520             ServiceSpecificException sse = new ServiceSpecificException(
521                     ICameraService.ERROR_DISCONNECTED,
522                     "Camera service is currently unavailable");
523             throwAsPublicException(sse);
524         }
525 
526         return multiResolutionStreamConfigurations;
527     }
528 
529     /**
530      * <p>Query the capabilities of a camera device. These capabilities are
531      * immutable for a given camera.</p>
532      *
533      * <p>From API level 29, this function can also be used to query the capabilities of physical
534      * cameras that can only be used as part of logical multi-camera. These cameras cannot be
535      * opened directly via {@link #openCamera}</p>
536      *
537      * <p>Also starting with API level 29, while most basic camera information is still available
538      * even without the CAMERA permission, some values are not available to apps that do not hold
539      * that permission. The keys not available are listed by
540      * {@link CameraCharacteristics#getKeysNeedingPermission}.</p>
541      *
542      * @param cameraId The id of the camera device to query. This could be either a standalone
543      * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that
544      * can only used as part of a logical multi-camera.
545      * @return The properties of the given camera
546      *
547      * @throws IllegalArgumentException if the cameraId does not match any
548      *         known camera device.
549      * @throws CameraAccessException if the camera device has been disconnected.
550      *
551      * @see #getCameraIdList
552      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
553      */
554     @NonNull
getCameraCharacteristics(@onNull String cameraId)555     public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
556             throws CameraAccessException {
557         CameraCharacteristics characteristics = null;
558         if (CameraManagerGlobal.sCameraServiceDisabled) {
559             throw new IllegalArgumentException("No cameras available on device");
560         }
561         synchronized (mLock) {
562             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
563             if (cameraService == null) {
564                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
565                         "Camera service is currently unavailable");
566             }
567             try {
568                 Size displaySize = getDisplaySize();
569 
570                 CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId,
571                         mContext.getApplicationInfo().targetSdkVersion);
572                 try {
573                     info.setCameraId(Integer.parseInt(cameraId));
574                 } catch (NumberFormatException e) {
575                     Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
576                 }
577 
578                 boolean hasConcurrentStreams =
579                         CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
580                 info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
581                 info.setDisplaySize(displaySize);
582 
583                 Map<String, StreamConfiguration[]> multiResolutionSizeMap =
584                         getPhysicalCameraMultiResolutionConfigs(cameraId, info, cameraService);
585                 if (multiResolutionSizeMap.size() > 0) {
586                     info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap);
587                 }
588 
589                 characteristics = new CameraCharacteristics(info);
590             } catch (ServiceSpecificException e) {
591                 throwAsPublicException(e);
592             } catch (RemoteException e) {
593                 // Camera service died - act as if the camera was disconnected
594                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
595                         "Camera service is currently unavailable", e);
596             }
597         }
598         registerDeviceStateListener(characteristics);
599         return characteristics;
600     }
601 
602     /**
603      * <p>Query the camera extension capabilities of a camera device.</p>
604      *
605      * @param cameraId The id of the camera device to query. This must be a standalone
606      * camera ID which can be directly opened by {@link #openCamera}.
607      * @return The properties of the given camera
608      *
609      * @throws IllegalArgumentException if the cameraId does not match any
610      *         known camera device.
611      * @throws CameraAccessException if the camera device has been disconnected.
612      *
613      * @see CameraExtensionCharacteristics
614      * @see CameraDevice#createExtensionSession(ExtensionSessionConfiguration)
615      * @see CameraExtensionSession
616      */
617     @NonNull
getCameraExtensionCharacteristics( @onNull String cameraId)618     public CameraExtensionCharacteristics getCameraExtensionCharacteristics(
619             @NonNull String cameraId) throws CameraAccessException {
620         CameraCharacteristics chars = getCameraCharacteristics(cameraId);
621         return new CameraExtensionCharacteristics(mContext, cameraId, chars);
622     }
623 
getPhysicalIdToCharsMap( CameraCharacteristics chars)624     private Map<String, CameraCharacteristics> getPhysicalIdToCharsMap(
625             CameraCharacteristics chars) throws CameraAccessException {
626         HashMap<String, CameraCharacteristics> physicalIdsToChars =
627                 new HashMap<String, CameraCharacteristics>();
628         Set<String> physicalCameraIds = chars.getPhysicalCameraIds();
629         for (String physicalCameraId : physicalCameraIds) {
630             CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId);
631             physicalIdsToChars.put(physicalCameraId, physicalChars);
632         }
633         return physicalIdsToChars;
634     }
635 
636     /**
637      * Helper for opening a connection to a camera with the given ID.
638      *
639      * @param cameraId The unique identifier of the camera device to open
640      * @param callback The callback for the camera. Must not be null.
641      * @param executor The executor to invoke the callback with. Must not be null.
642      * @param uid      The UID of the application actually opening the camera.
643      *                 Must be USE_CALLING_UID unless the caller is a service
644      *                 that is trusted to open the device on behalf of an
645      *                 application and to forward the real UID.
646      *
647      * @throws CameraAccessException if the camera is disabled by device policy,
648      * too many camera devices are already open, or the cameraId does not match
649      * any currently available camera device.
650      *
651      * @throws SecurityException if the application does not have permission to
652      * access the camera
653      * @throws IllegalArgumentException if callback or handler is null.
654      * @return A handle to the newly-created camera device.
655      *
656      * @see #getCameraIdList
657      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
658      */
openCameraDeviceUserAsync(String cameraId, CameraDevice.StateCallback callback, Executor executor, final int uid, final int oomScoreOffset)659     private CameraDevice openCameraDeviceUserAsync(String cameraId,
660             CameraDevice.StateCallback callback, Executor executor, final int uid,
661             final int oomScoreOffset) throws CameraAccessException {
662         CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
663         CameraDevice device = null;
664         Map<String, CameraCharacteristics> physicalIdsToChars =
665                 getPhysicalIdToCharsMap(characteristics);
666         synchronized (mLock) {
667 
668             ICameraDeviceUser cameraUser = null;
669             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
670                     new android.hardware.camera2.impl.CameraDeviceImpl(
671                         cameraId,
672                         callback,
673                         executor,
674                         characteristics,
675                         physicalIdsToChars,
676                         mContext.getApplicationInfo().targetSdkVersion,
677                         mContext);
678 
679             ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
680 
681             try {
682                 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
683                 if (cameraService == null) {
684                     throw new ServiceSpecificException(
685                         ICameraService.ERROR_DISCONNECTED,
686                         "Camera service is currently unavailable");
687                 }
688                 cameraUser = cameraService.connectDevice(callbacks, cameraId,
689                     mContext.getOpPackageName(),  mContext.getAttributionTag(), uid,
690                     oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion);
691             } catch (ServiceSpecificException e) {
692                 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
693                     throw new AssertionError("Should've gone down the shim path");
694                 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
695                         e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
696                         e.errorCode == ICameraService.ERROR_DISABLED ||
697                         e.errorCode == ICameraService.ERROR_DISCONNECTED ||
698                         e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
699                     // Received one of the known connection errors
700                     // The remote camera device cannot be connected to, so
701                     // set the local camera to the startup error state
702                     deviceImpl.setRemoteFailure(e);
703 
704                     if (e.errorCode == ICameraService.ERROR_DISABLED ||
705                             e.errorCode == ICameraService.ERROR_DISCONNECTED ||
706                             e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
707                         // Per API docs, these failures call onError and throw
708                         throwAsPublicException(e);
709                     }
710                 } else {
711                     // Unexpected failure - rethrow
712                     throwAsPublicException(e);
713                 }
714             } catch (RemoteException e) {
715                 // Camera service died - act as if it's a CAMERA_DISCONNECTED case
716                 ServiceSpecificException sse = new ServiceSpecificException(
717                     ICameraService.ERROR_DISCONNECTED,
718                     "Camera service is currently unavailable");
719                 deviceImpl.setRemoteFailure(sse);
720                 throwAsPublicException(sse);
721             }
722 
723             // TODO: factor out callback to be non-nested, then move setter to constructor
724             // For now, calling setRemoteDevice will fire initial
725             // onOpened/onUnconfigured callbacks.
726             // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
727             // cameraUser dies during setup.
728             deviceImpl.setRemoteDevice(cameraUser);
729             device = deviceImpl;
730         }
731 
732         return device;
733     }
734 
735     /**
736      * Open a connection to a camera with the given ID.
737      *
738      * <p>Use {@link #getCameraIdList} to get the list of available camera
739      * devices. Note that even if an id is listed, open may fail if the device
740      * is disconnected between the calls to {@link #getCameraIdList} and
741      * {@link #openCamera}, or if a higher-priority camera API client begins using the
742      * camera device.</p>
743      *
744      * <p>As of API level 23, devices for which the
745      * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the
746      * device being in use by a lower-priority, background camera API client can still potentially
747      * be opened by calling this method when the calling camera API client has a higher priority
748      * than the current camera API client using this device.  In general, if the top, foreground
749      * activity is running within your application process, your process will be given the highest
750      * priority when accessing the camera, and this method will succeed even if the camera device is
751      * in use by another camera API client. Any lower-priority application that loses control of the
752      * camera in this way will receive an
753      * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.
754      * Opening the same camera ID twice in the same application will similarly cause the
755      * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback
756      * being fired for the {@link CameraDevice} from the first open call and all ongoing tasks
757      * being droppped.</p>
758      *
759      * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
760      * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
761      * for operation by calling {@link CameraDevice#createCaptureSession} and
762      * {@link CameraDevice#createCaptureRequest}</p>
763      *
764      * <p>Before API level 30, when the application tries to open multiple {@link CameraDevice} of
765      * different IDs and the device does not support opening such combination, either the
766      * {@link #openCamera} will fail and throw a {@link CameraAccessException} or one or more of
767      * already opened {@link CameraDevice} will be disconnected and receive
768      * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback. Which
769      * behavior will happen depends on the device implementation and can vary on different devices.
770      * Starting in API level 30, if the device does not support the combination of cameras being
771      * opened, it is guaranteed the {@link #openCamera} call will fail and none of existing
772      * {@link CameraDevice} will be disconnected.</p>
773      *
774      * <!--
775      * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
776      * on the returned CameraDevice instance will be queued up until the device startup has
777      * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is
778      * called. The pending operations are then processed in order.</p>
779      * -->
780      * <p>If the camera becomes disconnected during initialization
781      * after this function call returns,
782      * {@link CameraDevice.StateCallback#onDisconnected} with a
783      * {@link CameraDevice} in the disconnected state (and
784      * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p>
785      *
786      * <p>If opening the camera device fails, then the device callback's
787      * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent
788      * calls on the camera device will throw a {@link CameraAccessException}.</p>
789      *
790      * @param cameraId
791      *             The unique identifier of the camera device to open
792      * @param callback
793      *             The callback which is invoked once the camera is opened
794      * @param handler
795      *             The handler on which the callback should be invoked, or
796      *             {@code null} to use the current thread's {@link android.os.Looper looper}.
797      *
798      * @throws CameraAccessException if the camera is disabled by device policy,
799      * has been disconnected, is being used by a higher-priority camera API client, or the device
800      * has reached its maximal resource and cannot open this camera device.
801      *
802      * @throws IllegalArgumentException if cameraId or the callback was null,
803      * or the cameraId does not match any currently or previously available
804      * camera device returned by {@link #getCameraIdList}.
805      *
806      * @throws SecurityException if the application does not have permission to
807      * access the camera
808      *
809      * @see #getCameraIdList
810      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
811      */
812     @RequiresPermission(android.Manifest.permission.CAMERA)
openCamera(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)813     public void openCamera(@NonNull String cameraId,
814             @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
815             throws CameraAccessException {
816 
817         openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
818                 USE_CALLING_UID);
819     }
820 
821     /**
822      * Open a connection to a camera with the given ID.
823      *
824      * <p>The behavior of this method matches that of
825      * {@link #openCamera(String, StateCallback, Handler)}, except that it uses
826      * {@link java.util.concurrent.Executor} as an argument instead of
827      * {@link android.os.Handler}.</p>
828      *
829      * @param cameraId
830      *             The unique identifier of the camera device to open
831      * @param executor
832      *             The executor which will be used when invoking the callback.
833      * @param callback
834      *             The callback which is invoked once the camera is opened
835      *
836      * @throws CameraAccessException if the camera is disabled by device policy,
837      * has been disconnected, or is being used by a higher-priority camera API client.
838      *
839      * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
840      * or the cameraId does not match any currently or previously available
841      * camera device.
842      *
843      * @throws SecurityException if the application does not have permission to
844      * access the camera
845      *
846      * @see #getCameraIdList
847      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
848      */
849     @RequiresPermission(android.Manifest.permission.CAMERA)
openCamera(@onNull String cameraId, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)850     public void openCamera(@NonNull String cameraId,
851             @NonNull @CallbackExecutor Executor executor,
852             @NonNull final CameraDevice.StateCallback callback)
853             throws CameraAccessException {
854         if (executor == null) {
855             throw new IllegalArgumentException("executor was null");
856         }
857         openCameraForUid(cameraId, callback, executor, USE_CALLING_UID);
858     }
859 
860     /**
861      * Open a connection to a camera with the given ID. Also specify what oom score must be offset
862      * by cameraserver for this client. This api can be useful for system
863      * components which want to assume a lower priority (for camera arbitration) than other clients
864      * which it might contend for camera devices with. Increasing the oom score of a client reduces
865      * its priority when the camera framework manages camera arbitration.
866      * Considering typical use cases:
867      *
868      * 1) oom score(apps hosting activities visible to the user) - oom score(of a foreground app)
869      *    is approximately 100.
870      *
871      * 2) The oom score (process which hosts components which that are perceptible to the user /
872      *    native vendor camera clients) - oom (foreground app) is approximately 200.
873      *
874      * 3) The oom score (process which is cached hosting activities not visible) - oom (foreground
875      *    app) is approximately 999.
876      *
877      * <p>The behavior of this method matches that of
878      * {@link #openCamera(String, StateCallback, Handler)}, except that it uses
879      * {@link java.util.concurrent.Executor} as an argument instead of
880      * {@link android.os.Handler}.</p>
881      *
882      * @param cameraId
883      *             The unique identifier of the camera device to open
884      * @param executor
885      *             The executor which will be used when invoking the callback.
886      * @param callback
887      *             The callback which is invoked once the camera is opened
888      * @param oomScoreOffset
889      *             The value by which the oom score of this client must be offset by the camera
890      *             framework in order to assist it with camera arbitration. This value must be > 0.
891      *             A positive value lowers the priority of this camera client compared to what the
892      *             camera framework would have originally seen.
893      *
894      * @throws CameraAccessException if the camera is disabled by device policy,
895      * has been disconnected, or is being used by a higher-priority camera API client.
896      *
897      * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
898      * or the cameraId does not match any currently or previously available
899      * camera device.
900      *
901      * @throws SecurityException if the application does not have permission to
902      * access the camera
903      *
904      * @see #getCameraIdList
905      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
906      *
907      * @hide
908      */
909     @SystemApi
910     @TestApi
911     @RequiresPermission(allOf = {
912             android.Manifest.permission.SYSTEM_CAMERA,
913             android.Manifest.permission.CAMERA,
914     })
openCamera(@onNull String cameraId, int oomScoreOffset, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)915     public void openCamera(@NonNull String cameraId, int oomScoreOffset,
916             @NonNull @CallbackExecutor Executor executor,
917             @NonNull final CameraDevice.StateCallback callback) throws CameraAccessException {
918         if (executor == null) {
919             throw new IllegalArgumentException("executor was null");
920         }
921         if (oomScoreOffset < 0) {
922             throw new IllegalArgumentException(
923                     "oomScoreOffset < 0, cannot increase priority of camera client");
924         }
925         openCameraForUid(cameraId, callback, executor, USE_CALLING_UID, oomScoreOffset);
926     }
927 
928     /**
929      * Open a connection to a camera with the given ID, on behalf of another application
930      * specified by clientUid. Also specify the minimum oom score and process state the application
931      * should have, as seen by the cameraserver.
932      *
933      * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
934      * the caller to specify the UID to use for permission/etc verification. This can only be
935      * done by services trusted by the camera subsystem to act on behalf of applications and
936      * to forward the real UID.</p>
937      *
938      * @param clientUid
939      *             The UID of the application on whose behalf the camera is being opened.
940      *             Must be USE_CALLING_UID unless the caller is a trusted service.
941      * @param oomScoreOffset
942      *             The minimum oom score that cameraservice must see for this client.
943      * @hide
944      */
openCameraForUid(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid, int oomScoreOffset)945     public void openCameraForUid(@NonNull String cameraId,
946             @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
947             int clientUid, int oomScoreOffset) throws CameraAccessException {
948 
949         if (cameraId == null) {
950             throw new IllegalArgumentException("cameraId was null");
951         } else if (callback == null) {
952             throw new IllegalArgumentException("callback was null");
953         }
954         if (CameraManagerGlobal.sCameraServiceDisabled) {
955             throw new IllegalArgumentException("No cameras available on device");
956         }
957 
958         openCameraDeviceUserAsync(cameraId, callback, executor, clientUid, oomScoreOffset);
959     }
960 
961     /**
962      * Open a connection to a camera with the given ID, on behalf of another application
963      * specified by clientUid.
964      *
965      * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
966      * the caller to specify the UID to use for permission/etc verification. This can only be
967      * done by services trusted by the camera subsystem to act on behalf of applications and
968      * to forward the real UID.</p>
969      *
970      * @param clientUid
971      *             The UID of the application on whose behalf the camera is being opened.
972      *             Must be USE_CALLING_UID unless the caller is a trusted service.
973      *
974      * @hide
975      */
openCameraForUid(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid)976     public void openCameraForUid(@NonNull String cameraId,
977             @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
978             int clientUid) throws CameraAccessException {
979             openCameraForUid(cameraId, callback, executor, clientUid, /*oomScoreOffset*/0);
980     }
981 
982     /**
983      * Set the flash unit's torch mode of the camera of the given ID without opening the camera
984      * device.
985      *
986      * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use
987      * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit.
988      * Note that even if a camera device has a flash unit, turning on the torch mode may fail
989      * if the camera device or other camera resources needed to turn on the torch mode are in use.
990      * </p>
991      *
992      * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully,
993      * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked.
994      * However, even if turning on the torch mode is successful, the application does not have the
995      * exclusive ownership of the flash unit or the camera device. The torch mode will be turned
996      * off and becomes unavailable when the camera device that the flash unit belongs to becomes
997      * unavailable or when other camera resources to keep the torch on become unavailable (
998      * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also,
999      * other applications are free to call {@link #setTorchMode} to turn off the torch mode (
1000      * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest
1001      * application that turned on the torch mode exits, the torch mode will be turned off.
1002      *
1003      * @param cameraId
1004      *             The unique identifier of the camera device that the flash unit belongs to.
1005      * @param enabled
1006      *             The desired state of the torch mode for the target camera device. Set to
1007      *             {@code true} to turn on the torch mode. Set to {@code false} to turn off the
1008      *             torch mode.
1009      *
1010      * @throws CameraAccessException if it failed to access the flash unit.
1011      *             {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
1012      *             is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
1013      *             other camera resources needed to turn on the torch mode are in use.
1014      *             {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera
1015      *             service is not available.
1016      *
1017      * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
1018      *             or previously available camera device, or the camera device doesn't have a
1019      *             flash unit.
1020      */
setTorchMode(@onNull String cameraId, boolean enabled)1021     public void setTorchMode(@NonNull String cameraId, boolean enabled)
1022             throws CameraAccessException {
1023         if (CameraManagerGlobal.sCameraServiceDisabled) {
1024             throw new IllegalArgumentException("No cameras available on device");
1025         }
1026         CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
1027     }
1028 
1029     /**
1030      * A callback for camera devices becoming available or unavailable to open.
1031      *
1032      * <p>Cameras become available when they are no longer in use, or when a new
1033      * removable camera is connected. They become unavailable when some
1034      * application or service starts using a camera, or when a removable camera
1035      * is disconnected.</p>
1036      *
1037      * <p>Extend this callback and pass an instance of the subclass to
1038      * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability
1039      * changes.</p>
1040      *
1041      * @see #registerAvailabilityCallback
1042      */
1043     public static abstract class AvailabilityCallback {
1044 
1045         /**
1046          * A new camera has become available to use.
1047          *
1048          * <p>The default implementation of this method does nothing.</p>
1049          *
1050          * @param cameraId The unique identifier of the new camera.
1051          */
onCameraAvailable(@onNull String cameraId)1052         public void onCameraAvailable(@NonNull String cameraId) {
1053             // default empty implementation
1054         }
1055 
1056         /**
1057          * A previously-available camera has become unavailable for use.
1058          *
1059          * <p>If an application had an active CameraDevice instance for the
1060          * now-disconnected camera, that application will receive a
1061          * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p>
1062          *
1063          * <p>The default implementation of this method does nothing.</p>
1064          *
1065          * @param cameraId The unique identifier of the disconnected camera.
1066          */
onCameraUnavailable(@onNull String cameraId)1067         public void onCameraUnavailable(@NonNull String cameraId) {
1068             // default empty implementation
1069         }
1070 
1071         /**
1072          * Called whenever camera access priorities change.
1073          *
1074          * <p>Notification that camera access priorities have changed and the camera may
1075          * now be openable. An application that was previously denied camera access due to
1076          * a higher-priority user already using the camera, or that was disconnected from an
1077          * active camera session due to a higher-priority user trying to open the camera,
1078          * should try to open the camera again if it still wants to use it.  Note that
1079          * multiple applications may receive this callback at the same time, and only one of
1080          * them will succeed in opening the camera in practice, depending on exact access
1081          * priority levels and timing. This method is useful in cases where multiple
1082          * applications may be in the resumed state at the same time, and the user switches
1083          * focus between them, or if the current camera-using application moves between
1084          * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera
1085          * available/unavailable callbacks will not be invoked, but another application may
1086          * now have higher priority for camera access than the current camera-using
1087          * application.</p>
1088          *
1089          * <p>The default implementation of this method does nothing.</p>
1090          *
1091          */
onCameraAccessPrioritiesChanged()1092         public void onCameraAccessPrioritiesChanged() {
1093             // default empty implementation
1094         }
1095 
1096         /**
1097          * A physical camera has become available for use again.
1098          *
1099          * <p>By default, all of the physical cameras of a logical multi-camera are
1100          * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
1101          * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
1102          * multi-camera is invoked. However, if some specific physical cameras are unavailable
1103          * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
1104          * {@link #onCameraAvailable}.</p>
1105          *
1106          * <p>The default implementation of this method does nothing.</p>
1107          *
1108          * @param cameraId The unique identifier of the logical multi-camera.
1109          * @param physicalCameraId The unique identifier of the physical camera.
1110          *
1111          * @see #onCameraAvailable
1112          * @see #onPhysicalCameraUnavailable
1113          */
onPhysicalCameraAvailable(@onNull String cameraId, @NonNull String physicalCameraId)1114         public void onPhysicalCameraAvailable(@NonNull String cameraId,
1115                 @NonNull String physicalCameraId) {
1116             // default empty implementation
1117         }
1118 
1119         /**
1120          * A previously-available physical camera has become unavailable for use.
1121          *
1122          * <p>By default, all of the physical cameras of a logical multi-camera are
1123          * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
1124          * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
1125          * multi-camera is invoked. If some specific physical cameras are unavailable
1126          * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
1127          * {@link #onCameraAvailable}.</p>
1128          *
1129          * <p>The default implementation of this method does nothing.</p>
1130          *
1131          * @param cameraId The unique identifier of the logical multi-camera.
1132          * @param physicalCameraId The unique identifier of the physical camera.
1133          *
1134          * @see #onCameraAvailable
1135          * @see #onPhysicalCameraAvailable
1136          */
onPhysicalCameraUnavailable(@onNull String cameraId, @NonNull String physicalCameraId)1137         public void onPhysicalCameraUnavailable(@NonNull String cameraId,
1138                 @NonNull String physicalCameraId) {
1139             // default empty implementation
1140         }
1141 
1142         /**
1143          * A camera device has been opened by an application.
1144          *
1145          * <p>The default implementation of this method does nothing.</p>
1146          *    android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER is required to receive this
1147          *    callback
1148          * @param cameraId The unique identifier of the camera opened.
1149          * @param packageId The package Id of the application opening the camera.
1150          *
1151          * @see #onCameraClosed
1152          * @hide
1153          */
1154         @SystemApi
1155         @TestApi
1156         @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER)
onCameraOpened(@onNull String cameraId, @NonNull String packageId)1157         public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) {
1158             // default empty implementation
1159         }
1160 
1161         /**
1162          * A previously-opened camera has been closed.
1163          *
1164          * <p>The default implementation of this method does nothing.</p>
1165          *    android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER is required to receive this
1166          *    callback.
1167          * @param cameraId The unique identifier of the closed camera.
1168          * @hide
1169          */
1170         @SystemApi
1171         @TestApi
1172         @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER)
onCameraClosed(@onNull String cameraId)1173         public void onCameraClosed(@NonNull String cameraId) {
1174             // default empty implementation
1175         }
1176     }
1177 
1178     /**
1179      * A callback for camera flash torch modes becoming unavailable, disabled, or enabled.
1180      *
1181      * <p>The torch mode becomes unavailable when the camera device it belongs to becomes
1182      * unavailable or other camera resources it needs become busy due to other higher priority
1183      * camera activities. The torch mode becomes disabled when it was turned off or when the camera
1184      * device it belongs to is no longer in use and other camera resources it needs are no longer
1185      * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to
1186      * turn off the camera's torch mode, or when an application turns on another camera's torch mode
1187      * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes
1188      * enabled when it is turned on via {@link #setTorchMode}.</p>
1189      *
1190      * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled
1191      * or enabled state.</p>
1192      *
1193      * <p>Extend this callback and pass an instance of the subclass to
1194      * {@link CameraManager#registerTorchCallback} to be notified of such status changes.
1195      * </p>
1196      *
1197      * @see #registerTorchCallback
1198      */
1199     public static abstract class TorchCallback {
1200         /**
1201          * A camera's torch mode has become unavailable to set via {@link #setTorchMode}.
1202          *
1203          * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be
1204          * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is
1205          * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or
1206          * enabled state again.</p>
1207          *
1208          * <p>The default implementation of this method does nothing.</p>
1209          *
1210          * @param cameraId The unique identifier of the camera whose torch mode has become
1211          *                 unavailable.
1212          */
onTorchModeUnavailable(@onNull String cameraId)1213         public void onTorchModeUnavailable(@NonNull String cameraId) {
1214             // default empty implementation
1215         }
1216 
1217         /**
1218          * A camera's torch mode has become enabled or disabled and can be changed via
1219          * {@link #setTorchMode}.
1220          *
1221          * <p>The default implementation of this method does nothing.</p>
1222          *
1223          * @param cameraId The unique identifier of the camera whose torch mode has been changed.
1224          *
1225          * @param enabled The state that the torch mode of the camera has been changed to.
1226          *                {@code true} when the torch mode has become on and available to be turned
1227          *                off. {@code false} when the torch mode has becomes off and available to
1228          *                be turned on.
1229          */
onTorchModeChanged(@onNull String cameraId, boolean enabled)1230         public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
1231             // default empty implementation
1232         }
1233     }
1234 
1235     /**
1236      * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces
1237      * into the correct public exceptions.
1238      *
1239      * @hide
1240      */
throwAsPublicException(Throwable t)1241     public static void throwAsPublicException(Throwable t) throws CameraAccessException {
1242         if (t instanceof ServiceSpecificException) {
1243             ServiceSpecificException e = (ServiceSpecificException) t;
1244             int reason = CameraAccessException.CAMERA_ERROR;
1245             switch(e.errorCode) {
1246                 case ICameraService.ERROR_DISCONNECTED:
1247                     reason = CameraAccessException.CAMERA_DISCONNECTED;
1248                     break;
1249                 case ICameraService.ERROR_DISABLED:
1250                     reason = CameraAccessException.CAMERA_DISABLED;
1251                     break;
1252                 case ICameraService.ERROR_CAMERA_IN_USE:
1253                     reason = CameraAccessException.CAMERA_IN_USE;
1254                     break;
1255                 case ICameraService.ERROR_MAX_CAMERAS_IN_USE:
1256                     reason = CameraAccessException.MAX_CAMERAS_IN_USE;
1257                     break;
1258                 case ICameraService.ERROR_DEPRECATED_HAL:
1259                     reason = CameraAccessException.CAMERA_DEPRECATED_HAL;
1260                     break;
1261                 case ICameraService.ERROR_ILLEGAL_ARGUMENT:
1262                 case ICameraService.ERROR_ALREADY_EXISTS:
1263                     throw new IllegalArgumentException(e.getMessage(), e);
1264                 case ICameraService.ERROR_PERMISSION_DENIED:
1265                     throw new SecurityException(e.getMessage(), e);
1266                 case ICameraService.ERROR_TIMED_OUT:
1267                 case ICameraService.ERROR_INVALID_OPERATION:
1268                 default:
1269                     reason = CameraAccessException.CAMERA_ERROR;
1270             }
1271             throw new CameraAccessException(reason, e.getMessage(), e);
1272         } else if (t instanceof DeadObjectException) {
1273             throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
1274                     "Camera service has died unexpectedly",
1275                     t);
1276         } else if (t instanceof RemoteException) {
1277             throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
1278                     " which should never happen.", t);
1279         } else if (t instanceof RuntimeException) {
1280             RuntimeException e = (RuntimeException) t;
1281             throw e;
1282         }
1283     }
1284 
1285     /**
1286      * Queries the camera service if a cameraId is a hidden physical camera that belongs to a
1287      * logical camera device.
1288      *
1289      * A hidden physical camera is a camera that cannot be opened by the application. But it
1290      * can be used as part of a logical camera.
1291      *
1292      * @param cameraId a non-{@code null} camera identifier
1293      * @return {@code true} if cameraId is a hidden physical camera device
1294      *
1295      * @hide
1296      */
isHiddenPhysicalCamera(String cameraId)1297     public static boolean isHiddenPhysicalCamera(String cameraId) {
1298         try {
1299             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
1300             // If no camera service, no support
1301             if (cameraService == null) return false;
1302 
1303             return cameraService.isHiddenPhysicalCamera(cameraId);
1304         } catch (RemoteException e) {
1305             // Camera service is now down, no support for any API level
1306         }
1307         return false;
1308     }
1309 
1310     /**
1311      * Inject the external camera to replace the internal camera session.
1312      *
1313      * <p>If injecting the external camera device fails, then the injection callback's
1314      * {@link CameraInjectionSession.InjectionStatusCallback#onInjectionError
1315      * onInjectionError} method will be called.</p>
1316      *
1317      * @param packageName   It scopes the injection to a particular app.
1318      * @param internalCamId The id of one of the physical or logical cameras on the phone.
1319      * @param externalCamId The id of one of the remote cameras that are provided by the dynamic
1320      *                      camera HAL.
1321      * @param executor      The executor which will be used when invoking the callback.
1322      * @param callback      The callback which is invoked once the external camera is injected.
1323      *
1324      * @throws CameraAccessException    If the camera device has been disconnected.
1325      *                                  {@link CameraAccessException#CAMERA_DISCONNECTED} will be
1326      *                                  thrown if camera service is not available.
1327      * @throws SecurityException        If the specific application that can cast to external
1328      *                                  devices does not have permission to inject the external
1329      *                                  camera.
1330      * @throws IllegalArgumentException If cameraId doesn't match any currently or previously
1331      *                                  available camera device or some camera functions might not
1332      *                                  work properly or the injection camera runs into a fatal
1333      *                                  error.
1334      * @hide
1335      */
1336     @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA)
injectCamera(@onNull String packageName, @NonNull String internalCamId, @NonNull String externalCamId, @NonNull @CallbackExecutor Executor executor, @NonNull CameraInjectionSession.InjectionStatusCallback callback)1337     public void injectCamera(@NonNull String packageName, @NonNull String internalCamId,
1338             @NonNull String externalCamId, @NonNull @CallbackExecutor Executor executor,
1339             @NonNull CameraInjectionSession.InjectionStatusCallback callback)
1340             throws CameraAccessException, SecurityException,
1341             IllegalArgumentException {
1342         if (CameraManagerGlobal.sCameraServiceDisabled) {
1343             throw new IllegalArgumentException("No cameras available on device");
1344         }
1345         ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
1346         if (cameraService == null) {
1347             throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
1348                     "Camera service is currently unavailable");
1349         }
1350         synchronized (mLock) {
1351             try {
1352                 CameraInjectionSessionImpl injectionSessionImpl =
1353                         new CameraInjectionSessionImpl(callback, executor);
1354                 ICameraInjectionCallback cameraInjectionCallback =
1355                         injectionSessionImpl.getCallback();
1356                 ICameraInjectionSession injectionSession = cameraService.injectCamera(packageName,
1357                         internalCamId, externalCamId, cameraInjectionCallback);
1358                 injectionSessionImpl.setRemoteInjectionSession(injectionSession);
1359             } catch (ServiceSpecificException e) {
1360                 throwAsPublicException(e);
1361             } catch (RemoteException e) {
1362                 // Camera service died - act as if it's a CAMERA_DISCONNECTED case
1363                 ServiceSpecificException sse = new ServiceSpecificException(
1364                         ICameraService.ERROR_DISCONNECTED,
1365                         "Camera service is currently unavailable");
1366                 throwAsPublicException(sse);
1367             }
1368         }
1369     }
1370 
1371     /**
1372      * A per-process global camera manager instance, to retain a connection to the camera service,
1373      * and to distribute camera availability notices to API-registered callbacks
1374      */
1375     private static final class CameraManagerGlobal extends ICameraServiceListener.Stub
1376             implements IBinder.DeathRecipient {
1377 
1378         private static final String TAG = "CameraManagerGlobal";
1379         private final boolean DEBUG = false;
1380 
1381         private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000;
1382 
1383         // Singleton instance
1384         private static final CameraManagerGlobal gCameraManager =
1385             new CameraManagerGlobal();
1386 
1387         /**
1388          * This must match the ICameraService definition
1389          */
1390         private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
1391 
1392         private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
1393         // Camera ID -> Status map
1394         private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
1395         // Camera ID -> (physical camera ID -> Status map)
1396         private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices =
1397                 new ArrayMap<String, ArrayList<String>>();
1398 
1399         private final Set<Set<String>> mConcurrentCameraIdCombinations =
1400                 new ArraySet<Set<String>>();
1401 
1402         // Registered availablility callbacks and their executors
1403         private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
1404             new ArrayMap<AvailabilityCallback, Executor>();
1405 
1406         // torch client binder to set the torch mode with.
1407         private Binder mTorchClientBinder = new Binder();
1408 
1409         // Camera ID -> Torch status map
1410         private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
1411 
1412         // Registered torch callbacks and their executors
1413         private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap =
1414                 new ArrayMap<TorchCallback, Executor>();
1415 
1416         private final Object mLock = new Object();
1417 
1418         // Access only through getCameraService to deal with binder death
1419         private ICameraService mCameraService;
1420 
1421         // Singleton, don't allow construction
CameraManagerGlobal()1422         private CameraManagerGlobal() { }
1423 
1424         public static final boolean sCameraServiceDisabled =
1425                 SystemProperties.getBoolean("config.disable_cameraservice", false);
1426 
get()1427         public static CameraManagerGlobal get() {
1428             return gCameraManager;
1429         }
1430 
1431         @Override
asBinder()1432         public IBinder asBinder() {
1433             return this;
1434         }
1435 
1436         /**
1437          * Return a best-effort ICameraService.
1438          *
1439          * <p>This will be null if the camera service is not currently available. If the camera
1440          * service has died since the last use of the camera service, will try to reconnect to the
1441          * service.</p>
1442          */
getCameraService()1443         public ICameraService getCameraService() {
1444             synchronized(mLock) {
1445                 connectCameraServiceLocked();
1446                 if (mCameraService == null && !sCameraServiceDisabled) {
1447                     Log.e(TAG, "Camera service is unavailable");
1448                 }
1449                 return mCameraService;
1450             }
1451         }
1452 
1453         /**
1454          * Connect to the camera service if it's available, and set up listeners.
1455          * If the service is already connected, do nothing.
1456          *
1457          * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
1458          */
connectCameraServiceLocked()1459         private void connectCameraServiceLocked() {
1460             // Only reconnect if necessary
1461             if (mCameraService != null || sCameraServiceDisabled) return;
1462 
1463             Log.i(TAG, "Connecting to camera service");
1464 
1465             IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
1466             if (cameraServiceBinder == null) {
1467                 // Camera service is now down, leave mCameraService as null
1468                 return;
1469             }
1470             try {
1471                 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
1472             } catch (RemoteException e) {
1473                 // Camera service is now down, leave mCameraService as null
1474                 return;
1475             }
1476 
1477             ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
1478 
1479             try {
1480                 CameraMetadataNative.setupGlobalVendorTagDescriptor();
1481             } catch (ServiceSpecificException e) {
1482                 handleRecoverableSetupErrors(e);
1483             }
1484 
1485             try {
1486                 CameraStatus[] cameraStatuses = cameraService.addListener(this);
1487                 for (CameraStatus c : cameraStatuses) {
1488                     onStatusChangedLocked(c.status, c.cameraId);
1489 
1490                     if (c.unavailablePhysicalCameras != null) {
1491                         for (String unavailPhysicalCamera : c.unavailablePhysicalCameras) {
1492                             onPhysicalCameraStatusChangedLocked(
1493                                     ICameraServiceListener.STATUS_NOT_PRESENT,
1494                                     c.cameraId, unavailPhysicalCamera);
1495                         }
1496                     }
1497                 }
1498                 mCameraService = cameraService;
1499             } catch(ServiceSpecificException e) {
1500                 // Unexpected failure
1501                 throw new IllegalStateException("Failed to register a camera service listener", e);
1502             } catch (RemoteException e) {
1503                 // Camera service is now down, leave mCameraService as null
1504             }
1505 
1506             try {
1507                 ConcurrentCameraIdCombination[] cameraIdCombinations =
1508                         cameraService.getConcurrentCameraIds();
1509                 for (ConcurrentCameraIdCombination comb : cameraIdCombinations) {
1510                     mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination());
1511                 }
1512             } catch (ServiceSpecificException e) {
1513                 // Unexpected failure
1514                 throw new IllegalStateException("Failed to get concurrent camera id combinations",
1515                         e);
1516             } catch (RemoteException e) {
1517                 // Camera service died in all probability
1518             }
1519         }
1520 
extractCameraIdListLocked()1521         private String[] extractCameraIdListLocked() {
1522             String[] cameraIds = null;
1523             int idCount = 0;
1524             for (int i = 0; i < mDeviceStatus.size(); i++) {
1525                 int status = mDeviceStatus.valueAt(i);
1526                 if (status == ICameraServiceListener.STATUS_NOT_PRESENT
1527                         || status == ICameraServiceListener.STATUS_ENUMERATING) continue;
1528                 idCount++;
1529             }
1530             cameraIds = new String[idCount];
1531             idCount = 0;
1532             for (int i = 0; i < mDeviceStatus.size(); i++) {
1533                 int status = mDeviceStatus.valueAt(i);
1534                 if (status == ICameraServiceListener.STATUS_NOT_PRESENT
1535                         || status == ICameraServiceListener.STATUS_ENUMERATING) continue;
1536                 cameraIds[idCount] = mDeviceStatus.keyAt(i);
1537                 idCount++;
1538             }
1539             return cameraIds;
1540         }
1541 
extractConcurrentCameraIdListLocked()1542         private Set<Set<String>> extractConcurrentCameraIdListLocked() {
1543             Set<Set<String>> concurrentCameraIds = new ArraySet<Set<String>>();
1544             for (Set<String> cameraIds : mConcurrentCameraIdCombinations) {
1545                 Set<String> extractedCameraIds = new ArraySet<String>();
1546                 for (String cameraId : cameraIds) {
1547                     // if the camera id status is NOT_PRESENT or ENUMERATING; skip the device.
1548                     // TODO: Would a device status NOT_PRESENT ever be in the map ? it gets removed
1549                     // in the callback anyway.
1550                     Integer status = mDeviceStatus.get(cameraId);
1551                     if (status == null) {
1552                         // camera id not present
1553                         continue;
1554                     }
1555                     if (status == ICameraServiceListener.STATUS_ENUMERATING
1556                             || status == ICameraServiceListener.STATUS_NOT_PRESENT) {
1557                         continue;
1558                     }
1559                     extractedCameraIds.add(cameraId);
1560                 }
1561                 concurrentCameraIds.add(extractedCameraIds);
1562             }
1563             return concurrentCameraIds;
1564         }
1565 
sortCameraIds(String[] cameraIds)1566         private static void sortCameraIds(String[] cameraIds) {
1567             // The sort logic must match the logic in
1568             // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
1569             Arrays.sort(cameraIds, new Comparator<String>() {
1570                     @Override
1571                     public int compare(String s1, String s2) {
1572                         int s1Int = 0, s2Int = 0;
1573                         try {
1574                             s1Int = Integer.parseInt(s1);
1575                         } catch (NumberFormatException e) {
1576                             s1Int = -1;
1577                         }
1578 
1579                         try {
1580                             s2Int = Integer.parseInt(s2);
1581                         } catch (NumberFormatException e) {
1582                             s2Int = -1;
1583                         }
1584 
1585                         // Uint device IDs first
1586                         if (s1Int >= 0 && s2Int >= 0) {
1587                             return s1Int - s2Int;
1588                         } else if (s1Int >= 0) {
1589                             return -1;
1590                         } else if (s2Int >= 0) {
1591                             return 1;
1592                         } else {
1593                             // Simple string compare if both id are not uint
1594                             return s1.compareTo(s2);
1595                         }
1596                     }});
1597 
1598         }
1599 
cameraStatusesContains(CameraStatus[] cameraStatuses, String id)1600         public static boolean cameraStatusesContains(CameraStatus[] cameraStatuses, String id) {
1601             for (CameraStatus c : cameraStatuses) {
1602                 if (c.cameraId.equals(id)) {
1603                     return true;
1604                 }
1605             }
1606             return false;
1607         }
1608 
getCameraIdListNoLazy()1609         public String[] getCameraIdListNoLazy() {
1610             if (sCameraServiceDisabled) {
1611                 return new String[] {};
1612             }
1613 
1614             CameraStatus[] cameraStatuses;
1615             ICameraServiceListener.Stub testListener = new ICameraServiceListener.Stub() {
1616                 @Override
1617                 public void onStatusChanged(int status, String id) throws RemoteException {
1618                 }
1619                 @Override
1620                 public void onPhysicalCameraStatusChanged(int status,
1621                         String id, String physicalId) throws RemoteException {
1622                 }
1623                 @Override
1624                 public void onTorchStatusChanged(int status, String id) throws RemoteException {
1625                 }
1626                 @Override
1627                 public void onCameraAccessPrioritiesChanged() {
1628                 }
1629                 @Override
1630                 public void onCameraOpened(String id, String clientPackageId) {
1631                 }
1632                 @Override
1633                 public void onCameraClosed(String id) {
1634                 }};
1635 
1636             String[] cameraIds = null;
1637             synchronized (mLock) {
1638                 connectCameraServiceLocked();
1639                 try {
1640                     // The purpose of the addListener, removeListener pair here is to get a fresh
1641                     // list of camera ids from cameraserver. We do this since for in test processes,
1642                     // changes can happen w.r.t non-changeable permissions (eg: SYSTEM_CAMERA
1643                     // permissions can be effectively changed by calling
1644                     // adopt(drop)ShellPermissionIdentity()).
1645                     // Camera devices, which have their discovery affected by these permission
1646                     // changes, will not have clients get callbacks informing them about these
1647                     // devices going offline (in real world scenarios, these permissions aren't
1648                     // changeable). Future calls to getCameraIdList() will reflect the changes in
1649                     // the camera id list after getCameraIdListNoLazy() is called.
1650                     // We need to remove the torch ids which may have been associated with the
1651                     // devices removed as well. This is the same situation.
1652                     cameraStatuses = mCameraService.addListener(testListener);
1653                     mCameraService.removeListener(testListener);
1654                     for (CameraStatus c : cameraStatuses) {
1655                         onStatusChangedLocked(c.status, c.cameraId);
1656                     }
1657                     Set<String> deviceCameraIds = mDeviceStatus.keySet();
1658                     ArrayList<String> deviceIdsToRemove = new ArrayList<String>();
1659                     for (String deviceCameraId : deviceCameraIds) {
1660                         // Its possible that a device id was removed without a callback notifying
1661                         // us. This may happen in case a process 'drops' system camera permissions
1662                         // (even though the permission isn't a changeable one, tests may call
1663                         // adoptShellPermissionIdentity() and then dropShellPermissionIdentity().
1664                         if (!cameraStatusesContains(cameraStatuses, deviceCameraId)) {
1665                             deviceIdsToRemove.add(deviceCameraId);
1666                         }
1667                     }
1668                     for (String id : deviceIdsToRemove) {
1669                         onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, id);
1670                         mTorchStatus.remove(id);
1671                     }
1672                 } catch (ServiceSpecificException e) {
1673                     // Unexpected failure
1674                     throw new IllegalStateException("Failed to register a camera service listener",
1675                             e);
1676                 } catch (RemoteException e) {
1677                     // Camera service is now down, leave mCameraService as null
1678                 }
1679                 cameraIds = extractCameraIdListLocked();
1680             }
1681             sortCameraIds(cameraIds);
1682             return cameraIds;
1683         }
1684 
1685         /**
1686          * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
1687          * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
1688          */
getCameraIdList()1689         public String[] getCameraIdList() {
1690             String[] cameraIds = null;
1691             synchronized (mLock) {
1692                 // Try to make sure we have an up-to-date list of camera devices.
1693                 connectCameraServiceLocked();
1694                 cameraIds = extractCameraIdListLocked();
1695             }
1696             sortCameraIds(cameraIds);
1697             return cameraIds;
1698         }
1699 
getConcurrentCameraIds()1700         public @NonNull Set<Set<String>> getConcurrentCameraIds() {
1701             Set<Set<String>> concurrentStreamingCameraIds = null;
1702             synchronized (mLock) {
1703                 // Try to make sure we have an up-to-date list of concurrent camera devices.
1704                 connectCameraServiceLocked();
1705                 concurrentStreamingCameraIds = extractConcurrentCameraIdListLocked();
1706             }
1707             // TODO: Some sort of sorting  ?
1708             return concurrentStreamingCameraIds;
1709         }
1710 
isConcurrentSessionConfigurationSupported( @onNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations, int targetSdkVersion)1711         public boolean isConcurrentSessionConfigurationSupported(
1712                 @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations,
1713                 int targetSdkVersion) throws CameraAccessException {
1714 
1715             if (cameraIdsAndSessionConfigurations == null) {
1716                 throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null");
1717             }
1718 
1719             int size = cameraIdsAndSessionConfigurations.size();
1720             if (size == 0) {
1721                 throw new IllegalArgumentException("camera id and session combination is empty");
1722             }
1723 
1724             synchronized (mLock) {
1725                 // Go through all the elements and check if the camera ids are valid at least /
1726                 // belong to one of the combinations returned by getConcurrentCameraIds()
1727                 boolean subsetFound = false;
1728                 for (Set<String> combination : mConcurrentCameraIdCombinations) {
1729                     if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) {
1730                         subsetFound = true;
1731                     }
1732                 }
1733                 if (!subsetFound) {
1734                     Log.v(TAG, "isConcurrentSessionConfigurationSupported called with a subset of"
1735                             + "camera ids not returned by getConcurrentCameraIds");
1736                     return false;
1737                 }
1738                 CameraIdAndSessionConfiguration [] cameraIdsAndConfigs =
1739                         new CameraIdAndSessionConfiguration[size];
1740                 int i = 0;
1741                 for (Map.Entry<String, SessionConfiguration> pair :
1742                         cameraIdsAndSessionConfigurations.entrySet()) {
1743                     cameraIdsAndConfigs[i] =
1744                             new CameraIdAndSessionConfiguration(pair.getKey(), pair.getValue());
1745                     i++;
1746                 }
1747                 try {
1748                     return mCameraService.isConcurrentSessionConfigurationSupported(
1749                             cameraIdsAndConfigs, targetSdkVersion);
1750                 } catch (ServiceSpecificException e) {
1751                    throwAsPublicException(e);
1752                 } catch (RemoteException e) {
1753                   // Camera service died - act as if the camera was disconnected
1754                   throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
1755                           "Camera service is currently unavailable", e);
1756                 }
1757             }
1758 
1759             return false;
1760         }
1761 
1762       /**
1763         * Helper function to find out if a camera id is in the set of combinations returned by
1764         * getConcurrentCameraIds()
1765         * @param cameraId the unique identifier of the camera device to query
1766         * @return Whether the camera device was found in the set of combinations returned by
1767         *         getConcurrentCameraIds
1768         */
cameraIdHasConcurrentStreamsLocked(String cameraId)1769         public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) {
1770             if (!mDeviceStatus.containsKey(cameraId)) {
1771                 // physical camera ids aren't advertised in concurrent camera id combinations.
1772                 if (DEBUG) {
1773                     Log.v(TAG, " physical camera id " + cameraId + " is hidden." +
1774                             " Available logical camera ids : " + mDeviceStatus.toString());
1775                 }
1776                 return false;
1777             }
1778             for (Set<String> comb : mConcurrentCameraIdCombinations) {
1779                 if (comb.contains(cameraId)) {
1780                     return true;
1781                 }
1782             }
1783             return false;
1784         }
1785 
setTorchMode(String cameraId, boolean enabled)1786         public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
1787             synchronized(mLock) {
1788 
1789                 if (cameraId == null) {
1790                     throw new IllegalArgumentException("cameraId was null");
1791                 }
1792 
1793                 ICameraService cameraService = getCameraService();
1794                 if (cameraService == null) {
1795                     throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
1796                         "Camera service is currently unavailable");
1797                 }
1798 
1799                 try {
1800                     cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder);
1801                 } catch(ServiceSpecificException e) {
1802                     throwAsPublicException(e);
1803                 } catch (RemoteException e) {
1804                     throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
1805                             "Camera service is currently unavailable");
1806                 }
1807             }
1808         }
1809 
handleRecoverableSetupErrors(ServiceSpecificException e)1810         private void handleRecoverableSetupErrors(ServiceSpecificException e) {
1811             switch (e.errorCode) {
1812                 case ICameraService.ERROR_DISCONNECTED:
1813                     Log.w(TAG, e.getMessage());
1814                     break;
1815                 default:
1816                     throw new IllegalStateException(e);
1817             }
1818         }
1819 
isAvailable(int status)1820         private boolean isAvailable(int status) {
1821             switch (status) {
1822                 case ICameraServiceListener.STATUS_PRESENT:
1823                     return true;
1824                 default:
1825                     return false;
1826             }
1827         }
1828 
validStatus(int status)1829         private boolean validStatus(int status) {
1830             switch (status) {
1831                 case ICameraServiceListener.STATUS_NOT_PRESENT:
1832                 case ICameraServiceListener.STATUS_PRESENT:
1833                 case ICameraServiceListener.STATUS_ENUMERATING:
1834                 case ICameraServiceListener.STATUS_NOT_AVAILABLE:
1835                     return true;
1836                 default:
1837                     return false;
1838             }
1839         }
1840 
validTorchStatus(int status)1841         private boolean validTorchStatus(int status) {
1842             switch (status) {
1843                 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE:
1844                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
1845                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
1846                     return true;
1847                 default:
1848                     return false;
1849             }
1850         }
1851 
postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback, final Executor executor)1852         private void postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback,
1853                 final Executor executor) {
1854             final long ident = Binder.clearCallingIdentity();
1855             try {
1856                 executor.execute(
1857                     new Runnable() {
1858                         @Override
1859                         public void run() {
1860                             callback.onCameraAccessPrioritiesChanged();
1861                         }
1862                     });
1863             } finally {
1864                 Binder.restoreCallingIdentity(ident);
1865             }
1866         }
1867 
postSingleCameraOpenedUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String packageId)1868         private void postSingleCameraOpenedUpdate(final AvailabilityCallback callback,
1869                 final Executor executor, final String id, final String packageId) {
1870             final long ident = Binder.clearCallingIdentity();
1871             try {
1872                 executor.execute(
1873                     new Runnable() {
1874                         @Override
1875                         public void run() {
1876                             callback.onCameraOpened(id, packageId);
1877                         }
1878                     });
1879             } finally {
1880                 Binder.restoreCallingIdentity(ident);
1881             }
1882         }
1883 
postSingleCameraClosedUpdate(final AvailabilityCallback callback, final Executor executor, final String id)1884         private void postSingleCameraClosedUpdate(final AvailabilityCallback callback,
1885                 final Executor executor, final String id) {
1886             final long ident = Binder.clearCallingIdentity();
1887             try {
1888                 executor.execute(
1889                     new Runnable() {
1890                         @Override
1891                         public void run() {
1892                             callback.onCameraClosed(id);
1893                         }
1894                     });
1895             } finally {
1896                 Binder.restoreCallingIdentity(ident);
1897             }
1898         }
1899 
postSingleUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String physicalId, final int status)1900         private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
1901                 final String id, final String physicalId, final int status) {
1902             if (isAvailable(status)) {
1903                 final long ident = Binder.clearCallingIdentity();
1904                 try {
1905                     executor.execute(
1906                         new Runnable() {
1907                             @Override
1908                             public void run() {
1909                                 if (physicalId == null) {
1910                                     callback.onCameraAvailable(id);
1911                                 } else {
1912                                     callback.onPhysicalCameraAvailable(id, physicalId);
1913                                 }
1914                             }
1915                         });
1916                 } finally {
1917                     Binder.restoreCallingIdentity(ident);
1918                 }
1919             } else {
1920                 final long ident = Binder.clearCallingIdentity();
1921                 try {
1922                     executor.execute(
1923                         new Runnable() {
1924                             @Override
1925                             public void run() {
1926                                 if (physicalId == null) {
1927                                     callback.onCameraUnavailable(id);
1928                                 } else {
1929                                     callback.onPhysicalCameraUnavailable(id, physicalId);
1930                                 }
1931                             }
1932                         });
1933                 } finally {
1934                     Binder.restoreCallingIdentity(ident);
1935                 }
1936             }
1937         }
1938 
postSingleTorchUpdate(final TorchCallback callback, final Executor executor, final String id, final int status)1939         private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor,
1940                 final String id, final int status) {
1941             switch(status) {
1942                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
1943                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: {
1944                         final long ident = Binder.clearCallingIdentity();
1945                         try {
1946                             executor.execute(() -> {
1947                                 callback.onTorchModeChanged(id, status ==
1948                                         ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
1949                             });
1950                         } finally {
1951                             Binder.restoreCallingIdentity(ident);
1952                         }
1953                     }
1954                     break;
1955                 default: {
1956                         final long ident = Binder.clearCallingIdentity();
1957                         try {
1958                             executor.execute(() -> {
1959                                 callback.onTorchModeUnavailable(id);
1960                             });
1961                         } finally {
1962                             Binder.restoreCallingIdentity(ident);
1963                         }
1964                     }
1965                     break;
1966             }
1967         }
1968 
1969         /**
1970          * Send the state of all known cameras to the provided listener, to initialize
1971          * the listener's knowledge of camera state.
1972          */
updateCallbackLocked(AvailabilityCallback callback, Executor executor)1973         private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) {
1974             for (int i = 0; i < mDeviceStatus.size(); i++) {
1975                 String id = mDeviceStatus.keyAt(i);
1976                 Integer status = mDeviceStatus.valueAt(i);
1977                 postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
1978 
1979                 // Send the NOT_PRESENT state for unavailable physical cameras
1980                 if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) {
1981                     ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id);
1982                     for (String unavailableId : unavailableIds) {
1983                         postSingleUpdate(callback, executor, id, unavailableId,
1984                                 ICameraServiceListener.STATUS_NOT_PRESENT);
1985                     }
1986                 }
1987             }
1988         }
1989 
onStatusChangedLocked(int status, String id)1990         private void onStatusChangedLocked(int status, String id) {
1991             if (DEBUG) {
1992                 Log.v(TAG,
1993                         String.format("Camera id %s has status changed to 0x%x", id, status));
1994             }
1995 
1996             if (!validStatus(status)) {
1997                 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id,
1998                                 status));
1999                 return;
2000             }
2001 
2002             Integer oldStatus;
2003             if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
2004                 oldStatus = mDeviceStatus.remove(id);
2005                 mUnavailablePhysicalDevices.remove(id);
2006             } else {
2007                 oldStatus = mDeviceStatus.put(id, status);
2008                 if (oldStatus == null) {
2009                     mUnavailablePhysicalDevices.put(id, new ArrayList<String>());
2010                 }
2011             }
2012 
2013             if (oldStatus != null && oldStatus == status) {
2014                 if (DEBUG) {
2015                     Log.v(TAG, String.format(
2016                         "Device status changed to 0x%x, which is what it already was",
2017                         status));
2018                 }
2019                 return;
2020             }
2021 
2022             // TODO: consider abstracting out this state minimization + transition
2023             // into a separate
2024             // more easily testable class
2025             // i.e. (new State()).addState(STATE_AVAILABLE)
2026             //                   .addState(STATE_NOT_AVAILABLE)
2027             //                   .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
2028             //                   .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
2029             //                   .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
2030             //                   .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
2031 
2032             // Translate all the statuses to either 'available' or 'not available'
2033             //  available -> available         => no new update
2034             //  not available -> not available => no new update
2035             if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
2036                 if (DEBUG) {
2037                     Log.v(TAG,
2038                             String.format(
2039                                 "Device status was previously available (%b), " +
2040                                 " and is now again available (%b)" +
2041                                 "so no new client visible update will be sent",
2042                                 isAvailable(oldStatus), isAvailable(status)));
2043                 }
2044                 return;
2045             }
2046 
2047             final int callbackCount = mCallbackMap.size();
2048             for (int i = 0; i < callbackCount; i++) {
2049                 Executor executor = mCallbackMap.valueAt(i);
2050                 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
2051 
2052                 postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
2053             }
2054         } // onStatusChangedLocked
2055 
onPhysicalCameraStatusChangedLocked(int status, String id, String physicalId)2056         private void onPhysicalCameraStatusChangedLocked(int status,
2057                 String id, String physicalId) {
2058             if (DEBUG) {
2059                 Log.v(TAG,
2060                         String.format("Camera id %s physical camera id %s has status "
2061                         + "changed to 0x%x", id, physicalId, status));
2062             }
2063 
2064             if (!validStatus(status)) {
2065                 Log.e(TAG, String.format(
2066                         "Ignoring invalid device %s physical device %s status 0x%x", id,
2067                         physicalId, status));
2068                 return;
2069             }
2070 
2071             //TODO: Do we need to treat this as error?
2072             if (!mDeviceStatus.containsKey(id) || !isAvailable(mDeviceStatus.get(id))
2073                     || !mUnavailablePhysicalDevices.containsKey(id)) {
2074                 Log.e(TAG, String.format("Camera %s is not available. Ignore physical camera "
2075                         + "status change", id));
2076                 return;
2077             }
2078 
2079             ArrayList<String> unavailablePhysicalDevices = mUnavailablePhysicalDevices.get(id);
2080             if (!isAvailable(status)
2081                     && !unavailablePhysicalDevices.contains(physicalId)) {
2082                 unavailablePhysicalDevices.add(physicalId);
2083             } else if (isAvailable(status)
2084                     && unavailablePhysicalDevices.contains(physicalId)) {
2085                 unavailablePhysicalDevices.remove(physicalId);
2086             } else {
2087                 if (DEBUG) {
2088                     Log.v(TAG,
2089                             String.format(
2090                                 "Physical camera device status was previously available (%b), "
2091                                 + " and is now again available (%b)"
2092                                 + "so no new client visible update will be sent",
2093                                 !unavailablePhysicalDevices.contains(physicalId),
2094                                 isAvailable(status)));
2095                 }
2096                 return;
2097             }
2098 
2099             final int callbackCount = mCallbackMap.size();
2100             for (int i = 0; i < callbackCount; i++) {
2101                 Executor executor = mCallbackMap.valueAt(i);
2102                 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
2103 
2104                 postSingleUpdate(callback, executor, id, physicalId, status);
2105             }
2106         } // onPhysicalCameraStatusChangedLocked
2107 
updateTorchCallbackLocked(TorchCallback callback, Executor executor)2108         private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
2109             for (int i = 0; i < mTorchStatus.size(); i++) {
2110                 String id = mTorchStatus.keyAt(i);
2111                 Integer status = mTorchStatus.valueAt(i);
2112                 postSingleTorchUpdate(callback, executor, id, status);
2113             }
2114         }
2115 
onTorchStatusChangedLocked(int status, String id)2116         private void onTorchStatusChangedLocked(int status, String id) {
2117             if (DEBUG) {
2118                 Log.v(TAG,
2119                         String.format("Camera id %s has torch status changed to 0x%x", id, status));
2120             }
2121 
2122             if (!validTorchStatus(status)) {
2123                 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id,
2124                                 status));
2125                 return;
2126             }
2127 
2128             Integer oldStatus = mTorchStatus.put(id, status);
2129             if (oldStatus != null && oldStatus == status) {
2130                 if (DEBUG) {
2131                     Log.v(TAG, String.format(
2132                         "Torch status changed to 0x%x, which is what it already was",
2133                         status));
2134                 }
2135                 return;
2136             }
2137 
2138             final int callbackCount = mTorchCallbackMap.size();
2139             for (int i = 0; i < callbackCount; i++) {
2140                 final Executor executor = mTorchCallbackMap.valueAt(i);
2141                 final TorchCallback callback = mTorchCallbackMap.keyAt(i);
2142                 postSingleTorchUpdate(callback, executor, id, status);
2143             }
2144         } // onTorchStatusChangedLocked
2145 
2146         /**
2147          * Register a callback to be notified about camera device availability with the
2148          * global listener singleton.
2149          *
2150          * @param callback the new callback to send camera availability notices to
2151          * @param executor The executor which should invoke the callback. May not be null.
2152          */
registerAvailabilityCallback(AvailabilityCallback callback, Executor executor)2153         public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) {
2154             synchronized (mLock) {
2155                 connectCameraServiceLocked();
2156 
2157                 Executor oldExecutor = mCallbackMap.put(callback, executor);
2158                 // For new callbacks, provide initial availability information
2159                 if (oldExecutor == null) {
2160                     updateCallbackLocked(callback, executor);
2161                 }
2162 
2163                 // If not connected to camera service, schedule a reconnect to camera service.
2164                 if (mCameraService == null) {
2165                     scheduleCameraServiceReconnectionLocked();
2166                 }
2167             }
2168         }
2169 
2170         /**
2171          * Remove a previously-added callback; the callback will no longer receive connection and
2172          * disconnection callbacks, and is no longer referenced by the global listener singleton.
2173          *
2174          * @param callback The callback to remove from the notification list
2175          */
unregisterAvailabilityCallback(AvailabilityCallback callback)2176         public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
2177             synchronized (mLock) {
2178                 mCallbackMap.remove(callback);
2179             }
2180         }
2181 
registerTorchCallback(TorchCallback callback, Executor executor)2182         public void registerTorchCallback(TorchCallback callback, Executor executor) {
2183             synchronized(mLock) {
2184                 connectCameraServiceLocked();
2185 
2186                 Executor oldExecutor = mTorchCallbackMap.put(callback, executor);
2187                 // For new callbacks, provide initial torch information
2188                 if (oldExecutor == null) {
2189                     updateTorchCallbackLocked(callback, executor);
2190                 }
2191 
2192                 // If not connected to camera service, schedule a reconnect to camera service.
2193                 if (mCameraService == null) {
2194                     scheduleCameraServiceReconnectionLocked();
2195                 }
2196             }
2197         }
2198 
unregisterTorchCallback(TorchCallback callback)2199         public void unregisterTorchCallback(TorchCallback callback) {
2200             synchronized(mLock) {
2201                 mTorchCallbackMap.remove(callback);
2202             }
2203         }
2204 
2205         /**
2206          * Callback from camera service notifying the process about camera availability changes
2207          */
2208         @Override
onStatusChanged(int status, String cameraId)2209         public void onStatusChanged(int status, String cameraId) throws RemoteException {
2210             synchronized(mLock) {
2211                 onStatusChangedLocked(status, cameraId);
2212             }
2213         }
2214 
2215         @Override
onPhysicalCameraStatusChanged(int status, String cameraId, String physicalCameraId)2216         public void onPhysicalCameraStatusChanged(int status, String cameraId,
2217                 String physicalCameraId) throws RemoteException {
2218             synchronized (mLock) {
2219                 onPhysicalCameraStatusChangedLocked(status, cameraId, physicalCameraId);
2220             }
2221         }
2222 
2223         @Override
onTorchStatusChanged(int status, String cameraId)2224         public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
2225             synchronized (mLock) {
2226                 onTorchStatusChangedLocked(status, cameraId);
2227             }
2228         }
2229 
2230         @Override
onCameraAccessPrioritiesChanged()2231         public void onCameraAccessPrioritiesChanged() {
2232             synchronized (mLock) {
2233                 final int callbackCount = mCallbackMap.size();
2234                 for (int i = 0; i < callbackCount; i++) {
2235                     Executor executor = mCallbackMap.valueAt(i);
2236                     final AvailabilityCallback callback = mCallbackMap.keyAt(i);
2237 
2238                     postSingleAccessPriorityChangeUpdate(callback, executor);
2239                 }
2240             }
2241         }
2242 
2243         @Override
onCameraOpened(String cameraId, String clientPackageId)2244         public void onCameraOpened(String cameraId, String clientPackageId) {
2245             synchronized (mLock) {
2246                 final int callbackCount = mCallbackMap.size();
2247                 for (int i = 0; i < callbackCount; i++) {
2248                     Executor executor = mCallbackMap.valueAt(i);
2249                     final AvailabilityCallback callback = mCallbackMap.keyAt(i);
2250 
2251                     postSingleCameraOpenedUpdate(callback, executor, cameraId, clientPackageId);
2252                 }
2253             }
2254         }
2255 
2256         @Override
onCameraClosed(String cameraId)2257         public void onCameraClosed(String cameraId) {
2258             synchronized (mLock) {
2259                 final int callbackCount = mCallbackMap.size();
2260                 for (int i = 0; i < callbackCount; i++) {
2261                     Executor executor = mCallbackMap.valueAt(i);
2262                     final AvailabilityCallback callback = mCallbackMap.keyAt(i);
2263 
2264                     postSingleCameraClosedUpdate(callback, executor, cameraId);
2265                 }
2266             }
2267         }
2268 
2269         /**
2270          * Try to connect to camera service after some delay if any client registered camera
2271          * availability callback or torch status callback.
2272          */
scheduleCameraServiceReconnectionLocked()2273         private void scheduleCameraServiceReconnectionLocked() {
2274             if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) {
2275                 // Not necessary to reconnect camera service if no client registers a callback.
2276                 return;
2277             }
2278 
2279             if (DEBUG) {
2280                 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS +
2281                         " ms");
2282             }
2283 
2284             try {
2285                 mScheduler.schedule(() -> {
2286                     ICameraService cameraService = getCameraService();
2287                     if (cameraService == null) {
2288                         synchronized(mLock) {
2289                             if (DEBUG) {
2290                                 Log.v(TAG, "Reconnecting Camera Service failed.");
2291                             }
2292                             scheduleCameraServiceReconnectionLocked();
2293                         }
2294                     }
2295                 }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
2296             } catch (RejectedExecutionException e) {
2297                 Log.e(TAG, "Failed to schedule camera service re-connect: " + e);
2298             }
2299         }
2300 
2301         /**
2302          * Listener for camera service death.
2303          *
2304          * <p>The camera service isn't supposed to die under any normal circumstances, but can be
2305          * turned off during debug, or crash due to bugs.  So detect that and null out the interface
2306          * object, so that the next calls to the manager can try to reconnect.</p>
2307          */
binderDied()2308         public void binderDied() {
2309             synchronized(mLock) {
2310                 // Only do this once per service death
2311                 if (mCameraService == null) return;
2312 
2313                 mCameraService = null;
2314 
2315                 // Tell listeners that the cameras and torch modes are unavailable and schedule a
2316                 // reconnection to camera service. When camera service is reconnected, the camera
2317                 // and torch statuses will be updated.
2318                 // Iterate from the end to the beginning befcause onStatusChangedLocked removes
2319                 // entries from the ArrayMap.
2320                 for (int i = mDeviceStatus.size() - 1; i >= 0; i--) {
2321                     String cameraId = mDeviceStatus.keyAt(i);
2322                     onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId);
2323                 }
2324                 for (int i = 0; i < mTorchStatus.size(); i++) {
2325                     String cameraId = mTorchStatus.keyAt(i);
2326                     onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE,
2327                             cameraId);
2328                 }
2329 
2330                 mConcurrentCameraIdCombinations.clear();
2331 
2332                 scheduleCameraServiceReconnectionLocked();
2333             }
2334         }
2335 
2336     } // CameraManagerGlobal
2337 
2338 } // CameraManager
2339