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