blob: 234014327150b95df679d788ae8f19434db5ea18 [file] [log] [blame]
Eino-Ville Talvalab2675542012-12-12 13:29:45 -08001/*
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
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070017package android.hardware.camera2;
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080018
Emilian Peev9129aa22018-03-20 15:39:34 +000019import android.annotation.CallbackExecutor;
Eino-Ville Talvala8b905572015-05-14 15:43:01 -070020import android.annotation.NonNull;
21import android.annotation.Nullable;
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -070022import android.annotation.RequiresPermission;
23import android.annotation.SystemService;
Igor Murashkine363fbb2013-06-25 20:26:06 +000024import android.content.Context;
Igor Murashkin4961bc82014-06-17 12:04:07 -070025import android.hardware.CameraInfo;
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -080026import android.hardware.CameraStatus;
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -070027import android.hardware.ICameraService;
28import android.hardware.ICameraServiceListener;
Emilian Peev9a0ec8c2018-02-28 14:53:30 +000029import android.hardware.camera2.impl.CameraDeviceImpl;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070030import android.hardware.camera2.impl.CameraMetadataNative;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070031import android.hardware.camera2.legacy.CameraDeviceUserShim;
Igor Murashkin4961bc82014-06-17 12:04:07 -070032import android.hardware.camera2.legacy.LegacyMetadataMapper;
Chien-Yu Chenafa91392015-02-11 11:23:28 -080033import android.os.Binder;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080034import android.os.DeadObjectException;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070035import android.os.Handler;
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -070036import android.os.IBinder;
Igor Murashkine363fbb2013-06-25 20:26:06 +000037import android.os.RemoteException;
38import android.os.ServiceManager;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080039import android.os.ServiceSpecificException;
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -070040import android.os.SystemProperties;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070041import android.util.ArrayMap;
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -070042import android.util.Log;
Emilian Peev423cbd72018-11-10 18:37:45 +000043import android.util.Size;
44import android.view.Display;
45import android.view.WindowManager;
Igor Murashkine363fbb2013-06-25 20:26:06 +000046
47import java.util.ArrayList;
Yin-Chia Yehf8aa7bc2018-03-09 15:48:16 -080048import java.util.Arrays;
49import java.util.Comparator;
Emilian Peev9a0ec8c2018-02-28 14:53:30 +000050import java.util.concurrent.Executor;
51import java.util.concurrent.Executors;
52import java.util.concurrent.RejectedExecutionException;
53import java.util.concurrent.ScheduledExecutorService;
54import java.util.concurrent.TimeUnit;
Igor Murashkine363fbb2013-06-25 20:26:06 +000055
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080056/**
Eino-Ville Talvalab67a3b32014-06-06 13:07:20 -070057 * <p>A system service manager for detecting, characterizing, and connecting to
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080058 * {@link CameraDevice CameraDevices}.</p>
59 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080060 * <p>For more details about communicating with camera devices, read the Camera
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070061 * developer guide or the {@link android.hardware.camera2 camera2}
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080062 * package documentation.</p>
63 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060064@SystemService(Context.CAMERA_SERVICE)
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080065public final class CameraManager {
66
Ruben Brunk85c43882014-02-21 17:40:51 -080067 private static final String TAG = "CameraManager";
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070068 private final boolean DEBUG = false;
Ruben Brunk85c43882014-02-21 17:40:51 -080069
Igor Murashkine363fbb2013-06-25 20:26:06 +000070 private static final int USE_CALLING_UID = -1;
71
Igor Murashkin4961bc82014-06-17 12:04:07 -070072 @SuppressWarnings("unused")
73 private static final int API_VERSION_1 = 1;
74 private static final int API_VERSION_2 = 2;
75
Eino-Ville Talvala57176122015-08-14 13:11:16 -070076 private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0;
77 private static final int CAMERA_TYPE_ALL = 1;
78
Igor Murashkine363fbb2013-06-25 20:26:06 +000079 private ArrayList<String> mDeviceIdList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070080
Igor Murashkine363fbb2013-06-25 20:26:06 +000081 private final Context mContext;
82 private final Object mLock = new Object();
83
84 /**
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080085 * @hide
86 */
Igor Murashkine363fbb2013-06-25 20:26:06 +000087 public CameraManager(Context context) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -070088 synchronized(mLock) {
89 mContext = context;
Igor Murashkine363fbb2013-06-25 20:26:06 +000090 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080091 }
92
93 /**
Ruben Brunk0f2be132015-04-17 14:23:40 -070094 * Return the list of currently connected camera devices by identifier, including
95 * cameras that may be in use by other camera API clients.
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070096 *
97 * <p>Non-removable cameras use integers starting at 0 for their
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080098 * identifiers, while removable cameras have a unique identifier for each
99 * individual device, even if they are the same model.</p>
100 *
Shuzhen Wang123deefc2018-08-20 12:00:05 -0700101 * <p>This list doesn't contain physical cameras that can only used as part of a logical
102 * multi-camera device.</p>
103 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800104 * @return The list of currently connected camera devices.
105 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700106 @NonNull
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700107 public String[] getCameraIdList() throws CameraAccessException {
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800108 return CameraManagerGlobal.get().getCameraIdList();
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800109 }
110
111 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700112 * Register a callback to be notified about camera device availability.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800113 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700114 * <p>Registering the same callback again will replace the handler with the
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700115 * new one provided.</p>
Igor Murashkine363fbb2013-06-25 20:26:06 +0000116 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700117 * <p>The first time a callback is registered, it is immediately called
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700118 * with the availability status of all currently known camera devices.</p>
119 *
Ruben Brunk0f2be132015-04-17 14:23:40 -0700120 * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera
121 * device is opened by any camera API client. As of API level 23, other camera API clients may
122 * still be able to open such a camera device, evicting the existing client if they have higher
123 * priority than the existing client of a camera device. See open() for more details.</p>
124 *
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700125 * <p>Since this callback will be registered with the camera service, remember to unregister it
126 * once it is no longer needed; otherwise the callback will continue to receive events
127 * indefinitely and it may prevent other resources from being released. Specifically, the
128 * callbacks will be invoked independently of the general activity lifecycle and independently
129 * of the state of individual CameraManager instances.</p>
130 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700131 * @param callback the new callback to send camera availability notices to
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800132 * @param handler The handler on which the callback should be invoked, or {@code null} to use
133 * the current thread's {@link android.os.Looper looper}.
134 *
135 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
136 * no looper.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800137 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700138 public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
139 @Nullable Handler handler) {
Emilian Peev9a0ec8c2018-02-28 14:53:30 +0000140 CameraManagerGlobal.get().registerAvailabilityCallback(callback,
141 CameraDeviceImpl.checkAndWrapHandler(handler));
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800142 }
143
144 /**
Emilian Peev9129aa22018-03-20 15:39:34 +0000145 * Register a callback to be notified about camera device availability.
146 *
147 * <p>The behavior of this method matches that of
148 * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)},
149 * except that it uses {@link java.util.concurrent.Executor} as an argument
150 * instead of {@link android.os.Handler}.</p>
151 *
152 * @param executor The executor which will be used to invoke the callback.
153 * @param callback the new callback to send camera availability notices to
154 *
155 * @throws IllegalArgumentException if the executor is {@code null}.
156 */
157 public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
158 @NonNull AvailabilityCallback callback) {
159 if (executor == null) {
160 throw new IllegalArgumentException("executor was null");
161 }
162 CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor);
163 }
164
165 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700166 * Remove a previously-added callback; the callback will no longer receive connection and
167 * disconnection callbacks.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800168 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700169 * <p>Removing a callback that isn't registered has no effect.</p>
Igor Murashkine363fbb2013-06-25 20:26:06 +0000170 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700171 * @param callback The callback to remove from the notification list
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800172 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700173 public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700174 CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800175 }
176
177 /**
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800178 * Register a callback to be notified about torch mode status.
179 *
180 * <p>Registering the same callback again will replace the handler with the
181 * new one provided.</p>
182 *
183 * <p>The first time a callback is registered, it is immediately called
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700184 * with the torch mode status of all currently known camera devices with a flash unit.</p>
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800185 *
186 * <p>Since this callback will be registered with the camera service, remember to unregister it
187 * once it is no longer needed; otherwise the callback will continue to receive events
188 * indefinitely and it may prevent other resources from being released. Specifically, the
189 * callbacks will be invoked independently of the general activity lifecycle and independently
190 * of the state of individual CameraManager instances.</p>
191 *
192 * @param callback The new callback to send torch mode status to
193 * @param handler The handler on which the callback should be invoked, or {@code null} to use
194 * the current thread's {@link android.os.Looper looper}.
195 *
196 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
197 * no looper.
198 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700199 public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
Emilian Peev9a0ec8c2018-02-28 14:53:30 +0000200 CameraManagerGlobal.get().registerTorchCallback(callback,
201 CameraDeviceImpl.checkAndWrapHandler(handler));
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800202 }
203
204 /**
Emilian Peev9129aa22018-03-20 15:39:34 +0000205 * Register a callback to be notified about torch mode status.
206 *
207 * <p>The behavior of this method matches that of
208 * {@link #registerTorchCallback(TorchCallback, Handler)},
209 * except that it uses {@link java.util.concurrent.Executor} as an argument
210 * instead of {@link android.os.Handler}.</p>
211 *
212 * @param executor The executor which will be used to invoke the callback
213 * @param callback The new callback to send torch mode status to
214 *
215 * @throws IllegalArgumentException if the executor is {@code null}.
216 */
217 public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor,
218 @NonNull TorchCallback callback) {
219 if (executor == null) {
220 throw new IllegalArgumentException("executor was null");
221 }
222 CameraManagerGlobal.get().registerTorchCallback(callback, executor);
223 }
224
225 /**
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800226 * Remove a previously-added callback; the callback will no longer receive torch mode status
227 * callbacks.
228 *
229 * <p>Removing a callback that isn't registered has no effect.</p>
230 *
231 * @param callback The callback to remove from the notification list
232 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700233 public void unregisterTorchCallback(@NonNull TorchCallback callback) {
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800234 CameraManagerGlobal.get().unregisterTorchCallback(callback);
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800235 }
236
Emilian Peev423cbd72018-11-10 18:37:45 +0000237 private Size getDisplaySize() {
238 Size ret = new Size(0, 0);
239
240 try {
241 WindowManager windowManager =
242 (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
243 Display display = windowManager.getDefaultDisplay();
244
245 int width = display.getWidth();
246 int height = display.getHeight();
247
248 if (height > width) {
249 height = width;
250 width = display.getHeight();
251 }
252
253 ret = new Size(width, height);
254 } catch (Exception e) {
255 Log.e(TAG, "getDisplaySize Failed. " + e.toString());
256 }
257
258 return ret;
259 }
260
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800261 /**
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800262 * <p>Query the capabilities of a camera device. These capabilities are
263 * immutable for a given camera.</p>
264 *
Shuzhen Wang123deefc2018-08-20 12:00:05 -0700265 * <p>From API level 29, this function can also be used to query the capabilities of physical
266 * cameras that can only be used as part of logical multi-camera. These cameras cannot not be
267 * opened directly via {@link #openCamera}</p>
268 *
269 * @param cameraId The id of the camera device to query. This could be either a standalone
270 * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that
271 * can only used as part of a logical multi-camera.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800272 * @return The properties of the given camera
273 *
274 * @throws IllegalArgumentException if the cameraId does not match any
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700275 * known camera device.
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700276 * @throws CameraAccessException if the camera device has been disconnected.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800277 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700278 * @see #getCameraIdList
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800279 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
280 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700281 @NonNull
282 public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800283 throws CameraAccessException {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700284 CameraCharacteristics characteristics = null;
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -0700285 if (CameraManagerGlobal.sCameraServiceDisabled) {
286 throw new IllegalArgumentException("No cameras available on device");
287 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000288 synchronized (mLock) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700289 /*
290 * Get the camera characteristics from the camera service directly if it supports it,
291 * otherwise get them from the legacy shim instead.
292 */
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700293 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700294 if (cameraService == null) {
295 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
296 "Camera service is currently unavailable");
297 }
Igor Murashkin4961bc82014-06-17 12:04:07 -0700298 try {
Emilian Peev423cbd72018-11-10 18:37:45 +0000299 Size displaySize = getDisplaySize();
300
Shuzhen Wang123deefc2018-08-20 12:00:05 -0700301 // First check isHiddenPhysicalCamera to avoid supportsCamera2ApiLocked throwing
302 // exception in case cameraId is a hidden physical camera.
303 if (!isHiddenPhysicalCamera(cameraId) && !supportsCamera2ApiLocked(cameraId)) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700304 // Legacy backwards compatibility path; build static info from the camera
305 // parameters
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800306 int id = Integer.parseInt(cameraId);
307
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800308 String parameters = cameraService.getLegacyParameters(id);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700309
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800310 CameraInfo info = cameraService.getCameraInfo(id);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700311
Emilian Peev423cbd72018-11-10 18:37:45 +0000312 characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info,
313 id, displaySize);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700314 } else {
315 // Normal path: Get the camera characteristics directly from the camera service
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800316 CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
Emilian Peev423cbd72018-11-10 18:37:45 +0000317 if (!isHiddenPhysicalCamera(cameraId)) {
318 try {
319 info.setCameraId(Integer.parseInt(cameraId));
320 } catch (NumberFormatException e) {
321 Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer");
322 }
323 }
324 info.setDisplaySize(displaySize);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700325
326 characteristics = new CameraCharacteristics(info);
327 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800328 } catch (ServiceSpecificException e) {
329 throwAsPublicException(e);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700330 } catch (RemoteException e) {
331 // Camera service died - act as if the camera was disconnected
332 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
333 "Camera service is currently unavailable", e);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700334 }
Zhijun He20011882013-09-25 10:05:59 -0700335 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700336 return characteristics;
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800337 }
338
339 /**
Ruben Brunk0f2be132015-04-17 14:23:40 -0700340 * Helper for opening a connection to a camera with the given ID.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800341 *
342 * @param cameraId The unique identifier of the camera device to open
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700343 * @param callback The callback for the camera. Must not be null.
Emilian Peev9129aa22018-03-20 15:39:34 +0000344 * @param executor The executor to invoke the callback with. Must not be null.
Eino-Ville Talvala62deabc2016-07-08 12:11:11 -0700345 * @param uid The UID of the application actually opening the camera.
346 * Must be USE_CALLING_UID unless the caller is a service
347 * that is trusted to open the device on behalf of an
348 * application and to forward the real UID.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800349 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800350 * @throws CameraAccessException if the camera is disabled by device policy,
Ruben Brunk0f2be132015-04-17 14:23:40 -0700351 * too many camera devices are already open, or the cameraId does not match
Ruben Brunk66ef6452013-08-08 13:05:30 -0700352 * any currently available camera device.
353 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800354 * @throws SecurityException if the application does not have permission to
355 * access the camera
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700356 * @throws IllegalArgumentException if callback or handler is null.
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700357 * @return A handle to the newly-created camera device.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800358 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700359 * @see #getCameraIdList
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800360 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
361 */
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700362 private CameraDevice openCameraDeviceUserAsync(String cameraId,
Emilian Peev9129aa22018-03-20 15:39:34 +0000363 CameraDevice.StateCallback callback, Executor executor, final int uid)
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700364 throws CameraAccessException {
Ruben Brunk57493682014-05-27 18:58:08 -0700365 CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700366 CameraDevice device = null;
Igor Murashkine363fbb2013-06-25 20:26:06 +0000367
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800368 synchronized (mLock) {
Igor Murashkin70725502013-06-25 20:27:06 +0000369
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800370 ICameraDeviceUser cameraUser = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000371
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800372 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
373 new android.hardware.camera2.impl.CameraDeviceImpl(
374 cameraId,
375 callback,
Emilian Peev9129aa22018-03-20 15:39:34 +0000376 executor,
Chien-Yu Chen3d2b5fe2017-04-05 16:42:31 -0700377 characteristics,
378 mContext.getApplicationInfo().targetSdkVersion);
Igor Murashkin70725502013-06-25 20:27:06 +0000379
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800380 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700381
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800382 try {
383 if (supportsCamera2ApiLocked(cameraId)) {
384 // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
385 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
386 if (cameraService == null) {
387 throw new ServiceSpecificException(
388 ICameraService.ERROR_DISCONNECTED,
389 "Camera service is currently unavailable");
390 }
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800391 cameraUser = cameraService.connectDevice(callbacks, cameraId,
Eino-Ville Talvala62deabc2016-07-08 12:11:11 -0700392 mContext.getOpPackageName(), uid);
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800393 } else {
394 // Use legacy camera implementation for HAL1 devices
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800395 int id;
396 try {
397 id = Integer.parseInt(cameraId);
398 } catch (NumberFormatException e) {
399 throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
400 + cameraId);
401 }
402
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800403 Log.i(TAG, "Using legacy camera HAL.");
Emilian Peev423cbd72018-11-10 18:37:45 +0000404 cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id,
405 getDisplaySize());
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800406 }
407 } catch (ServiceSpecificException e) {
408 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
409 throw new AssertionError("Should've gone down the shim path");
410 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
411 e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
412 e.errorCode == ICameraService.ERROR_DISABLED ||
413 e.errorCode == ICameraService.ERROR_DISCONNECTED ||
414 e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
415 // Received one of the known connection errors
416 // The remote camera device cannot be connected to, so
417 // set the local camera to the startup error state
418 deviceImpl.setRemoteFailure(e);
419
420 if (e.errorCode == ICameraService.ERROR_DISABLED ||
421 e.errorCode == ICameraService.ERROR_DISCONNECTED ||
422 e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
423 // Per API docs, these failures call onError and throw
424 throwAsPublicException(e);
425 }
426 } else {
427 // Unexpected failure - rethrow
428 throwAsPublicException(e);
429 }
430 } catch (RemoteException e) {
431 // Camera service died - act as if it's a CAMERA_DISCONNECTED case
432 ServiceSpecificException sse = new ServiceSpecificException(
433 ICameraService.ERROR_DISCONNECTED,
434 "Camera service is currently unavailable");
435 deviceImpl.setRemoteFailure(sse);
436 throwAsPublicException(sse);
437 }
438
439 // TODO: factor out callback to be non-nested, then move setter to constructor
440 // For now, calling setRemoteDevice will fire initial
441 // onOpened/onUnconfigured callbacks.
Shuzhen Wang78392932016-03-04 15:26:01 -0800442 // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
443 // cameraUser dies during setup.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800444 deviceImpl.setRemoteDevice(cameraUser);
445 device = deviceImpl;
Igor Murashkine363fbb2013-06-25 20:26:06 +0000446 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800447
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700448 return device;
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800449 }
450
451 /**
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700452 * Open a connection to a camera with the given ID.
453 *
454 * <p>Use {@link #getCameraIdList} to get the list of available camera
455 * devices. Note that even if an id is listed, open may fail if the device
456 * is disconnected between the calls to {@link #getCameraIdList} and
Ruben Brunk0f2be132015-04-17 14:23:40 -0700457 * {@link #openCamera}, or if a higher-priority camera API client begins using the
458 * camera device.</p>
459 *
460 * <p>As of API level 23, devices for which the
461 * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the
462 * device being in use by a lower-priority, background camera API client can still potentially
463 * be opened by calling this method when the calling camera API client has a higher priority
464 * than the current camera API client using this device. In general, if the top, foreground
465 * activity is running within your application process, your process will be given the highest
466 * priority when accessing the camera, and this method will succeed even if the camera device is
467 * in use by another camera API client. Any lower-priority application that loses control of the
468 * camera in this way will receive an
469 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700470 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700471 * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700472 * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
473 * for operation by calling {@link CameraDevice#createCaptureSession} and
474 * {@link CameraDevice#createCaptureRequest}</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700475 *
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700476 * <!--
477 * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
478 * on the returned CameraDevice instance will be queued up until the device startup has
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700479 * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700480 * called. The pending operations are then processed in order.</p>
481 * -->
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700482 * <p>If the camera becomes disconnected during initialization
483 * after this function call returns,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700484 * {@link CameraDevice.StateCallback#onDisconnected} with a
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700485 * {@link CameraDevice} in the disconnected state (and
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700486 * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700487 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700488 * <p>If opening the camera device fails, then the device callback's
489 * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700490 * calls on the camera device will throw a {@link CameraAccessException}.</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700491 *
492 * @param cameraId
493 * The unique identifier of the camera device to open
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700494 * @param callback
495 * The callback which is invoked once the camera is opened
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700496 * @param handler
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700497 * The handler on which the callback should be invoked, or
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700498 * {@code null} to use the current thread's {@link android.os.Looper looper}.
499 *
500 * @throws CameraAccessException if the camera is disabled by device policy,
Ruben Brunk0f2be132015-04-17 14:23:40 -0700501 * has been disconnected, or is being used by a higher-priority camera API client.
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700502 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700503 * @throws IllegalArgumentException if cameraId or the callback was null,
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700504 * or the cameraId does not match any currently or previously available
Shuzhen Wang123deefc2018-08-20 12:00:05 -0700505 * camera device returned by {@link #getCameraIdList}.
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700506 *
507 * @throws SecurityException if the application does not have permission to
508 * access the camera
509 *
510 * @see #getCameraIdList
511 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
512 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700513 @RequiresPermission(android.Manifest.permission.CAMERA)
514 public void openCamera(@NonNull String cameraId,
515 @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700516 throws CameraAccessException {
517
Emilian Peev9129aa22018-03-20 15:39:34 +0000518 openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
519 USE_CALLING_UID);
520 }
521
522 /**
523 * Open a connection to a camera with the given ID.
524 *
525 * <p>The behavior of this method matches that of
526 * {@link #openCamera(String, StateCallback, Handler)}, except that it uses
527 * {@link java.util.concurrent.Executor} as an argument instead of
528 * {@link android.os.Handler}.</p>
529 *
530 * @param cameraId
531 * The unique identifier of the camera device to open
532 * @param executor
533 * The executor which will be used when invoking the callback.
534 * @param callback
535 * The callback which is invoked once the camera is opened
536 *
537 * @throws CameraAccessException if the camera is disabled by device policy,
538 * has been disconnected, or is being used by a higher-priority camera API client.
539 *
540 * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
541 * or the cameraId does not match any currently or previously available
542 * camera device.
543 *
544 * @throws SecurityException if the application does not have permission to
545 * access the camera
546 *
547 * @see #getCameraIdList
548 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
549 */
550 @RequiresPermission(android.Manifest.permission.CAMERA)
551 public void openCamera(@NonNull String cameraId,
552 @NonNull @CallbackExecutor Executor executor,
553 @NonNull final CameraDevice.StateCallback callback)
554 throws CameraAccessException {
555 if (executor == null) {
556 throw new IllegalArgumentException("executor was null");
557 }
558 openCameraForUid(cameraId, callback, executor, USE_CALLING_UID);
Eino-Ville Talvala62deabc2016-07-08 12:11:11 -0700559 }
560
561 /**
562 * Open a connection to a camera with the given ID, on behalf of another application
563 * specified by clientUid.
564 *
565 * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
566 * the caller to specify the UID to use for permission/etc verification. This can only be
567 * done by services trusted by the camera subsystem to act on behalf of applications and
568 * to forward the real UID.</p>
569 *
570 * @param clientUid
571 * The UID of the application on whose behalf the camera is being opened.
572 * Must be USE_CALLING_UID unless the caller is a trusted service.
573 *
574 * @hide
575 */
576 public void openCameraForUid(@NonNull String cameraId,
Emilian Peev9129aa22018-03-20 15:39:34 +0000577 @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
Eino-Ville Talvala62deabc2016-07-08 12:11:11 -0700578 int clientUid)
579 throws CameraAccessException {
580
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700581 if (cameraId == null) {
582 throw new IllegalArgumentException("cameraId was null");
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700583 } else if (callback == null) {
584 throw new IllegalArgumentException("callback was null");
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700585 }
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -0700586 if (CameraManagerGlobal.sCameraServiceDisabled) {
587 throw new IllegalArgumentException("No cameras available on device");
588 }
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700589
Emilian Peev9129aa22018-03-20 15:39:34 +0000590 openCameraDeviceUserAsync(cameraId, callback, executor, clientUid);
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700591 }
592
593 /**
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800594 * Set the flash unit's torch mode of the camera of the given ID without opening the camera
595 * device.
596 *
597 * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use
598 * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit.
599 * Note that even if a camera device has a flash unit, turning on the torch mode may fail
600 * if the camera device or other camera resources needed to turn on the torch mode are in use.
601 * </p>
602 *
603 * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully,
604 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked.
605 * However, even if turning on the torch mode is successful, the application does not have the
606 * exclusive ownership of the flash unit or the camera device. The torch mode will be turned
607 * off and becomes unavailable when the camera device that the flash unit belongs to becomes
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800608 * unavailable or when other camera resources to keep the torch on become unavailable (
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800609 * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also,
610 * other applications are free to call {@link #setTorchMode} to turn off the torch mode (
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800611 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest
612 * application that turned on the torch mode exits, the torch mode will be turned off.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800613 *
614 * @param cameraId
615 * The unique identifier of the camera device that the flash unit belongs to.
616 * @param enabled
617 * The desired state of the torch mode for the target camera device. Set to
618 * {@code true} to turn on the torch mode. Set to {@code false} to turn off the
619 * torch mode.
620 *
621 * @throws CameraAccessException if it failed to access the flash unit.
622 * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
623 * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
624 * other camera resources needed to turn on the torch mode are in use.
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800625 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera
626 * service is not available.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800627 *
628 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
629 * or previously available camera device, or the camera device doesn't have a
630 * flash unit.
631 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700632 public void setTorchMode(@NonNull String cameraId, boolean enabled)
633 throws CameraAccessException {
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -0700634 if (CameraManagerGlobal.sCameraServiceDisabled) {
635 throw new IllegalArgumentException("No cameras available on device");
636 }
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800637 CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800638 }
639
640 /**
Ruben Brunk0f2be132015-04-17 14:23:40 -0700641 * A callback for camera devices becoming available or unavailable to open.
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700642 *
643 * <p>Cameras become available when they are no longer in use, or when a new
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800644 * removable camera is connected. They become unavailable when some
645 * application or service starts using a camera, or when a removable camera
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700646 * is disconnected.</p>
647 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700648 * <p>Extend this callback and pass an instance of the subclass to
649 * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability
Eino-Ville Talvalab67a3b32014-06-06 13:07:20 -0700650 * changes.</p>
651 *
Philip P. Moltmann85e1d502015-10-26 08:50:04 -0700652 * @see #registerAvailabilityCallback
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800653 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700654 public static abstract class AvailabilityCallback {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700655
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800656 /**
657 * A new camera has become available to use.
658 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700659 * <p>The default implementation of this method does nothing.</p>
660 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800661 * @param cameraId The unique identifier of the new camera.
662 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700663 public void onCameraAvailable(@NonNull String cameraId) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700664 // default empty implementation
665 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800666
667 /**
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700668 * A previously-available camera has become unavailable for use.
669 *
670 * <p>If an application had an active CameraDevice instance for the
671 * now-disconnected camera, that application will receive a
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700672 * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p>
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700673 *
674 * <p>The default implementation of this method does nothing.</p>
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800675 *
676 * @param cameraId The unique identifier of the disconnected camera.
677 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700678 public void onCameraUnavailable(@NonNull String cameraId) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700679 // default empty implementation
680 }
Emilian Peev0e971f32019-02-22 17:34:29 -0800681
682 /**
683 * Notify registered clients about a change in the camera access priorities.
684 *
685 * <p>Notification that camera access priorities have changed and the camera may
686 * now be openable. An application that was previously denied camera access due to
687 * a higher-priority user already using the camera, or that was disconnected from an
688 * active camera session due to a higher-priority user trying to open the camera,
689 * should try to open the camera again if it still wants to use it. Note that
690 * multiple applications may receive this callback at the same time, and only one of
691 * them will succeed in opening the camera in practice, depending on exact access
692 * priority levels and timing. This method is useful in cases where multiple
693 * applications may be in the resumed state at the same time, and the user switches
694 * focus between them, or if the current camera-using application moves between
695 * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera
696 * available/unavailable callbacks will not be invoked, but another application may
697 * now have higher priority for camera access than the current camera-using
698 * application.</p>
699 *
700 * <p>The default implementation of this method does nothing.</p>
701 *
702 */
703 public void onCameraAccessPrioritiesChanged() {
704 // default empty implementation
705 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800706 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000707
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700708 /**
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800709 * A callback for camera flash torch modes becoming unavailable, disabled, or enabled.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800710 *
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800711 * <p>The torch mode becomes unavailable when the camera device it belongs to becomes
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700712 * unavailable or other camera resources it needs become busy due to other higher priority
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800713 * camera activities. The torch mode becomes disabled when it was turned off or when the camera
714 * device it belongs to is no longer in use and other camera resources it needs are no longer
715 * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to
716 * turn off the camera's torch mode, or when an application turns on another camera's torch mode
717 * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes
718 * enabled when it is turned on via {@link #setTorchMode}.</p>
719 *
720 * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled
721 * or enabled state.</p>
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800722 *
723 * <p>Extend this callback and pass an instance of the subclass to
724 * {@link CameraManager#registerTorchCallback} to be notified of such status changes.
725 * </p>
726 *
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700727 * @see #registerTorchCallback
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800728 */
729 public static abstract class TorchCallback {
730 /**
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800731 * A camera's torch mode has become unavailable to set via {@link #setTorchMode}.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800732 *
733 * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be
734 * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800735 * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or
736 * enabled state again.</p>
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800737 *
738 * <p>The default implementation of this method does nothing.</p>
739 *
740 * @param cameraId The unique identifier of the camera whose torch mode has become
741 * unavailable.
742 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700743 public void onTorchModeUnavailable(@NonNull String cameraId) {
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800744 // default empty implementation
745 }
746
747 /**
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800748 * A camera's torch mode has become enabled or disabled and can be changed via
749 * {@link #setTorchMode}.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800750 *
751 * <p>The default implementation of this method does nothing.</p>
752 *
753 * @param cameraId The unique identifier of the camera whose torch mode has been changed.
754 *
755 * @param enabled The state that the torch mode of the camera has been changed to.
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800756 * {@code true} when the torch mode has become on and available to be turned
757 * off. {@code false} when the torch mode has becomes off and available to
758 * be turned on.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800759 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700760 public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800761 // default empty implementation
762 }
763 }
764
765 /**
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800766 * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces
767 * into the correct public exceptions.
768 *
769 * @hide
770 */
771 public static void throwAsPublicException(Throwable t) throws CameraAccessException {
772 if (t instanceof ServiceSpecificException) {
773 ServiceSpecificException e = (ServiceSpecificException) t;
774 int reason = CameraAccessException.CAMERA_ERROR;
775 switch(e.errorCode) {
776 case ICameraService.ERROR_DISCONNECTED:
777 reason = CameraAccessException.CAMERA_DISCONNECTED;
778 break;
779 case ICameraService.ERROR_DISABLED:
780 reason = CameraAccessException.CAMERA_DISABLED;
781 break;
782 case ICameraService.ERROR_CAMERA_IN_USE:
783 reason = CameraAccessException.CAMERA_IN_USE;
784 break;
785 case ICameraService.ERROR_MAX_CAMERAS_IN_USE:
786 reason = CameraAccessException.MAX_CAMERAS_IN_USE;
787 break;
788 case ICameraService.ERROR_DEPRECATED_HAL:
789 reason = CameraAccessException.CAMERA_DEPRECATED_HAL;
790 break;
791 case ICameraService.ERROR_ILLEGAL_ARGUMENT:
792 case ICameraService.ERROR_ALREADY_EXISTS:
793 throw new IllegalArgumentException(e.getMessage(), e);
794 case ICameraService.ERROR_PERMISSION_DENIED:
795 throw new SecurityException(e.getMessage(), e);
796 case ICameraService.ERROR_TIMED_OUT:
797 case ICameraService.ERROR_INVALID_OPERATION:
798 default:
799 reason = CameraAccessException.CAMERA_ERROR;
800 }
801 throw new CameraAccessException(reason, e.getMessage(), e);
802 } else if (t instanceof DeadObjectException) {
803 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
804 "Camera service has died unexpectedly",
805 t);
806 } else if (t instanceof RemoteException) {
807 throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
808 " which should never happen.", t);
809 } else if (t instanceof RuntimeException) {
810 RuntimeException e = (RuntimeException) t;
811 throw e;
812 }
813 }
814
815 /**
Igor Murashkin4961bc82014-06-17 12:04:07 -0700816 * Queries the camera service if it supports the camera2 api directly, or needs a shim.
817 *
818 * @param cameraId a non-{@code null} camera identifier
819 * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
820 */
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700821 private boolean supportsCamera2ApiLocked(String cameraId) {
822 return supportsCameraApiLocked(cameraId, API_VERSION_2);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700823 }
824
825 /**
826 * Queries the camera service if it supports a camera api directly, or needs a shim.
827 *
828 * @param cameraId a non-{@code null} camera identifier
829 * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
830 * @return {@code true} if connecting will work for that device version.
831 */
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700832 private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
Igor Murashkin4961bc82014-06-17 12:04:07 -0700833 /*
834 * Possible return values:
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700835 * - NO_ERROR => CameraX API is supported
836 * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
837 * - Remote exception => If the camera service died
Igor Murashkin4961bc82014-06-17 12:04:07 -0700838 *
839 * Anything else is an unexpected error we don't want to recover from.
840 */
Igor Murashkin4961bc82014-06-17 12:04:07 -0700841 try {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700842 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700843 // If no camera service, no support
844 if (cameraService == null) return false;
Igor Murashkin4961bc82014-06-17 12:04:07 -0700845
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800846 return cameraService.supportsCameraApi(cameraId, apiVersion);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700847 } catch (RemoteException e) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700848 // Camera service is now down, no support for any API level
849 }
850 return false;
851 }
852
853 /**
Shuzhen Wang123deefc2018-08-20 12:00:05 -0700854 * Queries the camera service if a cameraId is a hidden physical camera that belongs to a
855 * logical camera device.
856 *
857 * A hidden physical camera is a camera that cannot be opened by the application. But it
858 * can be used as part of a logical camera.
859 *
860 * @param cameraId a non-{@code null} camera identifier
861 * @return {@code true} if cameraId is a hidden physical camera device
862 */
863 private boolean isHiddenPhysicalCamera(String cameraId) {
864 try {
865 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
866 // If no camera service, no support
867 if (cameraService == null) return false;
868
869 return cameraService.isHiddenPhysicalCamera(cameraId);
870 } catch (RemoteException e) {
871 // Camera service is now down, no support for any API level
872 }
873 return false;
874 }
875
876 /**
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700877 * A per-process global camera manager instance, to retain a connection to the camera service,
878 * and to distribute camera availability notices to API-registered callbacks
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700879 */
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700880 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub
881 implements IBinder.DeathRecipient {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700882
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700883 private static final String TAG = "CameraManagerGlobal";
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700884 private final boolean DEBUG = false;
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700885
Chien-Yu Chen0f69b462015-07-13 16:37:13 -0700886 private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000;
887
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700888 // Singleton instance
889 private static final CameraManagerGlobal gCameraManager =
890 new CameraManagerGlobal();
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700891
892 /**
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700893 * This must match the ICameraService definition
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700894 */
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700895 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
Igor Murashkine363fbb2013-06-25 20:26:06 +0000896
Emilian Peev9a0ec8c2018-02-28 14:53:30 +0000897 private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000898 // Camera ID -> Status map
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700899 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
Igor Murashkine363fbb2013-06-25 20:26:06 +0000900
Emilian Peev9a0ec8c2018-02-28 14:53:30 +0000901 // Registered availablility callbacks and their executors
902 private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
903 new ArrayMap<AvailabilityCallback, Executor>();
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700904
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800905 // torch client binder to set the torch mode with.
906 private Binder mTorchClientBinder = new Binder();
907
908 // Camera ID -> Torch status map
909 private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
910
Emilian Peev9a0ec8c2018-02-28 14:53:30 +0000911 // Registered torch callbacks and their executors
912 private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap =
913 new ArrayMap<TorchCallback, Executor>();
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800914
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700915 private final Object mLock = new Object();
916
917 // Access only through getCameraService to deal with binder death
918 private ICameraService mCameraService;
919
920 // Singleton, don't allow construction
921 private CameraManagerGlobal() {
922 }
923
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -0700924 public static final boolean sCameraServiceDisabled =
925 SystemProperties.getBoolean("config.disable_cameraservice", false);
926
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700927 public static CameraManagerGlobal get() {
928 return gCameraManager;
929 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000930
931 @Override
932 public IBinder asBinder() {
933 return this;
934 }
935
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700936 /**
937 * Return a best-effort ICameraService.
938 *
939 * <p>This will be null if the camera service is not currently available. If the camera
940 * service has died since the last use of the camera service, will try to reconnect to the
941 * service.</p>
942 */
943 public ICameraService getCameraService() {
944 synchronized(mLock) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700945 connectCameraServiceLocked();
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -0700946 if (mCameraService == null && !sCameraServiceDisabled) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700947 Log.e(TAG, "Camera service is unavailable");
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700948 }
949 return mCameraService;
950 }
951 }
952
953 /**
954 * Connect to the camera service if it's available, and set up listeners.
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700955 * If the service is already connected, do nothing.
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700956 *
957 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
958 */
959 private void connectCameraServiceLocked() {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700960 // Only reconnect if necessary
Eino-Ville Talvala19d96a12017-07-13 12:07:20 -0700961 if (mCameraService != null || sCameraServiceDisabled) return;
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700962
963 Log.i(TAG, "Connecting to camera service");
964
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700965 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
966 if (cameraServiceBinder == null) {
967 // Camera service is now down, leave mCameraService as null
968 return;
969 }
970 try {
971 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
972 } catch (RemoteException e) {
973 // Camera service is now down, leave mCameraService as null
974 return;
975 }
976
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800977 ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700978
979 try {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800980 CameraMetadataNative.setupGlobalVendorTagDescriptor();
981 } catch (ServiceSpecificException e) {
982 handleRecoverableSetupErrors(e);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700983 }
984
985 try {
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800986 CameraStatus[] cameraStatuses = cameraService.addListener(this);
987 for (CameraStatus c : cameraStatuses) {
988 onStatusChangedLocked(c.status, c.cameraId);
989 }
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700990 mCameraService = cameraService;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800991 } catch(ServiceSpecificException e) {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700992 // Unexpected failure
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800993 throw new IllegalStateException("Failed to register a camera service listener", e);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700994 } catch (RemoteException e) {
995 // Camera service is now down, leave mCameraService as null
996 }
997 }
998
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -0800999 /**
1000 * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
1001 * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
1002 */
1003 public String[] getCameraIdList() {
1004 String[] cameraIds = null;
1005 synchronized(mLock) {
1006 // Try to make sure we have an up-to-date list of camera devices.
1007 connectCameraServiceLocked();
1008
1009 int idCount = 0;
1010 for (int i = 0; i < mDeviceStatus.size(); i++) {
1011 int status = mDeviceStatus.valueAt(i);
1012 if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
1013 status == ICameraServiceListener.STATUS_ENUMERATING) continue;
1014 idCount++;
1015 }
1016 cameraIds = new String[idCount];
1017 idCount = 0;
1018 for (int i = 0; i < mDeviceStatus.size(); i++) {
1019 int status = mDeviceStatus.valueAt(i);
1020 if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
1021 status == ICameraServiceListener.STATUS_ENUMERATING) continue;
1022 cameraIds[idCount] = mDeviceStatus.keyAt(i);
1023 idCount++;
1024 }
1025 }
Yin-Chia Yehf8aa7bc2018-03-09 15:48:16 -08001026
1027 // The sort logic must match the logic in
1028 // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
1029 Arrays.sort(cameraIds, new Comparator<String>() {
1030 @Override
1031 public int compare(String s1, String s2) {
1032 int s1Int = 0, s2Int = 0;
1033 try {
1034 s1Int = Integer.parseInt(s1);
1035 } catch (NumberFormatException e) {
1036 s1Int = -1;
1037 }
1038
1039 try {
1040 s2Int = Integer.parseInt(s2);
1041 } catch (NumberFormatException e) {
1042 s2Int = -1;
1043 }
1044
1045 // Uint device IDs first
1046 if (s1Int >= 0 && s2Int >= 0) {
1047 return s1Int - s2Int;
1048 } else if (s1Int >= 0) {
1049 return -1;
1050 } else if (s2Int >= 0) {
1051 return 1;
1052 } else {
1053 // Simple string compare if both id are not uint
1054 return s1.compareTo(s2);
1055 }
1056 }});
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -08001057 return cameraIds;
1058 }
1059
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001060 public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
1061 synchronized(mLock) {
1062
1063 if (cameraId == null) {
1064 throw new IllegalArgumentException("cameraId was null");
1065 }
1066
1067 ICameraService cameraService = getCameraService();
1068 if (cameraService == null) {
1069 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
1070 "Camera service is currently unavailable");
1071 }
1072
1073 try {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001074 cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder);
1075 } catch(ServiceSpecificException e) {
1076 throwAsPublicException(e);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001077 } catch (RemoteException e) {
1078 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
1079 "Camera service is currently unavailable");
1080 }
1081 }
1082 }
1083
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001084 private void handleRecoverableSetupErrors(ServiceSpecificException e) {
1085 switch (e.errorCode) {
1086 case ICameraService.ERROR_DISCONNECTED:
1087 Log.w(TAG, e.getMessage());
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001088 break;
1089 default:
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001090 throw new IllegalStateException(e);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001091 }
1092 }
1093
Igor Murashkine363fbb2013-06-25 20:26:06 +00001094 private boolean isAvailable(int status) {
1095 switch (status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001096 case ICameraServiceListener.STATUS_PRESENT:
Igor Murashkine363fbb2013-06-25 20:26:06 +00001097 return true;
1098 default:
1099 return false;
1100 }
1101 }
1102
1103 private boolean validStatus(int status) {
1104 switch (status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001105 case ICameraServiceListener.STATUS_NOT_PRESENT:
1106 case ICameraServiceListener.STATUS_PRESENT:
1107 case ICameraServiceListener.STATUS_ENUMERATING:
1108 case ICameraServiceListener.STATUS_NOT_AVAILABLE:
Igor Murashkine363fbb2013-06-25 20:26:06 +00001109 return true;
1110 default:
1111 return false;
1112 }
1113 }
1114
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001115 private boolean validTorchStatus(int status) {
1116 switch (status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001117 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE:
1118 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
1119 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001120 return true;
1121 default:
1122 return false;
1123 }
1124 }
1125
Emilian Peev0e971f32019-02-22 17:34:29 -08001126 private void postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback,
1127 final Executor executor) {
1128 final long ident = Binder.clearCallingIdentity();
1129 try {
1130 executor.execute(
1131 new Runnable() {
1132 @Override
1133 public void run() {
1134 callback.onCameraAccessPrioritiesChanged();
1135 }
1136 });
1137 } finally {
1138 Binder.restoreCallingIdentity(ident);
1139 }
1140 }
1141
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001142 private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001143 final String id, final int status) {
1144 if (isAvailable(status)) {
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001145 final long ident = Binder.clearCallingIdentity();
1146 try {
1147 executor.execute(
1148 new Runnable() {
1149 @Override
1150 public void run() {
1151 callback.onCameraAvailable(id);
1152 }
1153 });
1154 } finally {
1155 Binder.restoreCallingIdentity(ident);
1156 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001157 } else {
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001158 final long ident = Binder.clearCallingIdentity();
1159 try {
1160 executor.execute(
1161 new Runnable() {
1162 @Override
1163 public void run() {
1164 callback.onCameraUnavailable(id);
1165 }
1166 });
1167 } finally {
1168 Binder.restoreCallingIdentity(ident);
1169 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001170 }
1171 }
1172
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001173 private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor,
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001174 final String id, final int status) {
1175 switch(status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001176 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001177 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: {
1178 final long ident = Binder.clearCallingIdentity();
1179 try {
1180 executor.execute(() -> {
1181 callback.onTorchModeChanged(id, status ==
1182 ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001183 });
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001184 } finally {
1185 Binder.restoreCallingIdentity(ident);
1186 }
1187 }
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001188 break;
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001189 default: {
1190 final long ident = Binder.clearCallingIdentity();
1191 try {
1192 executor.execute(() -> {
1193 callback.onTorchModeUnavailable(id);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001194 });
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001195 } finally {
1196 Binder.restoreCallingIdentity(ident);
1197 }
1198 }
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001199 break;
1200 }
1201 }
1202
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001203 /**
1204 * Send the state of all known cameras to the provided listener, to initialize
1205 * the listener's knowledge of camera state.
1206 */
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001207 private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001208 for (int i = 0; i < mDeviceStatus.size(); i++) {
1209 String id = mDeviceStatus.keyAt(i);
1210 Integer status = mDeviceStatus.valueAt(i);
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001211 postSingleUpdate(callback, executor, id, status);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001212 }
1213 }
1214
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001215 private void onStatusChangedLocked(int status, String id) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001216 if (DEBUG) {
Igor Murashkine363fbb2013-06-25 20:26:06 +00001217 Log.v(TAG,
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001218 String.format("Camera id %s has status changed to 0x%x", id, status));
1219 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001220
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001221 if (!validStatus(status)) {
1222 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id,
1223 status));
1224 return;
1225 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001226
Yin-Chia Yeh0c961162018-01-02 11:17:58 -08001227 Integer oldStatus;
1228 if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
1229 oldStatus = mDeviceStatus.remove(id);
1230 } else {
1231 oldStatus = mDeviceStatus.put(id, status);
1232 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001233
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001234 if (oldStatus != null && oldStatus == status) {
1235 if (DEBUG) {
Igor Murashkine363fbb2013-06-25 20:26:06 +00001236 Log.v(TAG, String.format(
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001237 "Device status changed to 0x%x, which is what it already was",
1238 status));
Igor Murashkine363fbb2013-06-25 20:26:06 +00001239 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001240 return;
1241 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001242
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001243 // TODO: consider abstracting out this state minimization + transition
1244 // into a separate
1245 // more easily testable class
1246 // i.e. (new State()).addState(STATE_AVAILABLE)
1247 // .addState(STATE_NOT_AVAILABLE)
1248 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
1249 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
1250 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
1251 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
Igor Murashkine363fbb2013-06-25 20:26:06 +00001252
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001253 // Translate all the statuses to either 'available' or 'not available'
1254 // available -> available => no new update
1255 // not available -> not available => no new update
1256 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
1257 if (DEBUG) {
Igor Murashkine363fbb2013-06-25 20:26:06 +00001258 Log.v(TAG,
1259 String.format(
Andreas Gampe02ffb2a2015-03-15 14:43:31 -07001260 "Device status was previously available (%b), " +
1261 " and is now again available (%b)" +
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001262 "so no new client visible update will be sent",
Andreas Gampe02ffb2a2015-03-15 14:43:31 -07001263 isAvailable(oldStatus), isAvailable(status)));
Igor Murashkine363fbb2013-06-25 20:26:06 +00001264 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001265 return;
1266 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001267
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001268 final int callbackCount = mCallbackMap.size();
1269 for (int i = 0; i < callbackCount; i++) {
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001270 Executor executor = mCallbackMap.valueAt(i);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001271 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001272
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001273 postSingleUpdate(callback, executor, id, status);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001274 }
1275 } // onStatusChangedLocked
1276
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001277 private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001278 for (int i = 0; i < mTorchStatus.size(); i++) {
1279 String id = mTorchStatus.keyAt(i);
1280 Integer status = mTorchStatus.valueAt(i);
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001281 postSingleTorchUpdate(callback, executor, id, status);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001282 }
1283 }
1284
1285 private void onTorchStatusChangedLocked(int status, String id) {
1286 if (DEBUG) {
1287 Log.v(TAG,
1288 String.format("Camera id %s has torch status changed to 0x%x", id, status));
1289 }
1290
1291 if (!validTorchStatus(status)) {
1292 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id,
1293 status));
1294 return;
1295 }
1296
1297 Integer oldStatus = mTorchStatus.put(id, status);
1298 if (oldStatus != null && oldStatus == status) {
1299 if (DEBUG) {
1300 Log.v(TAG, String.format(
1301 "Torch status changed to 0x%x, which is what it already was",
1302 status));
1303 }
1304 return;
1305 }
1306
1307 final int callbackCount = mTorchCallbackMap.size();
1308 for (int i = 0; i < callbackCount; i++) {
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001309 final Executor executor = mTorchCallbackMap.valueAt(i);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001310 final TorchCallback callback = mTorchCallbackMap.keyAt(i);
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001311 postSingleTorchUpdate(callback, executor, id, status);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001312 }
1313 } // onTorchStatusChangedLocked
1314
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001315 /**
1316 * Register a callback to be notified about camera device availability with the
1317 * global listener singleton.
1318 *
1319 * @param callback the new callback to send camera availability notices to
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001320 * @param executor The executor which should invoke the callback. May not be null.
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001321 */
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001322 public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001323 synchronized (mLock) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -07001324 connectCameraServiceLocked();
1325
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001326 Executor oldExecutor = mCallbackMap.put(callback, executor);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001327 // For new callbacks, provide initial availability information
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001328 if (oldExecutor == null) {
1329 updateCallbackLocked(callback, executor);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001330 }
Chien-Yu Chen0ac408e2016-03-17 10:51:25 -07001331
1332 // If not connected to camera service, schedule a reconnect to camera service.
1333 if (mCameraService == null) {
1334 scheduleCameraServiceReconnectionLocked();
1335 }
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001336 }
1337 }
1338
1339 /**
1340 * Remove a previously-added callback; the callback will no longer receive connection and
1341 * disconnection callbacks, and is no longer referenced by the global listener singleton.
1342 *
1343 * @param callback The callback to remove from the notification list
1344 */
1345 public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
1346 synchronized (mLock) {
1347 mCallbackMap.remove(callback);
1348 }
1349 }
1350
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001351 public void registerTorchCallback(TorchCallback callback, Executor executor) {
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001352 synchronized(mLock) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -07001353 connectCameraServiceLocked();
1354
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001355 Executor oldExecutor = mTorchCallbackMap.put(callback, executor);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001356 // For new callbacks, provide initial torch information
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001357 if (oldExecutor == null) {
1358 updateTorchCallbackLocked(callback, executor);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001359 }
Chien-Yu Chen0ac408e2016-03-17 10:51:25 -07001360
1361 // If not connected to camera service, schedule a reconnect to camera service.
1362 if (mCameraService == null) {
1363 scheduleCameraServiceReconnectionLocked();
1364 }
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001365 }
1366 }
1367
1368 public void unregisterTorchCallback(TorchCallback callback) {
1369 synchronized(mLock) {
1370 mTorchCallbackMap.remove(callback);
1371 }
1372 }
1373
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001374 /**
1375 * Callback from camera service notifying the process about camera availability changes
1376 */
1377 @Override
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -08001378 public void onStatusChanged(int status, String cameraId) throws RemoteException {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001379 synchronized(mLock) {
Eino-Ville Talvala283ae234f2016-12-08 13:38:52 -08001380 onStatusChangedLocked(status, cameraId);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001381 }
1382 }
1383
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001384 @Override
1385 public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
1386 synchronized (mLock) {
1387 onTorchStatusChangedLocked(status, cameraId);
1388 }
1389 }
1390
Emilian Peev0e971f32019-02-22 17:34:29 -08001391 @Override
1392 public void onCameraAccessPrioritiesChanged() {
1393 synchronized (mLock) {
1394 final int callbackCount = mCallbackMap.size();
1395 for (int i = 0; i < callbackCount; i++) {
1396 Executor executor = mCallbackMap.valueAt(i);
1397 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
1398
1399 postSingleAccessPriorityChangeUpdate(callback, executor);
1400 }
1401 }
1402 }
1403
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001404 /**
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001405 * Try to connect to camera service after some delay if any client registered camera
1406 * availability callback or torch status callback.
1407 */
1408 private void scheduleCameraServiceReconnectionLocked() {
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001409 if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) {
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001410 // Not necessary to reconnect camera service if no client registers a callback.
1411 return;
1412 }
1413
1414 if (DEBUG) {
1415 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS +
1416 " ms");
1417 }
1418
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001419 try {
1420 mScheduler.schedule(() -> {
1421 ICameraService cameraService = getCameraService();
1422 if (cameraService == null) {
1423 synchronized(mLock) {
1424 if (DEBUG) {
1425 Log.v(TAG, "Reconnecting Camera Service failed.");
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001426 }
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001427 scheduleCameraServiceReconnectionLocked();
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001428 }
Emilian Peev9a0ec8c2018-02-28 14:53:30 +00001429 }
1430 }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
1431 } catch (RejectedExecutionException e) {
1432 Log.e(TAG, "Failed to schedule camera service re-connect: " + e);
1433 }
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001434 }
1435
1436 /**
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001437 * Listener for camera service death.
1438 *
1439 * <p>The camera service isn't supposed to die under any normal circumstances, but can be
1440 * turned off during debug, or crash due to bugs. So detect that and null out the interface
1441 * object, so that the next calls to the manager can try to reconnect.</p>
1442 */
1443 public void binderDied() {
1444 synchronized(mLock) {
1445 // Only do this once per service death
1446 if (mCameraService == null) return;
1447
1448 mCameraService = null;
1449
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001450 // Tell listeners that the cameras and torch modes are unavailable and schedule a
1451 // reconnection to camera service. When camera service is reconnected, the camera
1452 // and torch statuses will be updated.
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001453 for (int i = 0; i < mDeviceStatus.size(); i++) {
1454 String cameraId = mDeviceStatus.keyAt(i);
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001455 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001456 }
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001457 for (int i = 0; i < mTorchStatus.size(); i++) {
1458 String cameraId = mTorchStatus.keyAt(i);
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001459 onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE,
1460 cameraId);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001461 }
1462
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001463 scheduleCameraServiceReconnectionLocked();
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001464 }
1465 }
1466
1467 } // CameraManagerGlobal
1468
Igor Murashkine363fbb2013-06-25 20:26:06 +00001469} // CameraManager