blob: ffd9d8969e65cbc7c4f8454c51a490077a1a0f1d [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
Eino-Ville Talvala8b905572015-05-14 15:43:01 -070019import android.annotation.RequiresPermission;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
Igor Murashkine363fbb2013-06-25 20:26:06 +000022import android.content.Context;
23import android.hardware.ICameraService;
24import android.hardware.ICameraServiceListener;
Igor Murashkin4961bc82014-06-17 12:04:07 -070025import android.hardware.CameraInfo;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070026import android.hardware.camera2.impl.CameraMetadataNative;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070027import android.hardware.camera2.legacy.CameraDeviceUserShim;
Igor Murashkin4961bc82014-06-17 12:04:07 -070028import android.hardware.camera2.legacy.LegacyMetadataMapper;
Igor Murashkine363fbb2013-06-25 20:26:06 +000029import android.os.IBinder;
Chien-Yu Chenafa91392015-02-11 11:23:28 -080030import android.os.Binder;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080031import android.os.DeadObjectException;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070032import android.os.Handler;
33import android.os.Looper;
Igor Murashkine363fbb2013-06-25 20:26:06 +000034import android.os.RemoteException;
35import android.os.ServiceManager;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080036import android.os.ServiceSpecificException;
Igor Murashkine363fbb2013-06-25 20:26:06 +000037import android.util.Log;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070038import android.util.ArrayMap;
Igor Murashkine363fbb2013-06-25 20:26:06 +000039
40import java.util.ArrayList;
Igor Murashkine363fbb2013-06-25 20:26:06 +000041
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080042/**
Eino-Ville Talvalab67a3b32014-06-06 13:07:20 -070043 * <p>A system service manager for detecting, characterizing, and connecting to
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080044 * {@link CameraDevice CameraDevices}.</p>
45 *
46 * <p>You can get an instance of this class by calling
47 * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p>
48 *
49 * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre>
50 *
51 * <p>For more details about communicating with camera devices, read the Camera
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070052 * developer guide or the {@link android.hardware.camera2 camera2}
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080053 * package documentation.</p>
54 */
55public final class CameraManager {
56
Ruben Brunk85c43882014-02-21 17:40:51 -080057 private static final String TAG = "CameraManager";
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070058 private final boolean DEBUG = false;
Ruben Brunk85c43882014-02-21 17:40:51 -080059
Igor Murashkine363fbb2013-06-25 20:26:06 +000060 private static final int USE_CALLING_UID = -1;
61
Igor Murashkin4961bc82014-06-17 12:04:07 -070062 @SuppressWarnings("unused")
63 private static final int API_VERSION_1 = 1;
64 private static final int API_VERSION_2 = 2;
65
Eino-Ville Talvala57176122015-08-14 13:11:16 -070066 private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0;
67 private static final int CAMERA_TYPE_ALL = 1;
68
Igor Murashkine363fbb2013-06-25 20:26:06 +000069 private ArrayList<String> mDeviceIdList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070070
Igor Murashkine363fbb2013-06-25 20:26:06 +000071 private final Context mContext;
72 private final Object mLock = new Object();
73
74 /**
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080075 * @hide
76 */
Igor Murashkine363fbb2013-06-25 20:26:06 +000077 public CameraManager(Context context) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -070078 synchronized(mLock) {
79 mContext = context;
Igor Murashkine363fbb2013-06-25 20:26:06 +000080 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080081 }
82
83 /**
Ruben Brunk0f2be132015-04-17 14:23:40 -070084 * Return the list of currently connected camera devices by identifier, including
85 * cameras that may be in use by other camera API clients.
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070086 *
87 * <p>Non-removable cameras use integers starting at 0 for their
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080088 * identifiers, while removable cameras have a unique identifier for each
89 * individual device, even if they are the same model.</p>
90 *
91 * @return The list of currently connected camera devices.
92 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -070093 @NonNull
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070094 public String[] getCameraIdList() throws CameraAccessException {
Igor Murashkine363fbb2013-06-25 20:26:06 +000095 synchronized (mLock) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -070096 // ID list creation handles various known failures in device enumeration, so only
97 // exceptions it'll throw are unexpected, and should be propagated upward.
98 return getOrCreateDeviceIdListLocked().toArray(new String[0]);
Igor Murashkine363fbb2013-06-25 20:26:06 +000099 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800100 }
101
102 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700103 * Register a callback to be notified about camera device availability.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800104 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700105 * <p>Registering the same callback again will replace the handler with the
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700106 * new one provided.</p>
Igor Murashkine363fbb2013-06-25 20:26:06 +0000107 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700108 * <p>The first time a callback is registered, it is immediately called
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700109 * with the availability status of all currently known camera devices.</p>
110 *
Ruben Brunk0f2be132015-04-17 14:23:40 -0700111 * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera
112 * device is opened by any camera API client. As of API level 23, other camera API clients may
113 * still be able to open such a camera device, evicting the existing client if they have higher
114 * priority than the existing client of a camera device. See open() for more details.</p>
115 *
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700116 * <p>Since this callback will be registered with the camera service, remember to unregister it
117 * once it is no longer needed; otherwise the callback will continue to receive events
118 * indefinitely and it may prevent other resources from being released. Specifically, the
119 * callbacks will be invoked independently of the general activity lifecycle and independently
120 * of the state of individual CameraManager instances.</p>
121 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700122 * @param callback the new callback to send camera availability notices to
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800123 * @param handler The handler on which the callback should be invoked, or {@code null} to use
124 * the current thread's {@link android.os.Looper looper}.
125 *
126 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
127 * no looper.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800128 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700129 public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
130 @Nullable Handler handler) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700131 if (handler == null) {
132 Looper looper = Looper.myLooper();
133 if (looper == null) {
134 throw new IllegalArgumentException(
135 "No handler given, and current thread has no looper!");
136 }
137 handler = new Handler(looper);
138 }
139
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700140 CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800141 }
142
143 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700144 * Remove a previously-added callback; the callback will no longer receive connection and
145 * disconnection callbacks.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800146 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700147 * <p>Removing a callback that isn't registered has no effect.</p>
Igor Murashkine363fbb2013-06-25 20:26:06 +0000148 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700149 * @param callback The callback to remove from the notification list
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800150 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700151 public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700152 CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800153 }
154
155 /**
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800156 * Register a callback to be notified about torch mode status.
157 *
158 * <p>Registering the same callback again will replace the handler with the
159 * new one provided.</p>
160 *
161 * <p>The first time a callback is registered, it is immediately called
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700162 * with the torch mode status of all currently known camera devices with a flash unit.</p>
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800163 *
164 * <p>Since this callback will be registered with the camera service, remember to unregister it
165 * once it is no longer needed; otherwise the callback will continue to receive events
166 * indefinitely and it may prevent other resources from being released. Specifically, the
167 * callbacks will be invoked independently of the general activity lifecycle and independently
168 * of the state of individual CameraManager instances.</p>
169 *
170 * @param callback The new callback to send torch mode status to
171 * @param handler The handler on which the callback should be invoked, or {@code null} to use
172 * the current thread's {@link android.os.Looper looper}.
173 *
174 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
175 * no looper.
176 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700177 public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800178 if (handler == null) {
179 Looper looper = Looper.myLooper();
180 if (looper == null) {
181 throw new IllegalArgumentException(
182 "No handler given, and current thread has no looper!");
183 }
184 handler = new Handler(looper);
185 }
186 CameraManagerGlobal.get().registerTorchCallback(callback, handler);
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800187 }
188
189 /**
190 * Remove a previously-added callback; the callback will no longer receive torch mode status
191 * callbacks.
192 *
193 * <p>Removing a callback that isn't registered has no effect.</p>
194 *
195 * @param callback The callback to remove from the notification list
196 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700197 public void unregisterTorchCallback(@NonNull TorchCallback callback) {
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800198 CameraManagerGlobal.get().unregisterTorchCallback(callback);
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800199 }
200
201 /**
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800202 * <p>Query the capabilities of a camera device. These capabilities are
203 * immutable for a given camera.</p>
204 *
205 * @param cameraId The id of the camera device to query
206 * @return The properties of the given camera
207 *
208 * @throws IllegalArgumentException if the cameraId does not match any
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700209 * known camera device.
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700210 * @throws CameraAccessException if the camera device has been disconnected.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800211 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700212 * @see #getCameraIdList
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800213 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
214 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700215 @NonNull
216 public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800217 throws CameraAccessException {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700218 CameraCharacteristics characteristics = null;
Igor Murashkine363fbb2013-06-25 20:26:06 +0000219
220 synchronized (mLock) {
221 if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
222 throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
223 " currently connected camera device", cameraId));
224 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000225
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700226 int id = Integer.valueOf(cameraId);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700227
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700228 /*
229 * Get the camera characteristics from the camera service directly if it supports it,
230 * otherwise get them from the legacy shim instead.
231 */
Igor Murashkin4961bc82014-06-17 12:04:07 -0700232
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700233 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700234 if (cameraService == null) {
235 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
236 "Camera service is currently unavailable");
237 }
Igor Murashkin4961bc82014-06-17 12:04:07 -0700238 try {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700239 if (!supportsCamera2ApiLocked(cameraId)) {
240 // Legacy backwards compatibility path; build static info from the camera
241 // parameters
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800242 String parameters = cameraService.getLegacyParameters(id);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700243
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800244 CameraInfo info = cameraService.getCameraInfo(id);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700245
246 characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
247 } else {
248 // Normal path: Get the camera characteristics directly from the camera service
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800249 CameraMetadataNative info = cameraService.getCameraCharacteristics(id);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700250
251 characteristics = new CameraCharacteristics(info);
252 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800253 } catch (ServiceSpecificException e) {
254 throwAsPublicException(e);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700255 } catch (RemoteException e) {
256 // Camera service died - act as if the camera was disconnected
257 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
258 "Camera service is currently unavailable", e);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700259 }
Zhijun He20011882013-09-25 10:05:59 -0700260 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700261 return characteristics;
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800262 }
263
264 /**
Ruben Brunk0f2be132015-04-17 14:23:40 -0700265 * Helper for opening a connection to a camera with the given ID.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800266 *
267 * @param cameraId The unique identifier of the camera device to open
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700268 * @param callback The callback for the camera. Must not be null.
269 * @param handler The handler to invoke the callback on. Must not be null.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800270 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800271 * @throws CameraAccessException if the camera is disabled by device policy,
Ruben Brunk0f2be132015-04-17 14:23:40 -0700272 * too many camera devices are already open, or the cameraId does not match
Ruben Brunk66ef6452013-08-08 13:05:30 -0700273 * any currently available camera device.
274 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800275 * @throws SecurityException if the application does not have permission to
276 * access the camera
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700277 * @throws IllegalArgumentException if callback or handler is null.
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700278 * @return A handle to the newly-created camera device.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800279 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700280 * @see #getCameraIdList
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800281 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
282 */
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700283 private CameraDevice openCameraDeviceUserAsync(String cameraId,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700284 CameraDevice.StateCallback callback, Handler handler)
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700285 throws CameraAccessException {
Ruben Brunk57493682014-05-27 18:58:08 -0700286 CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700287 CameraDevice device = null;
Igor Murashkine363fbb2013-06-25 20:26:06 +0000288
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800289 synchronized (mLock) {
Igor Murashkin70725502013-06-25 20:27:06 +0000290
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800291 ICameraDeviceUser cameraUser = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000292
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800293 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
294 new android.hardware.camera2.impl.CameraDeviceImpl(
295 cameraId,
296 callback,
297 handler,
298 characteristics);
Igor Murashkin70725502013-06-25 20:27:06 +0000299
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800300 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700301
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800302 int id;
303 try {
304 id = Integer.parseInt(cameraId);
305 } catch (NumberFormatException e) {
306 throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
307 + cameraId);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000308 }
309
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800310 try {
311 if (supportsCamera2ApiLocked(cameraId)) {
312 // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
313 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
314 if (cameraService == null) {
315 throw new ServiceSpecificException(
316 ICameraService.ERROR_DISCONNECTED,
317 "Camera service is currently unavailable");
318 }
319 cameraUser = cameraService.connectDevice(callbacks, id,
320 mContext.getOpPackageName(), USE_CALLING_UID);
321 } else {
322 // Use legacy camera implementation for HAL1 devices
323 Log.i(TAG, "Using legacy camera HAL.");
324 cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
325 }
326 } catch (ServiceSpecificException e) {
327 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
328 throw new AssertionError("Should've gone down the shim path");
329 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
330 e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
331 e.errorCode == ICameraService.ERROR_DISABLED ||
332 e.errorCode == ICameraService.ERROR_DISCONNECTED ||
333 e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
334 // Received one of the known connection errors
335 // The remote camera device cannot be connected to, so
336 // set the local camera to the startup error state
337 deviceImpl.setRemoteFailure(e);
338
339 if (e.errorCode == ICameraService.ERROR_DISABLED ||
340 e.errorCode == ICameraService.ERROR_DISCONNECTED ||
341 e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
342 // Per API docs, these failures call onError and throw
343 throwAsPublicException(e);
344 }
345 } else {
346 // Unexpected failure - rethrow
347 throwAsPublicException(e);
348 }
349 } catch (RemoteException e) {
350 // Camera service died - act as if it's a CAMERA_DISCONNECTED case
351 ServiceSpecificException sse = new ServiceSpecificException(
352 ICameraService.ERROR_DISCONNECTED,
353 "Camera service is currently unavailable");
354 deviceImpl.setRemoteFailure(sse);
355 throwAsPublicException(sse);
356 }
357
358 // TODO: factor out callback to be non-nested, then move setter to constructor
359 // For now, calling setRemoteDevice will fire initial
360 // onOpened/onUnconfigured callbacks.
Shuzhen Wang78392932016-03-04 15:26:01 -0800361 // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
362 // cameraUser dies during setup.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800363 deviceImpl.setRemoteDevice(cameraUser);
364 device = deviceImpl;
Igor Murashkine363fbb2013-06-25 20:26:06 +0000365 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800366
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700367 return device;
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800368 }
369
370 /**
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700371 * Open a connection to a camera with the given ID.
372 *
373 * <p>Use {@link #getCameraIdList} to get the list of available camera
374 * devices. Note that even if an id is listed, open may fail if the device
375 * is disconnected between the calls to {@link #getCameraIdList} and
Ruben Brunk0f2be132015-04-17 14:23:40 -0700376 * {@link #openCamera}, or if a higher-priority camera API client begins using the
377 * camera device.</p>
378 *
379 * <p>As of API level 23, devices for which the
380 * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the
381 * device being in use by a lower-priority, background camera API client can still potentially
382 * be opened by calling this method when the calling camera API client has a higher priority
383 * than the current camera API client using this device. In general, if the top, foreground
384 * activity is running within your application process, your process will be given the highest
385 * priority when accessing the camera, and this method will succeed even if the camera device is
386 * in use by another camera API client. Any lower-priority application that loses control of the
387 * camera in this way will receive an
388 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700389 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700390 * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700391 * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
392 * for operation by calling {@link CameraDevice#createCaptureSession} and
393 * {@link CameraDevice#createCaptureRequest}</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700394 *
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700395 * <!--
396 * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
397 * on the returned CameraDevice instance will be queued up until the device startup has
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700398 * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700399 * called. The pending operations are then processed in order.</p>
400 * -->
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700401 * <p>If the camera becomes disconnected during initialization
402 * after this function call returns,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700403 * {@link CameraDevice.StateCallback#onDisconnected} with a
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700404 * {@link CameraDevice} in the disconnected state (and
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700405 * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700406 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700407 * <p>If opening the camera device fails, then the device callback's
408 * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700409 * calls on the camera device will throw a {@link CameraAccessException}.</p>
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700410 *
411 * @param cameraId
412 * The unique identifier of the camera device to open
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700413 * @param callback
414 * The callback which is invoked once the camera is opened
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700415 * @param handler
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700416 * The handler on which the callback should be invoked, or
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700417 * {@code null} to use the current thread's {@link android.os.Looper looper}.
418 *
419 * @throws CameraAccessException if the camera is disabled by device policy,
Ruben Brunk0f2be132015-04-17 14:23:40 -0700420 * has been disconnected, or is being used by a higher-priority camera API client.
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700421 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700422 * @throws IllegalArgumentException if cameraId or the callback was null,
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700423 * or the cameraId does not match any currently or previously available
424 * camera device.
425 *
426 * @throws SecurityException if the application does not have permission to
427 * access the camera
428 *
429 * @see #getCameraIdList
430 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
431 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700432 @RequiresPermission(android.Manifest.permission.CAMERA)
433 public void openCamera(@NonNull String cameraId,
434 @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700435 throws CameraAccessException {
436
437 if (cameraId == null) {
438 throw new IllegalArgumentException("cameraId was null");
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700439 } else if (callback == null) {
440 throw new IllegalArgumentException("callback was null");
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700441 } else if (handler == null) {
442 if (Looper.myLooper() != null) {
443 handler = new Handler();
444 } else {
445 throw new IllegalArgumentException(
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700446 "Handler argument is null, but no looper exists in the calling thread");
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700447 }
448 }
449
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700450 openCameraDeviceUserAsync(cameraId, callback, handler);
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700451 }
452
453 /**
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800454 * Set the flash unit's torch mode of the camera of the given ID without opening the camera
455 * device.
456 *
457 * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use
458 * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit.
459 * Note that even if a camera device has a flash unit, turning on the torch mode may fail
460 * if the camera device or other camera resources needed to turn on the torch mode are in use.
461 * </p>
462 *
463 * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully,
464 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked.
465 * However, even if turning on the torch mode is successful, the application does not have the
466 * exclusive ownership of the flash unit or the camera device. The torch mode will be turned
467 * off and becomes unavailable when the camera device that the flash unit belongs to becomes
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800468 * unavailable or when other camera resources to keep the torch on become unavailable (
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800469 * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also,
470 * other applications are free to call {@link #setTorchMode} to turn off the torch mode (
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800471 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest
472 * application that turned on the torch mode exits, the torch mode will be turned off.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800473 *
474 * @param cameraId
475 * The unique identifier of the camera device that the flash unit belongs to.
476 * @param enabled
477 * The desired state of the torch mode for the target camera device. Set to
478 * {@code true} to turn on the torch mode. Set to {@code false} to turn off the
479 * torch mode.
480 *
481 * @throws CameraAccessException if it failed to access the flash unit.
482 * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
483 * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
484 * other camera resources needed to turn on the torch mode are in use.
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800485 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera
486 * service is not available.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800487 *
488 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
489 * or previously available camera device, or the camera device doesn't have a
490 * flash unit.
491 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700492 public void setTorchMode(@NonNull String cameraId, boolean enabled)
493 throws CameraAccessException {
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800494 CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800495 }
496
497 /**
Ruben Brunk0f2be132015-04-17 14:23:40 -0700498 * A callback for camera devices becoming available or unavailable to open.
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700499 *
500 * <p>Cameras become available when they are no longer in use, or when a new
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800501 * removable camera is connected. They become unavailable when some
502 * application or service starts using a camera, or when a removable camera
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700503 * is disconnected.</p>
504 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700505 * <p>Extend this callback and pass an instance of the subclass to
506 * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability
Eino-Ville Talvalab67a3b32014-06-06 13:07:20 -0700507 * changes.</p>
508 *
Philip P. Moltmann85e1d502015-10-26 08:50:04 -0700509 * @see #registerAvailabilityCallback
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800510 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700511 public static abstract class AvailabilityCallback {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700512
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800513 /**
514 * A new camera has become available to use.
515 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700516 * <p>The default implementation of this method does nothing.</p>
517 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800518 * @param cameraId The unique identifier of the new camera.
519 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700520 public void onCameraAvailable(@NonNull String cameraId) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700521 // default empty implementation
522 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800523
524 /**
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700525 * A previously-available camera has become unavailable for use.
526 *
527 * <p>If an application had an active CameraDevice instance for the
528 * now-disconnected camera, that application will receive a
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700529 * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p>
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700530 *
531 * <p>The default implementation of this method does nothing.</p>
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800532 *
533 * @param cameraId The unique identifier of the disconnected camera.
534 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700535 public void onCameraUnavailable(@NonNull String cameraId) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700536 // default empty implementation
537 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800538 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000539
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700540 /**
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800541 * A callback for camera flash torch modes becoming unavailable, disabled, or enabled.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800542 *
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800543 * <p>The torch mode becomes unavailable when the camera device it belongs to becomes
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700544 * unavailable or other camera resources it needs become busy due to other higher priority
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800545 * camera activities. The torch mode becomes disabled when it was turned off or when the camera
546 * device it belongs to is no longer in use and other camera resources it needs are no longer
547 * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to
548 * turn off the camera's torch mode, or when an application turns on another camera's torch mode
549 * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes
550 * enabled when it is turned on via {@link #setTorchMode}.</p>
551 *
552 * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled
553 * or enabled state.</p>
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800554 *
555 * <p>Extend this callback and pass an instance of the subclass to
556 * {@link CameraManager#registerTorchCallback} to be notified of such status changes.
557 * </p>
558 *
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700559 * @see #registerTorchCallback
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800560 */
561 public static abstract class TorchCallback {
562 /**
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800563 * A camera's torch mode has become unavailable to set via {@link #setTorchMode}.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800564 *
565 * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be
566 * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800567 * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or
568 * enabled state again.</p>
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800569 *
570 * <p>The default implementation of this method does nothing.</p>
571 *
572 * @param cameraId The unique identifier of the camera whose torch mode has become
573 * unavailable.
574 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700575 public void onTorchModeUnavailable(@NonNull String cameraId) {
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800576 // default empty implementation
577 }
578
579 /**
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800580 * A camera's torch mode has become enabled or disabled and can be changed via
581 * {@link #setTorchMode}.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800582 *
583 * <p>The default implementation of this method does nothing.</p>
584 *
585 * @param cameraId The unique identifier of the camera whose torch mode has been changed.
586 *
587 * @param enabled The state that the torch mode of the camera has been changed to.
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800588 * {@code true} when the torch mode has become on and available to be turned
589 * off. {@code false} when the torch mode has becomes off and available to
590 * be turned on.
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800591 */
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700592 public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
Chien-Yu Chenc8c109b2015-01-09 15:52:37 -0800593 // default empty implementation
594 }
595 }
596
597 /**
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800598 * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces
599 * into the correct public exceptions.
600 *
601 * @hide
602 */
603 public static void throwAsPublicException(Throwable t) throws CameraAccessException {
604 if (t instanceof ServiceSpecificException) {
605 ServiceSpecificException e = (ServiceSpecificException) t;
606 int reason = CameraAccessException.CAMERA_ERROR;
607 switch(e.errorCode) {
608 case ICameraService.ERROR_DISCONNECTED:
609 reason = CameraAccessException.CAMERA_DISCONNECTED;
610 break;
611 case ICameraService.ERROR_DISABLED:
612 reason = CameraAccessException.CAMERA_DISABLED;
613 break;
614 case ICameraService.ERROR_CAMERA_IN_USE:
615 reason = CameraAccessException.CAMERA_IN_USE;
616 break;
617 case ICameraService.ERROR_MAX_CAMERAS_IN_USE:
618 reason = CameraAccessException.MAX_CAMERAS_IN_USE;
619 break;
620 case ICameraService.ERROR_DEPRECATED_HAL:
621 reason = CameraAccessException.CAMERA_DEPRECATED_HAL;
622 break;
623 case ICameraService.ERROR_ILLEGAL_ARGUMENT:
624 case ICameraService.ERROR_ALREADY_EXISTS:
625 throw new IllegalArgumentException(e.getMessage(), e);
626 case ICameraService.ERROR_PERMISSION_DENIED:
627 throw new SecurityException(e.getMessage(), e);
628 case ICameraService.ERROR_TIMED_OUT:
629 case ICameraService.ERROR_INVALID_OPERATION:
630 default:
631 reason = CameraAccessException.CAMERA_ERROR;
632 }
633 throw new CameraAccessException(reason, e.getMessage(), e);
634 } else if (t instanceof DeadObjectException) {
635 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
636 "Camera service has died unexpectedly",
637 t);
638 } else if (t instanceof RemoteException) {
639 throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
640 " which should never happen.", t);
641 } else if (t instanceof RuntimeException) {
642 RuntimeException e = (RuntimeException) t;
643 throw e;
644 }
645 }
646
647 /**
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700648 * Return or create the list of currently connected camera devices.
649 *
650 * <p>In case of errors connecting to the camera service, will return an empty list.</p>
651 */
Igor Murashkine363fbb2013-06-25 20:26:06 +0000652 private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
653 if (mDeviceIdList == null) {
654 int numCameras = 0;
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700655 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700656 ArrayList<String> deviceIdList = new ArrayList<>();
657
658 // If no camera service, then no devices
659 if (cameraService == null) {
660 return deviceIdList;
661 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000662
663 try {
Eino-Ville Talvala57176122015-08-14 13:11:16 -0700664 numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL);
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800665 } catch(ServiceSpecificException e) {
666 throwAsPublicException(e);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000667 } catch (RemoteException e) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700668 // camera service just died - if no camera service, then no devices
669 return deviceIdList;
Igor Murashkine363fbb2013-06-25 20:26:06 +0000670 }
671
Igor Murashkine363fbb2013-06-25 20:26:06 +0000672 for (int i = 0; i < numCameras; ++i) {
673 // Non-removable cameras use integers starting at 0 for their
674 // identifiers
Zhijun He18fe0ae2013-10-01 11:09:28 -0700675 boolean isDeviceSupported = false;
676 try {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800677 CameraMetadataNative info = cameraService.getCameraCharacteristics(i);
Zhijun He18fe0ae2013-10-01 11:09:28 -0700678 if (!info.isEmpty()) {
679 isDeviceSupported = true;
680 } else {
681 throw new AssertionError("Expected to get non-empty characteristics");
682 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800683 } catch(ServiceSpecificException e) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700684 // DISCONNECTED means that the HAL reported an low-level error getting the
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800685 // device info; ILLEGAL_ARGUMENT means that this devices is not supported.
686 // Skip listing the device. Other errors,
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700687 // propagate exception onward
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800688 if (e.errorCode != ICameraService.ERROR_DISCONNECTED ||
689 e.errorCode != ICameraService.ERROR_ILLEGAL_ARGUMENT) {
690 throwAsPublicException(e);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700691 }
Zhijun He18fe0ae2013-10-01 11:09:28 -0700692 } catch(RemoteException e) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700693 // Camera service died - no devices to list
694 deviceIdList.clear();
695 return deviceIdList;
Zhijun He18fe0ae2013-10-01 11:09:28 -0700696 }
697
698 if (isDeviceSupported) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700699 deviceIdList.add(String.valueOf(i));
700 } else {
701 Log.w(TAG, "Error querying camera device " + i + " for listing.");
Zhijun He18fe0ae2013-10-01 11:09:28 -0700702 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000703
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700704 }
705 mDeviceIdList = deviceIdList;
Igor Murashkine363fbb2013-06-25 20:26:06 +0000706 }
707 return mDeviceIdList;
708 }
709
Igor Murashkin4961bc82014-06-17 12:04:07 -0700710 /**
711 * Queries the camera service if it supports the camera2 api directly, or needs a shim.
712 *
713 * @param cameraId a non-{@code null} camera identifier
714 * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
715 */
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700716 private boolean supportsCamera2ApiLocked(String cameraId) {
717 return supportsCameraApiLocked(cameraId, API_VERSION_2);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700718 }
719
720 /**
721 * Queries the camera service if it supports a camera api directly, or needs a shim.
722 *
723 * @param cameraId a non-{@code null} camera identifier
724 * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
725 * @return {@code true} if connecting will work for that device version.
726 */
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700727 private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
Igor Murashkin4961bc82014-06-17 12:04:07 -0700728 int id = Integer.parseInt(cameraId);
729
730 /*
731 * Possible return values:
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700732 * - NO_ERROR => CameraX API is supported
733 * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
734 * - Remote exception => If the camera service died
Igor Murashkin4961bc82014-06-17 12:04:07 -0700735 *
736 * Anything else is an unexpected error we don't want to recover from.
737 */
Igor Murashkin4961bc82014-06-17 12:04:07 -0700738 try {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700739 ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700740 // If no camera service, no support
741 if (cameraService == null) return false;
Igor Murashkin4961bc82014-06-17 12:04:07 -0700742
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800743 return cameraService.supportsCameraApi(id, apiVersion);
Igor Murashkin4961bc82014-06-17 12:04:07 -0700744 } catch (RemoteException e) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700745 // Camera service is now down, no support for any API level
746 }
747 return false;
748 }
749
750 /**
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700751 * A per-process global camera manager instance, to retain a connection to the camera service,
752 * and to distribute camera availability notices to API-registered callbacks
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700753 */
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700754 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub
755 implements IBinder.DeathRecipient {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700756
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700757 private static final String TAG = "CameraManagerGlobal";
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700758 private final boolean DEBUG = false;
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700759
Chien-Yu Chen0f69b462015-07-13 16:37:13 -0700760 private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000;
761
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700762 // Singleton instance
763 private static final CameraManagerGlobal gCameraManager =
764 new CameraManagerGlobal();
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700765
766 /**
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700767 * This must match the ICameraService definition
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700768 */
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700769 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
Igor Murashkine363fbb2013-06-25 20:26:06 +0000770
Igor Murashkine363fbb2013-06-25 20:26:06 +0000771 // Camera ID -> Status map
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700772 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
Igor Murashkine363fbb2013-06-25 20:26:06 +0000773
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700774 // Registered availablility callbacks and their handlers
775 private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
776 new ArrayMap<AvailabilityCallback, Handler>();
777
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800778 // torch client binder to set the torch mode with.
779 private Binder mTorchClientBinder = new Binder();
780
781 // Camera ID -> Torch status map
782 private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
783
784 // Registered torch callbacks and their handlers
785 private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
786 new ArrayMap<TorchCallback, Handler>();
787
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700788 private final Object mLock = new Object();
789
790 // Access only through getCameraService to deal with binder death
791 private ICameraService mCameraService;
792
793 // Singleton, don't allow construction
794 private CameraManagerGlobal() {
795 }
796
797 public static CameraManagerGlobal get() {
798 return gCameraManager;
799 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000800
801 @Override
802 public IBinder asBinder() {
803 return this;
804 }
805
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700806 /**
807 * Return a best-effort ICameraService.
808 *
809 * <p>This will be null if the camera service is not currently available. If the camera
810 * service has died since the last use of the camera service, will try to reconnect to the
811 * service.</p>
812 */
813 public ICameraService getCameraService() {
814 synchronized(mLock) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700815 connectCameraServiceLocked();
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700816 if (mCameraService == null) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700817 Log.e(TAG, "Camera service is unavailable");
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700818 }
819 return mCameraService;
820 }
821 }
822
823 /**
824 * Connect to the camera service if it's available, and set up listeners.
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700825 * If the service is already connected, do nothing.
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700826 *
827 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
828 */
829 private void connectCameraServiceLocked() {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -0700830 // Only reconnect if necessary
831 if (mCameraService != null) return;
832
833 Log.i(TAG, "Connecting to camera service");
834
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700835 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
836 if (cameraServiceBinder == null) {
837 // Camera service is now down, leave mCameraService as null
838 return;
839 }
840 try {
841 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
842 } catch (RemoteException e) {
843 // Camera service is now down, leave mCameraService as null
844 return;
845 }
846
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800847 ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700848
849 try {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800850 CameraMetadataNative.setupGlobalVendorTagDescriptor();
851 } catch (ServiceSpecificException e) {
852 handleRecoverableSetupErrors(e);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700853 }
854
855 try {
856 cameraService.addListener(this);
857 mCameraService = cameraService;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800858 } catch(ServiceSpecificException e) {
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700859 // Unexpected failure
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800860 throw new IllegalStateException("Failed to register a camera service listener", e);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700861 } catch (RemoteException e) {
862 // Camera service is now down, leave mCameraService as null
863 }
864 }
865
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800866 public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
867 synchronized(mLock) {
868
869 if (cameraId == null) {
870 throw new IllegalArgumentException("cameraId was null");
871 }
872
873 ICameraService cameraService = getCameraService();
874 if (cameraService == null) {
875 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
876 "Camera service is currently unavailable");
877 }
878
879 try {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800880 cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder);
881 } catch(ServiceSpecificException e) {
882 throwAsPublicException(e);
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800883 } catch (RemoteException e) {
884 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
885 "Camera service is currently unavailable");
886 }
887 }
888 }
889
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800890 private void handleRecoverableSetupErrors(ServiceSpecificException e) {
891 switch (e.errorCode) {
892 case ICameraService.ERROR_DISCONNECTED:
893 Log.w(TAG, e.getMessage());
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800894 break;
895 default:
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800896 throw new IllegalStateException(e);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700897 }
898 }
899
Igor Murashkine363fbb2013-06-25 20:26:06 +0000900 private boolean isAvailable(int status) {
901 switch (status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800902 case ICameraServiceListener.STATUS_PRESENT:
Igor Murashkine363fbb2013-06-25 20:26:06 +0000903 return true;
904 default:
905 return false;
906 }
907 }
908
909 private boolean validStatus(int status) {
910 switch (status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800911 case ICameraServiceListener.STATUS_NOT_PRESENT:
912 case ICameraServiceListener.STATUS_PRESENT:
913 case ICameraServiceListener.STATUS_ENUMERATING:
914 case ICameraServiceListener.STATUS_NOT_AVAILABLE:
Igor Murashkine363fbb2013-06-25 20:26:06 +0000915 return true;
916 default:
917 return false;
918 }
919 }
920
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800921 private boolean validTorchStatus(int status) {
922 switch (status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800923 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE:
924 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
925 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800926 return true;
927 default:
928 return false;
929 }
930 }
931
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700932 private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700933 final String id, final int status) {
934 if (isAvailable(status)) {
935 handler.post(
936 new Runnable() {
937 @Override
938 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700939 callback.onCameraAvailable(id);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700940 }
941 });
942 } else {
943 handler.post(
944 new Runnable() {
945 @Override
946 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700947 callback.onCameraUnavailable(id);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700948 }
949 });
950 }
951 }
952
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800953 private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
954 final String id, final int status) {
955 switch(status) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800956 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
957 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800958 handler.post(
959 new Runnable() {
960 @Override
961 public void run() {
962 callback.onTorchModeChanged(id, status ==
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800963 ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
Chien-Yu Chenafa91392015-02-11 11:23:28 -0800964 }
965 });
966 break;
967 default:
968 handler.post(
969 new Runnable() {
970 @Override
971 public void run() {
972 callback.onTorchModeUnavailable(id);
973 }
974 });
975 break;
976 }
977 }
978
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700979 /**
980 * Send the state of all known cameras to the provided listener, to initialize
981 * the listener's knowledge of camera state.
982 */
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700983 private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700984 for (int i = 0; i < mDeviceStatus.size(); i++) {
985 String id = mDeviceStatus.keyAt(i);
986 Integer status = mDeviceStatus.valueAt(i);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700987 postSingleUpdate(callback, handler, id, status);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700988 }
989 }
990
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -0700991 private void onStatusChangedLocked(int status, String id) {
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700992 if (DEBUG) {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000993 Log.v(TAG,
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700994 String.format("Camera id %s has status changed to 0x%x", id, status));
995 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000996
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -0700997 if (!validStatus(status)) {
998 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id,
999 status));
1000 return;
1001 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001002
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001003 Integer oldStatus = mDeviceStatus.put(id, status);
Igor Murashkine363fbb2013-06-25 20:26:06 +00001004
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001005 if (oldStatus != null && oldStatus == status) {
1006 if (DEBUG) {
Igor Murashkine363fbb2013-06-25 20:26:06 +00001007 Log.v(TAG, String.format(
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001008 "Device status changed to 0x%x, which is what it already was",
1009 status));
Igor Murashkine363fbb2013-06-25 20:26:06 +00001010 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001011 return;
1012 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001013
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001014 // TODO: consider abstracting out this state minimization + transition
1015 // into a separate
1016 // more easily testable class
1017 // i.e. (new State()).addState(STATE_AVAILABLE)
1018 // .addState(STATE_NOT_AVAILABLE)
1019 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
1020 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
1021 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
1022 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
Igor Murashkine363fbb2013-06-25 20:26:06 +00001023
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001024 // Translate all the statuses to either 'available' or 'not available'
1025 // available -> available => no new update
1026 // not available -> not available => no new update
1027 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
1028 if (DEBUG) {
Igor Murashkine363fbb2013-06-25 20:26:06 +00001029 Log.v(TAG,
1030 String.format(
Andreas Gampe02ffb2a2015-03-15 14:43:31 -07001031 "Device status was previously available (%b), " +
1032 " and is now again available (%b)" +
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001033 "so no new client visible update will be sent",
Andreas Gampe02ffb2a2015-03-15 14:43:31 -07001034 isAvailable(oldStatus), isAvailable(status)));
Igor Murashkine363fbb2013-06-25 20:26:06 +00001035 }
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001036 return;
1037 }
Igor Murashkine363fbb2013-06-25 20:26:06 +00001038
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001039 final int callbackCount = mCallbackMap.size();
1040 for (int i = 0; i < callbackCount; i++) {
1041 Handler handler = mCallbackMap.valueAt(i);
1042 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001043
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001044 postSingleUpdate(callback, handler, id, status);
Eino-Ville Talvalabd9b1062014-07-22 11:12:24 -07001045 }
1046 } // onStatusChangedLocked
1047
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001048 private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
1049 for (int i = 0; i < mTorchStatus.size(); i++) {
1050 String id = mTorchStatus.keyAt(i);
1051 Integer status = mTorchStatus.valueAt(i);
1052 postSingleTorchUpdate(callback, handler, id, status);
1053 }
1054 }
1055
1056 private void onTorchStatusChangedLocked(int status, String id) {
1057 if (DEBUG) {
1058 Log.v(TAG,
1059 String.format("Camera id %s has torch status changed to 0x%x", id, status));
1060 }
1061
1062 if (!validTorchStatus(status)) {
1063 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id,
1064 status));
1065 return;
1066 }
1067
1068 Integer oldStatus = mTorchStatus.put(id, status);
1069 if (oldStatus != null && oldStatus == status) {
1070 if (DEBUG) {
1071 Log.v(TAG, String.format(
1072 "Torch status changed to 0x%x, which is what it already was",
1073 status));
1074 }
1075 return;
1076 }
1077
1078 final int callbackCount = mTorchCallbackMap.size();
1079 for (int i = 0; i < callbackCount; i++) {
1080 final Handler handler = mTorchCallbackMap.valueAt(i);
1081 final TorchCallback callback = mTorchCallbackMap.keyAt(i);
1082 postSingleTorchUpdate(callback, handler, id, status);
1083 }
1084 } // onTorchStatusChangedLocked
1085
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001086 /**
1087 * Register a callback to be notified about camera device availability with the
1088 * global listener singleton.
1089 *
1090 * @param callback the new callback to send camera availability notices to
1091 * @param handler The handler on which the callback should be invoked. May not be null.
1092 */
1093 public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
1094 synchronized (mLock) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -07001095 connectCameraServiceLocked();
1096
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001097 Handler oldHandler = mCallbackMap.put(callback, handler);
1098 // For new callbacks, provide initial availability information
1099 if (oldHandler == null) {
1100 updateCallbackLocked(callback, handler);
1101 }
Chien-Yu Chen0ac408e2016-03-17 10:51:25 -07001102
1103 // If not connected to camera service, schedule a reconnect to camera service.
1104 if (mCameraService == null) {
1105 scheduleCameraServiceReconnectionLocked();
1106 }
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001107 }
1108 }
1109
1110 /**
1111 * Remove a previously-added callback; the callback will no longer receive connection and
1112 * disconnection callbacks, and is no longer referenced by the global listener singleton.
1113 *
1114 * @param callback The callback to remove from the notification list
1115 */
1116 public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
1117 synchronized (mLock) {
1118 mCallbackMap.remove(callback);
1119 }
1120 }
1121
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001122 public void registerTorchCallback(TorchCallback callback, Handler handler) {
1123 synchronized(mLock) {
Eino-Ville Talvala2ef01732015-05-30 17:56:37 -07001124 connectCameraServiceLocked();
1125
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001126 Handler oldHandler = mTorchCallbackMap.put(callback, handler);
1127 // For new callbacks, provide initial torch information
1128 if (oldHandler == null) {
1129 updateTorchCallbackLocked(callback, handler);
1130 }
Chien-Yu Chen0ac408e2016-03-17 10:51:25 -07001131
1132 // If not connected to camera service, schedule a reconnect to camera service.
1133 if (mCameraService == null) {
1134 scheduleCameraServiceReconnectionLocked();
1135 }
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001136 }
1137 }
1138
1139 public void unregisterTorchCallback(TorchCallback callback) {
1140 synchronized(mLock) {
1141 mTorchCallbackMap.remove(callback);
1142 }
1143 }
1144
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001145 /**
1146 * Callback from camera service notifying the process about camera availability changes
1147 */
1148 @Override
1149 public void onStatusChanged(int status, int cameraId) throws RemoteException {
1150 synchronized(mLock) {
1151 onStatusChangedLocked(status, String.valueOf(cameraId));
1152 }
1153 }
1154
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001155 @Override
1156 public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
1157 synchronized (mLock) {
1158 onTorchStatusChangedLocked(status, cameraId);
1159 }
1160 }
1161
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001162 /**
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001163 * Try to connect to camera service after some delay if any client registered camera
1164 * availability callback or torch status callback.
1165 */
1166 private void scheduleCameraServiceReconnectionLocked() {
1167 final Handler handler;
1168
1169 if (mCallbackMap.size() > 0) {
1170 handler = mCallbackMap.valueAt(0);
1171 } else if (mTorchCallbackMap.size() > 0) {
1172 handler = mTorchCallbackMap.valueAt(0);
1173 } else {
1174 // Not necessary to reconnect camera service if no client registers a callback.
1175 return;
1176 }
1177
1178 if (DEBUG) {
1179 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS +
1180 " ms");
1181 }
1182
1183 handler.postDelayed(
1184 new Runnable() {
1185 @Override
1186 public void run() {
1187 ICameraService cameraService = getCameraService();
1188 if (cameraService == null) {
1189 synchronized(mLock) {
1190 if (DEBUG) {
1191 Log.v(TAG, "Reconnecting Camera Service failed.");
1192 }
1193 scheduleCameraServiceReconnectionLocked();
1194 }
1195 }
1196 }
1197 },
1198 CAMERA_SERVICE_RECONNECT_DELAY_MS);
1199 }
1200
1201 /**
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001202 * Listener for camera service death.
1203 *
1204 * <p>The camera service isn't supposed to die under any normal circumstances, but can be
1205 * turned off during debug, or crash due to bugs. So detect that and null out the interface
1206 * object, so that the next calls to the manager can try to reconnect.</p>
1207 */
1208 public void binderDied() {
1209 synchronized(mLock) {
1210 // Only do this once per service death
1211 if (mCameraService == null) return;
1212
1213 mCameraService = null;
1214
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001215 // Tell listeners that the cameras and torch modes are unavailable and schedule a
1216 // reconnection to camera service. When camera service is reconnected, the camera
1217 // and torch statuses will be updated.
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001218 for (int i = 0; i < mDeviceStatus.size(); i++) {
1219 String cameraId = mDeviceStatus.keyAt(i);
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001220 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId);
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001221 }
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001222 for (int i = 0; i < mTorchStatus.size(); i++) {
1223 String cameraId = mTorchStatus.keyAt(i);
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001224 onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE,
1225 cameraId);
Chien-Yu Chenafa91392015-02-11 11:23:28 -08001226 }
1227
Chien-Yu Chen0f69b462015-07-13 16:37:13 -07001228 scheduleCameraServiceReconnectionLocked();
Eino-Ville Talvala4c9c7a52014-10-22 14:39:31 -07001229 }
1230 }
1231
1232 } // CameraManagerGlobal
1233
Igor Murashkine363fbb2013-06-25 20:26:06 +00001234} // CameraManager