blob: 2ac50e429cde545aacb87a170012d30bc9b85c08 [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
Igor Murashkine363fbb2013-06-25 20:26:06 +000019import android.content.Context;
20import android.hardware.ICameraService;
21import android.hardware.ICameraServiceListener;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070022import android.hardware.camera2.impl.CameraMetadataNative;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070023import android.hardware.camera2.utils.CameraBinderDecorator;
24import android.hardware.camera2.utils.CameraRuntimeException;
Ruben Brunk66ef6452013-08-08 13:05:30 -070025import android.hardware.camera2.utils.BinderHolder;
Igor Murashkine363fbb2013-06-25 20:26:06 +000026import android.os.IBinder;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070027import android.os.Handler;
28import android.os.Looper;
Igor Murashkine363fbb2013-06-25 20:26:06 +000029import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.util.Log;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070032import android.util.ArrayMap;
Igor Murashkine363fbb2013-06-25 20:26:06 +000033
34import java.util.ArrayList;
Igor Murashkine363fbb2013-06-25 20:26:06 +000035
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080036/**
37 * <p>An interface for iterating, listing, and connecting to
38 * {@link CameraDevice CameraDevices}.</p>
39 *
40 * <p>You can get an instance of this class by calling
41 * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p>
42 *
43 * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre>
44 *
45 * <p>For more details about communicating with camera devices, read the Camera
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070046 * developer guide or the {@link android.hardware.camera2 camera2}
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080047 * package documentation.</p>
48 */
49public final class CameraManager {
50
51 /**
Igor Murashkine363fbb2013-06-25 20:26:06 +000052 * This should match the ICameraService definition
53 */
54 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
55 private static final int USE_CALLING_UID = -1;
56
57 private final ICameraService mCameraService;
58 private ArrayList<String> mDeviceIdList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070059
Igor Murashkin5c9eaf62013-09-10 19:35:24 -070060 private final ArrayMap<AvailabilityListener, Handler> mListenerMap =
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070061 new ArrayMap<AvailabilityListener, Handler>();
62
Igor Murashkine363fbb2013-06-25 20:26:06 +000063 private final Context mContext;
64 private final Object mLock = new Object();
65
66 /**
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080067 * @hide
68 */
Igor Murashkine363fbb2013-06-25 20:26:06 +000069 public CameraManager(Context context) {
70 mContext = context;
71
72 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
73 ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
74
75 /**
76 * Wrap the camera service in a decorator which automatically translates return codes
77 * into exceptions, and RemoteExceptions into other exceptions.
78 */
79 mCameraService = CameraBinderDecorator.newInstance(cameraServiceRaw);
80
81 try {
82 mCameraService.addListener(new CameraServiceListener());
83 } catch(CameraRuntimeException e) {
84 throw new IllegalStateException("Failed to register a camera service listener",
85 e.asChecked());
86 } catch (RemoteException e) {
87 // impossible
88 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080089 }
90
91 /**
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070092 * Return the list of currently connected camera devices by
93 * identifier.
94 *
95 * <p>Non-removable cameras use integers starting at 0 for their
Eino-Ville Talvalab2675542012-12-12 13:29:45 -080096 * identifiers, while removable cameras have a unique identifier for each
97 * individual device, even if they are the same model.</p>
98 *
99 * @return The list of currently connected camera devices.
100 */
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700101 public String[] getCameraIdList() throws CameraAccessException {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000102 synchronized (mLock) {
Igor Murashkin70725502013-06-25 20:27:06 +0000103 try {
104 return getOrCreateDeviceIdListLocked().toArray(new String[0]);
105 } catch(CameraAccessException e) {
106 // this should almost never happen, except if mediaserver crashes
107 throw new IllegalStateException(
108 "Failed to query camera service for device ID list", e);
109 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000110 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800111 }
112
113 /**
114 * Register a listener to be notified about camera device availability.
115 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700116 * <p>Registering the same listener again will replace the handler with the
117 * new one provided.</p>
Igor Murashkine363fbb2013-06-25 20:26:06 +0000118 *
Benjamin Hendricks24eb8a32013-08-15 12:46:22 -0700119 * @param listener The new listener to send camera availability notices to
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700120 * @param handler The handler on which the listener should be invoked, or
121 * {@code null} to use the current thread's {@link android.os.Looper looper}.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800122 */
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700123 public void addAvailabilityListener(AvailabilityListener listener, Handler handler) {
124 if (handler == null) {
125 Looper looper = Looper.myLooper();
126 if (looper == null) {
127 throw new IllegalArgumentException(
128 "No handler given, and current thread has no looper!");
129 }
130 handler = new Handler(looper);
131 }
132
Igor Murashkine363fbb2013-06-25 20:26:06 +0000133 synchronized (mLock) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700134 mListenerMap.put(listener, handler);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000135 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800136 }
137
138 /**
139 * Remove a previously-added listener; the listener will no longer receive
140 * connection and disconnection callbacks.
141 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700142 * <p>Removing a listener that isn't registered has no effect.</p>
Igor Murashkine363fbb2013-06-25 20:26:06 +0000143 *
Benjamin Hendricks24eb8a32013-08-15 12:46:22 -0700144 * @param listener The listener to remove from the notification list
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800145 */
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700146 public void removeAvailabilityListener(AvailabilityListener listener) {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000147 synchronized (mLock) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700148 mListenerMap.remove(listener);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000149 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800150 }
151
152 /**
153 * <p>Query the capabilities of a camera device. These capabilities are
154 * immutable for a given camera.</p>
155 *
156 * @param cameraId The id of the camera device to query
157 * @return The properties of the given camera
158 *
159 * @throws IllegalArgumentException if the cameraId does not match any
160 * currently connected camera device.
161 * @throws CameraAccessException if the camera is disabled by device policy.
162 * @throws SecurityException if the application does not have permission to
163 * access the camera
164 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700165 * @see #getCameraIdList
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800166 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
167 */
Igor Murashkin68f40062013-09-10 12:15:54 -0700168 public CameraCharacteristics getCameraCharacteristics(String cameraId)
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800169 throws CameraAccessException {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000170
171 synchronized (mLock) {
172 if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
173 throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
174 " currently connected camera device", cameraId));
175 }
176 }
177
Zhijun He20011882013-09-25 10:05:59 -0700178 CameraMetadataNative info = new CameraMetadataNative();
179 try {
180 mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
181 } catch(CameraRuntimeException e) {
182 throw e.asChecked();
183 } catch(RemoteException e) {
184 // impossible
185 return null;
186 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000187
Zhijun He20011882013-09-25 10:05:59 -0700188 return new CameraCharacteristics(info);
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800189 }
190
191 /**
192 * Open a connection to a camera with the given ID. Use
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700193 * {@link #getCameraIdList} to get the list of available camera
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800194 * devices. Note that even if an id is listed, open may fail if the device
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700195 * is disconnected between the calls to {@link #getCameraIdList} and
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800196 * {@link #openCamera}.
197 *
198 * @param cameraId The unique identifier of the camera device to open
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700199 * @param listener The listener for the camera. Must not be null.
200 * @param handler The handler to call the listener on. Must not be null.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800201 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800202 * @throws CameraAccessException if the camera is disabled by device policy,
Ruben Brunk66ef6452013-08-08 13:05:30 -0700203 * or too many camera devices are already open, or the cameraId does not match
204 * any currently available camera device.
205 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800206 * @throws SecurityException if the application does not have permission to
207 * access the camera
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700208 * @throws IllegalArgumentException if listener or handler is null.
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800209 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700210 * @see #getCameraIdList
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800211 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
212 */
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700213 private void openCameraDeviceUserAsync(String cameraId,
214 CameraDevice.StateListener listener, Handler handler)
215 throws CameraAccessException {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000216 try {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000217
218 synchronized (mLock) {
Igor Murashkin70725502013-06-25 20:27:06 +0000219
220 ICameraDeviceUser cameraUser;
221
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -0700222 android.hardware.camera2.impl.CameraDevice device =
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700223 new android.hardware.camera2.impl.CameraDevice(
224 cameraId,
225 listener,
226 handler);
Igor Murashkin70725502013-06-25 20:27:06 +0000227
Ruben Brunk66ef6452013-08-08 13:05:30 -0700228 BinderHolder holder = new BinderHolder();
229 mCameraService.connectDevice(device.getCallbacks(),
Igor Murashkine363fbb2013-06-25 20:26:06 +0000230 Integer.parseInt(cameraId),
Ruben Brunk66ef6452013-08-08 13:05:30 -0700231 mContext.getPackageName(), USE_CALLING_UID, holder);
232 cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
Igor Murashkin70725502013-06-25 20:27:06 +0000233
234 // TODO: factor out listener to be non-nested, then move setter to constructor
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700235 // For now, calling setRemoteDevice will fire initial
236 // onOpened/onUnconfigured callbacks.
Igor Murashkin70725502013-06-25 20:27:06 +0000237 device.setRemoteDevice(cameraUser);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000238 }
239
Igor Murashkine363fbb2013-06-25 20:26:06 +0000240 } catch (NumberFormatException e) {
241 throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
242 + cameraId);
243 } catch (CameraRuntimeException e) {
Ruben Brunk66ef6452013-08-08 13:05:30 -0700244 throw e.asChecked();
Igor Murashkine363fbb2013-06-25 20:26:06 +0000245 } catch (RemoteException e) {
246 // impossible
Igor Murashkine363fbb2013-06-25 20:26:06 +0000247 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800248 }
249
250 /**
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700251 * Open a connection to a camera with the given ID.
252 *
253 * <p>Use {@link #getCameraIdList} to get the list of available camera
254 * devices. Note that even if an id is listed, open may fail if the device
255 * is disconnected between the calls to {@link #getCameraIdList} and
256 * {@link #openCamera}.</p>
257 *
258 * <p>If the camera successfully opens after this function call returns,
259 * {@link CameraDevice.StateListener#onOpened} will be invoked with the
260 * newly opened {@link CameraDevice} in the unconfigured state.</p>
261 *
262 * <p>If the camera becomes disconnected during initialization
263 * after this function call returns,
264 * {@link CameraDevice.StateListener#onDisconnected} with a
265 * {@link CameraDevice} in the disconnected state (and
266 * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
267 *
268 * <p>If the camera fails to initialize after this function call returns,
269 * {@link CameraDevice.StateListener#onError} will be invoked with a
270 * {@link CameraDevice} in the error state (and
271 * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
272 *
273 * @param cameraId
274 * The unique identifier of the camera device to open
275 * @param listener
276 * The listener which is invoked once the camera is opened
277 * @param handler
278 * The handler on which the listener should be invoked, or
279 * {@code null} to use the current thread's {@link android.os.Looper looper}.
280 *
281 * @throws CameraAccessException if the camera is disabled by device policy,
282 * or the camera has become or was disconnected.
283 *
284 * @throws IllegalArgumentException if cameraId or the listener was null,
285 * or the cameraId does not match any currently or previously available
286 * camera device.
287 *
288 * @throws SecurityException if the application does not have permission to
289 * access the camera
290 *
291 * @see #getCameraIdList
292 * @see android.app.admin.DevicePolicyManager#setCameraDisabled
293 */
294 public void openCamera(String cameraId, final CameraDevice.StateListener listener,
295 Handler handler)
296 throws CameraAccessException {
297
298 if (cameraId == null) {
299 throw new IllegalArgumentException("cameraId was null");
300 } else if (listener == null) {
301 throw new IllegalArgumentException("listener was null");
302 } else if (handler == null) {
303 if (Looper.myLooper() != null) {
304 handler = new Handler();
305 } else {
306 throw new IllegalArgumentException(
307 "Looper doesn't exist in the calling thread");
308 }
309 }
310
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700311 openCameraDeviceUserAsync(cameraId, listener, handler);
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700312 }
313
314 /**
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700315 * Interface for listening to camera devices becoming available or
316 * unavailable.
317 *
318 * <p>Cameras become available when they are no longer in use, or when a new
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800319 * removable camera is connected. They become unavailable when some
320 * application or service starts using a camera, or when a removable camera
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700321 * is disconnected.</p>
322 *
323 * @see addAvailabilityListener
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800324 */
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700325 public static abstract class AvailabilityListener {
326
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800327 /**
328 * A new camera has become available to use.
329 *
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700330 * <p>The default implementation of this method does nothing.</p>
331 *
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800332 * @param cameraId The unique identifier of the new camera.
333 */
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700334 public void onCameraAvailable(String cameraId) {
335 // default empty implementation
336 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800337
338 /**
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700339 * A previously-available camera has become unavailable for use.
340 *
341 * <p>If an application had an active CameraDevice instance for the
342 * now-disconnected camera, that application will receive a
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700343 * {@link CameraDevice.StateListener#onDisconnected disconnection error}.</p>
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700344 *
345 * <p>The default implementation of this method does nothing.</p>
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800346 *
347 * @param cameraId The unique identifier of the disconnected camera.
348 */
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700349 public void onCameraUnavailable(String cameraId) {
350 // default empty implementation
351 }
Eino-Ville Talvalab2675542012-12-12 13:29:45 -0800352 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000353
354 private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
355 if (mDeviceIdList == null) {
356 int numCameras = 0;
357
358 try {
359 numCameras = mCameraService.getNumberOfCameras();
360 } catch(CameraRuntimeException e) {
361 throw e.asChecked();
362 } catch (RemoteException e) {
363 // impossible
364 return null;
365 }
366
367 mDeviceIdList = new ArrayList<String>();
Zhijun He18fe0ae2013-10-01 11:09:28 -0700368 CameraMetadataNative info = new CameraMetadataNative();
Igor Murashkine363fbb2013-06-25 20:26:06 +0000369 for (int i = 0; i < numCameras; ++i) {
370 // Non-removable cameras use integers starting at 0 for their
371 // identifiers
Zhijun He18fe0ae2013-10-01 11:09:28 -0700372 boolean isDeviceSupported = false;
373 try {
374 mCameraService.getCameraCharacteristics(i, info);
375 if (!info.isEmpty()) {
376 isDeviceSupported = true;
377 } else {
378 throw new AssertionError("Expected to get non-empty characteristics");
379 }
380 } catch(IllegalArgumentException e) {
381 // Got a BAD_VALUE from service, meaning that this
382 // device is not supported.
383 } catch(CameraRuntimeException e) {
384 throw e.asChecked();
385 } catch(RemoteException e) {
386 // impossible
387 }
388
389 if (isDeviceSupported) {
390 mDeviceIdList.add(String.valueOf(i));
391 }
Igor Murashkine363fbb2013-06-25 20:26:06 +0000392 }
393
394 }
395 return mDeviceIdList;
396 }
397
398 // TODO: this class needs unit tests
399 // TODO: extract class into top level
Zhijun Heecb323e2013-07-31 09:40:27 -0700400 private class CameraServiceListener extends ICameraServiceListener.Stub {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000401
402 // Keep up-to-date with ICameraServiceListener.h
403
404 // Device physically unplugged
405 public static final int STATUS_NOT_PRESENT = 0;
406 // Device physically has been plugged in
407 // and the camera can be used exclusively
408 public static final int STATUS_PRESENT = 1;
409 // Device physically has been plugged in
410 // but it will not be connect-able until enumeration is complete
411 public static final int STATUS_ENUMERATING = 2;
412 // Camera is in use by another app and cannot be used exclusively
413 public static final int STATUS_NOT_AVAILABLE = 0x80000000;
414
415 // Camera ID -> Status map
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700416 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
Igor Murashkine363fbb2013-06-25 20:26:06 +0000417
418 private static final String TAG = "CameraServiceListener";
419
420 @Override
421 public IBinder asBinder() {
422 return this;
423 }
424
425 private boolean isAvailable(int status) {
426 switch (status) {
427 case STATUS_PRESENT:
428 return true;
429 default:
430 return false;
431 }
432 }
433
434 private boolean validStatus(int status) {
435 switch (status) {
436 case STATUS_NOT_PRESENT:
437 case STATUS_PRESENT:
438 case STATUS_ENUMERATING:
439 case STATUS_NOT_AVAILABLE:
440 return true;
441 default:
442 return false;
443 }
444 }
445
446 @Override
447 public void onStatusChanged(int status, int cameraId) throws RemoteException {
Igor Murashkin70725502013-06-25 20:27:06 +0000448 synchronized(CameraManager.this.mLock) {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000449
450 Log.v(TAG,
451 String.format("Camera id %d has status changed to 0x%x", cameraId, status));
452
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700453 final String id = String.valueOf(cameraId);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000454
455 if (!validStatus(status)) {
456 Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
457 status));
458 return;
459 }
460
461 Integer oldStatus = mDeviceStatus.put(id, status);
462
Igor Murashkin74416952013-09-06 16:03:29 -0700463 if (oldStatus != null && oldStatus == status) {
Igor Murashkine363fbb2013-06-25 20:26:06 +0000464 Log.v(TAG, String.format(
465 "Device status changed to 0x%x, which is what it already was",
466 status));
467 return;
468 }
469
470 // TODO: consider abstracting out this state minimization + transition
471 // into a separate
472 // more easily testable class
473 // i.e. (new State()).addState(STATE_AVAILABLE)
474 // .addState(STATE_NOT_AVAILABLE)
475 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
476 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
477 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
478 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
479
480 // Translate all the statuses to either 'available' or 'not available'
481 // available -> available => no new update
482 // not available -> not available => no new update
483 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
484
485 Log.v(TAG,
486 String.format(
487 "Device status was previously available (%d), " +
488 " and is now again available (%d)" +
489 "so no new client visible update will be sent",
490 isAvailable(status), isAvailable(status)));
491 return;
492 }
493
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700494 final int listenerCount = mListenerMap.size();
495 for (int i = 0; i < listenerCount; i++) {
496 Handler handler = mListenerMap.valueAt(i);
497 final AvailabilityListener listener = mListenerMap.keyAt(i);
Igor Murashkine363fbb2013-06-25 20:26:06 +0000498 if (isAvailable(status)) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700499 handler.post(
500 new Runnable() {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700501 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700502 public void run() {
503 listener.onCameraAvailable(id);
504 }
505 });
Igor Murashkine363fbb2013-06-25 20:26:06 +0000506 } else {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700507 handler.post(
508 new Runnable() {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700509 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700510 public void run() {
511 listener.onCameraUnavailable(id);
512 }
513 });
Igor Murashkine363fbb2013-06-25 20:26:06 +0000514 }
515 } // for
516 } // synchronized
517 } // onStatusChanged
518 } // CameraServiceListener
519} // CameraManager