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