CameraManagerpublic final class CameraManager extends Object A system service manager for detecting, characterizing, and connecting to
{@link CameraDevice CameraDevices}.
You can get an instance of this class by calling
{@link android.content.Context#getSystemService(String) Context.getSystemService()}.
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
For more details about communicating with camera devices, read the Camera
developer guide or the {@link android.hardware.camera2 camera2}
package documentation. |
Fields Summary |
---|
private static final String | TAG | private final boolean | DEBUG | private static final int | USE_CALLING_UID | private static final int | API_VERSION_1 | private static final int | API_VERSION_2 | private ArrayList | mDeviceIdList | private final android.content.Context | mContext | private final Object | mLock |
Constructors Summary |
---|
public CameraManager(android.content.Context context)
DEBUG = Log.isLoggable(TAG, Log.DEBUG);
synchronized(mLock) {
mContext = context;
}
|
Methods Summary |
---|
public CameraCharacteristics | getCameraCharacteristics(java.lang.String cameraId)Query the capabilities of a camera device. These capabilities are
immutable for a given camera.
CameraCharacteristics characteristics = null;
synchronized (mLock) {
if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
" currently connected camera device", cameraId));
}
int id = Integer.valueOf(cameraId);
/*
* Get the camera characteristics from the camera service directly if it supports it,
* otherwise get them from the legacy shim instead.
*/
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable");
}
try {
if (!supportsCamera2ApiLocked(cameraId)) {
// Legacy backwards compatibility path; build static info from the camera
// parameters
String[] outParameters = new String[1];
cameraService.getLegacyParameters(id, /*out*/outParameters);
String parameters = outParameters[0];
CameraInfo info = new CameraInfo();
cameraService.getCameraInfo(id, /*out*/info);
characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
} else {
// Normal path: Get the camera characteristics directly from the camera service
CameraMetadataNative info = new CameraMetadataNative();
cameraService.getCameraCharacteristics(id, info);
characteristics = new CameraCharacteristics(info);
}
} catch (CameraRuntimeException e) {
throw e.asChecked();
} catch (RemoteException e) {
// Camera service died - act as if the camera was disconnected
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable", e);
}
}
return characteristics;
| public java.lang.String[] | getCameraIdList()Return the list of currently connected camera devices by
identifier.
Non-removable cameras use integers starting at 0 for their
identifiers, while removable cameras have a unique identifier for each
individual device, even if they are the same model.
synchronized (mLock) {
// ID list creation handles various known failures in device enumeration, so only
// exceptions it'll throw are unexpected, and should be propagated upward.
return getOrCreateDeviceIdListLocked().toArray(new String[0]);
}
| private java.util.ArrayList | getOrCreateDeviceIdListLocked()Return or create the list of currently connected camera devices.
In case of errors connecting to the camera service, will return an empty list.
if (mDeviceIdList == null) {
int numCameras = 0;
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
ArrayList<String> deviceIdList = new ArrayList<>();
// If no camera service, then no devices
if (cameraService == null) {
return deviceIdList;
}
try {
numCameras = cameraService.getNumberOfCameras();
} catch(CameraRuntimeException e) {
throw e.asChecked();
} catch (RemoteException e) {
// camera service just died - if no camera service, then no devices
return deviceIdList;
}
CameraMetadataNative info = new CameraMetadataNative();
for (int i = 0; i < numCameras; ++i) {
// Non-removable cameras use integers starting at 0 for their
// identifiers
boolean isDeviceSupported = false;
try {
cameraService.getCameraCharacteristics(i, info);
if (!info.isEmpty()) {
isDeviceSupported = true;
} else {
throw new AssertionError("Expected to get non-empty characteristics");
}
} catch(IllegalArgumentException e) {
// Got a BAD_VALUE from service, meaning that this
// device is not supported.
} catch(CameraRuntimeException e) {
// DISCONNECTED means that the HAL reported an low-level error getting the
// device info; skip listing the device. Other errors,
// propagate exception onward
if (e.getReason() != CameraAccessException.CAMERA_DISCONNECTED) {
throw e.asChecked();
}
} catch(RemoteException e) {
// Camera service died - no devices to list
deviceIdList.clear();
return deviceIdList;
}
if (isDeviceSupported) {
deviceIdList.add(String.valueOf(i));
} else {
Log.w(TAG, "Error querying camera device " + i + " for listing.");
}
}
mDeviceIdList = deviceIdList;
}
return mDeviceIdList;
| public void | openCamera(java.lang.String cameraId, CameraDevice.StateCallback callback, android.os.Handler handler)Open a connection to a camera with the given ID.
Use {@link #getCameraIdList} to get the list of available camera
devices. Note that even if an id is listed, open may fail if the device
is disconnected between the calls to {@link #getCameraIdList} and
{@link #openCamera}.
Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
for operation by calling {@link CameraDevice#createCaptureSession} and
{@link CameraDevice#createCaptureRequest}
If the camera becomes disconnected during initialization
after this function call returns,
{@link CameraDevice.StateCallback#onDisconnected} with a
{@link CameraDevice} in the disconnected state (and
{@link CameraDevice.StateCallback#onOpened} will be skipped).
If opening the camera device fails, then the device callback's
{@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent
calls on the camera device will throw a {@link CameraAccessException}.
if (cameraId == null) {
throw new IllegalArgumentException("cameraId was null");
} else if (callback == null) {
throw new IllegalArgumentException("callback was null");
} else if (handler == null) {
if (Looper.myLooper() != null) {
handler = new Handler();
} else {
throw new IllegalArgumentException(
"Looper doesn't exist in the calling thread");
}
}
openCameraDeviceUserAsync(cameraId, callback, handler);
| private CameraDevice | openCameraDeviceUserAsync(java.lang.String cameraId, CameraDevice.StateCallback callback, android.os.Handler handler)Helper for openning a connection to a camera with the given ID.
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
try {
synchronized (mLock) {
ICameraDeviceUser cameraUser = null;
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback,
handler,
characteristics);
BinderHolder holder = new BinderHolder();
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
int id = Integer.parseInt(cameraId);
try {
if (supportsCamera2ApiLocked(cameraId)) {
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new CameraRuntimeException(
CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable");
}
cameraService.connectDevice(callbacks, id,
mContext.getPackageName(), USE_CALLING_UID, holder);
cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
} else {
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
}
} catch (CameraRuntimeException e) {
if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
throw new AssertionError("Should've gone down the shim path");
} else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
e.getReason() == CameraAccessException.CAMERA_DISABLED ||
e.getReason() == CameraAccessException.CAMERA_DISCONNECTED ||
e.getReason() == CameraAccessException.CAMERA_ERROR) {
// Received one of the known connection errors
// The remote camera device cannot be connected to, so
// set the local camera to the startup error state
deviceImpl.setRemoteFailure(e);
if (e.getReason() == CameraAccessException.CAMERA_DISABLED ||
e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
// Per API docs, these failures call onError and throw
throw e.asChecked();
}
} else {
// Unexpected failure - rethrow
throw e;
}
} catch (RemoteException e) {
// Camera service died - act as if it's a CAMERA_DISCONNECTED case
CameraRuntimeException ce = new CameraRuntimeException(
CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable", e);
deviceImpl.setRemoteFailure(ce);
throw ce.asChecked();
}
// TODO: factor out callback to be non-nested, then move setter to constructor
// For now, calling setRemoteDevice will fire initial
// onOpened/onUnconfigured callbacks.
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
+ cameraId);
} catch (CameraRuntimeException e) {
throw e.asChecked();
}
return device;
| public void | registerAvailabilityCallback(android.hardware.camera2.CameraManager$AvailabilityCallback callback, android.os.Handler handler)Register a callback to be notified about camera device availability.
Registering the same callback again will replace the handler with the
new one provided.
The first time a callback is registered, it is immediately called
with the availability status of all currently known camera devices.
Since this callback will be registered with the camera service, remember to unregister it
once it is no longer needed; otherwise the callback will continue to receive events
indefinitely and it may prevent other resources from being released. Specifically, the
callbacks will be invoked independently of the general activity lifecycle and independently
of the state of individual CameraManager instances.
if (handler == null) {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalArgumentException(
"No handler given, and current thread has no looper!");
}
handler = new Handler(looper);
}
CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
| private boolean | supportsCamera2ApiLocked(java.lang.String cameraId)Queries the camera service if it supports the camera2 api directly, or needs a shim.
return supportsCameraApiLocked(cameraId, API_VERSION_2);
| private boolean | supportsCameraApiLocked(java.lang.String cameraId, int apiVersion)Queries the camera service if it supports a camera api directly, or needs a shim.
int id = Integer.parseInt(cameraId);
/*
* Possible return values:
* - NO_ERROR => CameraX API is supported
* - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
* - Remote exception => If the camera service died
*
* Anything else is an unexpected error we don't want to recover from.
*/
try {
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
// If no camera service, no support
if (cameraService == null) return false;
int res = cameraService.supportsCameraApi(id, apiVersion);
if (res != CameraServiceBinderDecorator.NO_ERROR) {
throw new AssertionError("Unexpected value " + res);
}
return true;
} catch (CameraRuntimeException e) {
if (e.getReason() != CameraAccessException.CAMERA_DEPRECATED_HAL) {
throw e;
}
// API level is not supported
} catch (RemoteException e) {
// Camera service is now down, no support for any API level
}
return false;
| public void | unregisterAvailabilityCallback(android.hardware.camera2.CameraManager$AvailabilityCallback callback)Remove a previously-added callback; the callback will no longer receive connection and
disconnection callbacks.
Removing a callback that isn't registered has no effect.
CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
|
|