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