这里追踪 相机打开流程,从 Java API 到 camera service,可以参照下面的核心类,进行追踪
1 openCamera
frameworks/base/core/java/android/hardware/camera2/CameraManager.java # openCamera
打开与给定ID的相机的连接。
下面是一些注意点,简单看一下:
使用{@link #getCameraIdList}来获取可用相机设备的列表。请注意,即使列出了id,如果设备在调用{@ link# getCameraIdList}和{@ link# openCamera}之间断开连接,或者如果更高优先级的相机API客户端开始使用相机设备,则打开可能会失败。
一旦相机成功打开,{@link CameraDevice。StateCallback#onOpened}将与新打开的{@link CameraDevice}一起调用。然后可以通过调用{@link CameraDevice#createCaptureSession}和{@link CameraDevice#createCaptureRequest}来设置相机设备进行操作。
在API级别30之前,当应用程序尝试打开多个不同id的{@link CameraDevice}并且设备不支持打开这样的组合时,{@link #openCamera}将失败并抛出{@link CameraAccessException}或一个或多个已打开的{@link CameraDevice}将断开连接并接收{@link android.硬件。camera2.CameraDevice.StateCallback # onDisconnected}的回调函数。哪种行为会发生取决于设备实现,并且在不同的设备上可能会有所不同。从API级别30开始,如果设备不支持正在打开的相机组合,则可以保证{@link #openCamera}调用将失败,并且现有的{@link CameraDevice}都不会断开连接。
由于相机设备将被异步打开,任何在返回的CameraDevice实例上完成的异步操作将被排队,直到设备启动完成和回调的{@link CameraDevice.StateCallback#onOpened onOpened}方法被调用。然后按顺序处理挂起的操作。
* @param cameraId
* The unique identifier of the camera device to open
* @param callback
* The callback which is invoked once the camera is opened
* @param handler
* The handler on which the callback should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper looper}.
// Open a connection to a camera with the given ID.
@RequiresPermission(android.Manifest.permission.CAMERA)
public void openCamera(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
USE_CALLING_UID);
}
2 追踪 openCamera 具体做了哪些事情
我们重点看一下 打开相机的过程做了哪些事情:
2.1 openCameraForUid
可以看到 openCamera指定用于权限等验证的UID为 USE_CALLING_UID,用来通过权限认证
/**
* Open a connection to a camera with the given ID, on behalf of another application
* specified by clientUid.
代表由clientUid指定的另一个应用程序,打开给定ID的相机的连接。
*
* <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
* the caller to specify the UID to use for permission/etc verification. This can only be
* done by services trusted by the camera subsystem to act on behalf of applications and
* to forward the real UID.</p>
*
* @param clientUid
* The UID of the application on whose behalf the camera is being opened.
* Must be USE_CALLING_UID unless the caller is a trusted service.
*
* @hide
*/
public void openCameraForUid(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
int clientUid) throws CameraAccessException {
openCameraForUid(cameraId, callback, executor, clientUid, /*oomScoreOffset*/0,
shouldOverrideToPortrait(mContext));
}
2.2 openCameraDeviceUserAsync
frameworks/base/core/java/android/hardware/camera2/CameraManager.java # openCameraDeviceUserAsync
真正的逻辑在这里开始
/**
* Helper for opening a connection to a camera with the given ID.
*
* @param cameraId The unique identifier of the camera device to open
* @param callback The callback for the camera. Must not be null.
* @param executor The executor to invoke the callback with. Must not be null.
* @param uid The UID of the application actually opening the camera.
* Must be USE_CALLING_UID unless the caller is a service
* that is trusted to open the device on behalf of an
* application and to forward the real UID.
*
* @throws CameraAccessException if the camera is disabled by device policy,
* too many camera devices are already open, or the cameraId does not match
* any currently available camera device.
*
* @throws SecurityException if the application does not have permission to
* access the camera
* @throws IllegalArgumentException if callback or handler is null.
* @return A handle to the newly-created camera device.
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateCallback callback, Executor executor, final int uid,
final int oomScoreOffset, boolean overrideToPortrait) throws CameraAccessException {
// 返回的信息,包括是否有强制并发流的标志,int 类型的camera id, display size, 可能得 多分辨率流配置
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
// 返回 getPhysicalCameraIds()返回的physicalCameraId作为key,
// getCameraCharacteristics(physicalCameraId)返回的 CameraCharacteristics 作为 value的 map
Map<String, CameraCharacteristics> physicalIdsToChars =
getPhysicalIdToCharsMap(characteristics);
synchronized (mLock) {
ICameraDeviceUser cameraUser = null;
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback, // CameraDeviceImpl # mDeviceCallback
executor,
characteristics,
physicalIdsToChars,
mContext.getApplicationInfo().targetSdkVersion,
mContext);
// CameraDeviceImpl # mCallbacks
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
try {
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new ServiceSpecificException(
ICameraService.ERROR_DISCONNECTED,
"Camera service is currently unavailable");
}
// 连接设备
cameraUser = cameraService.connectDevice(callbacks, cameraId,
mContext.getOpPackageName(), mContext.getAttributionTag(), uid,
oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion,
overrideToPortrait);
} catch (ServiceSpecificException e) {
if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
throw new AssertionError("Should've gone down the shim path");
} else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
e.errorCode == ICameraService.ERROR_DISABLED ||
e.errorCode == ICameraService.ERROR_DISCONNECTED ||
e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
// 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.errorCode == ICameraService.ERROR_DISABLED ||
e.errorCode == ICameraService.ERROR_DISCONNECTED ||
e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
// Per API docs, these failures call onError and throw
throwAsPublicException(e);
}
} else {
// Unexpected failure - rethrow
throwAsPublicException(e);
}
} catch (RemoteException e) {
// Camera service died - act as if it's a CAMERA_DISCONNECTED case
ServiceSpecificException sse = new ServiceSpecificException(
ICameraService.ERROR_DISCONNECTED,
"Camera service is currently unavailable");
deviceImpl.setRemoteFailure(sse);
throwAsPublicException(sse);
}
// TODO: factor out callback to be non-nested, then move setter to constructor
// For now, calling setRemoteDevice will fire initial
// onOpened/onUnconfigured callbacks.
// This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
// cameraUser dies during setup.
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
}
return device;
}
2.2.1 getCameraCharacteristics
返回的信息,包括是否有强制并发流,int 类型的camera id, display size, 可能得 多分辨率流配置;当然还有其他的属性,这里不关注
frameworks/base/core/java/android/hardware/camera2/CameraManager.java # getCameraCharacteristics
/**
* <p>Query the capabilities of a camera device. These capabilities are
* immutable for a given camera.</p>
*
* <p>From API level 29, this function can also be used to query the capabilities of physical
* cameras that can only be used as part of logical multi-camera. These cameras cannot be
* opened directly via {@link #openCamera}</p>
*
* <p>Also starting with API level 29, while most basic camera information is still available
* even without the CAMERA permission, some values are not available to apps that do not hold
* that permission. The keys not available are listed by
* {@link CameraCharacteristics#getKeysNeedingPermission}.</p>
*
* @param cameraId The id of the camera device to query. This could be either a standalone
* camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that
* can only used as part of a logical multi-camera.
* @return The properties of the given camera
*
* @throws IllegalArgumentException if the cameraId does not match any
* known camera device.
* @throws CameraAccessException if the camera device has been disconnected.
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
@NonNull
public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
throws CameraAccessException {
return getCameraCharacteristics(cameraId, shouldOverrideToPortrait(mContext));
}
public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId,
boolean overrideToPortrait) throws CameraAccessException {
CameraCharacteristics characteristics = null;
if (CameraManagerGlobal.sCameraServiceDisabled) {
throw new IllegalArgumentException("No cameras available on device");
}
synchronized (mLock) {
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable");
}
try {
Size displaySize = getDisplaySize();
CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId,
mContext.getApplicationInfo().targetSdkVersion, overrideToPortrait);
try {
info.setCameraId(Integer.parseInt(cameraId));
} catch (NumberFormatException e) {
Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
}
boolean hasConcurrentStreams =
CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
info.setDisplaySize(displaySize);
Map<String, StreamConfiguration[]> multiResolutionSizeMap =
getPhysicalCameraMultiResolutionConfigs(cameraId, info, cameraService);
if (multiResolutionSizeMap.size() > 0) {
info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap);
}
characteristics = new CameraCharacteristics(info);
} catch (ServiceSpecificException e) {
throw ExceptionUtils.throwAsPublicException(e);
} 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);
}
}
registerDeviceStateListener(characteristics);
// 返回的信息,包括是否有强制并发流,int 类型的camera id, display size, 可能得 多分辨率流配置
return characteristics;
}
CameraCharacteristics
frameworks/base/core/java/android/hardware/camera2/CameraCharacteristics.java
/**
* Takes ownership of the passed-in properties object
*
* @param properties Camera properties.
* @hide
*/
public CameraCharacteristics(CameraMetadataNative properties) {
mProperties = CameraMetadataNative.move(properties);
setNativeInstance(mProperties);
}
CameraMetadataNative
实现相机元数据跨Binder到camera service的编组/解组
frameworks/base/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
public class CameraMetadataNative implements Parcelable {
/**
* Set the current camera Id.
*
* @param cameraId Current camera id.
*
* @hide
*/
public void setCameraId(int cameraId) {
mCameraId = cameraId;
}
/**
* Set the current camera Id.
*
* @param hasMandatoryConcurrentStreams whether the metadata advertises mandatory concurrent
* streams.
hasMandatoryConcurrentStreams元数据是否发布强制并发流。
*
* @hide
*/
public void setHasMandatoryConcurrentStreams(boolean hasMandatoryConcurrentStreams) {
mHasMandatoryConcurrentStreams = hasMandatoryConcurrentStreams;
}
/**
* Set the current display size.
*
* @param displaySize The current display size.
*
* @hide
*/
public void setDisplaySize(Size displaySize) {
mDisplaySize = displaySize;
}
/**
* Set the multi-resolution stream configuration map.
*
* @param multiResolutionMap The multi-resolution stream configuration map.
*
* @hide
*/
public void setMultiResolutionStreamConfigurationMap(
@NonNull Map<String, StreamConfiguration[]> multiResolutionMap) {
mMultiResolutionStreamConfigurationMap =
new MultiResolutionStreamConfigurationMap(multiResolutionMap);
}
}
2.2.2 getPhysicalIdToCharsMap
frameworks/base/core/java/android/hardware/camera2/CameraManager.java # getPhysicalIdToCharsMap
返回 getPhysicalCameraIds()返回的物理相机id作为key, getCameraCharacteristics(physicalCameraId)返回的 CameraCharacteristics 作为 value的 map
private Map<String, CameraCharacteristics> getPhysicalIdToCharsMap(
CameraCharacteristics chars) throws CameraAccessException {
HashMap<String, CameraCharacteristics> physicalIdsToChars =
new HashMap<String, CameraCharacteristics>();
// 返回由这个逻辑相机{@link CameraDevice}组成的物理相机id集合
Set<String> physicalCameraIds = chars.getPhysicalCameraIds();
for (String physicalCameraId : physicalCameraIds) {
CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId);
physicalIdsToChars.put(physicalCameraId, physicalChars);
}
return physicalIdsToChars;
}
CameraCharacteristics.java # getPhysicalCameraIds
返回由这个逻辑相机{@link CameraDevice}组成的物理相机id集合
frameworks/base/core/java/android/hardware/camera2/CameraCharacteristics.java # getPhysicalCameraIds
在API级别29之前,所有返回的id都保证由{@link CameraManager#getCameraIdList}返回,并且可以通过{@link CameraManager#openCamera}直接打开。
从API级别29开始,对于每个返回的ID,如果它也由{@link CameraManager#getCameraIdList}返回,它可以被{@link CameraManager#openCamera}用作一个独立的相机。否则,摄像机ID只能作为当前逻辑摄像机的一部分使用。
/**
* Returns the set of physical camera ids that this logical {@link CameraDevice} is
* made up of.
*
* <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
* @return Set of physical camera ids for this logical camera device.
*/
@NonNull
public Set<String> getPhysicalCameraIds() {
return mProperties.getPhysicalCameraIds();
}
2…2.3 CameraDeviceImpl
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
/**
- HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
*/
public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
CameraCharacteristics characteristics,
Map<String, CameraCharacteristics> physicalIdsToChars,
int appTargetSdkVersion,
Context ctx) {
if (cameraId == null || callback == null || executor == null || characteristics == null) {
throw new IllegalArgumentException("Null argument given");
}
mCameraId = cameraId;
mDeviceCallback = callback;
mDeviceExecutor = executor;
mCharacteristics = characteristics;
mPhysicalIdsToChars = physicalIdsToChars;
mAppTargetSdkVersion = appTargetSdkVersion;
mContext = ctx;
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
if (tag.length() > MAX_TAG_LEN) {
tag = tag.substring(0, MAX_TAG_LEN);
}
TAG = tag;
Integer partialCount =
mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
if (partialCount == null) {
// 1 means partial result is not supported.
mTotalPartialCount = 1;
} else {
mTotalPartialCount = partialCount;
}
}
2.2.3.1 getCallbacks()
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java # getCallbacks()
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
public CameraDeviceCallbacks getCallbacks() {
return mCallbacks;
}
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java # CameraDeviceCallbacks
/**
* A callback for notifications about the state of a camera device, adding in the callbacks that
* were part of the earlier KK API design, but now only used internally.
*/
public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
}}
2.3 打开 camera device
经过追踪,和前面的参数、回调、权限等准备,最终来到真正打开相机的逻辑入口
//callbacks 实际上是 CameraDeviceImpl # mCallbacks
// frameworks/base/core/java/android/hardware/camera2/CameraManager.java # openCameraDeviceUserAsync
cameraUser = cameraService.connectDevice(callbacks, cameraId,
mContext.getOpPackageName(), mContext.getAttributionTag(), uid,
oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion,
overrideToPortrait);
ICameraService.aidl
通过aidl 接口,最终会调用到CameraService 对应的接口,这里就跨进程了,下面就是 native framework的代码,暂时先追踪到这里,下次从CameraService往下追踪到HAL
/**
* Open a camera device through the new camera API
* Only supported for device HAL versions >= 3.2
*/
ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks,
@utf8InCpp String cameraId,
@utf8InCpp String opPackageName,
@nullable @utf8InCpp String featureId,
int clientUid, int oomScoreOffset,
int targetSdkVersion,
boolean overrideToPortrait);