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