前言
Android在版本更新和迭代过程中, Camera相关代码也经历了几个版本的更新, 主要表现为Camera HAL版本更新(HAL1 -> HAL2 -> HAL3), Camera API版本更新(Camera API1 -> Camera API2), 由于Android版本是向后兼容的, 所以为了解决兼容问题, 势必要做一些特殊的处理来保证不同版本调用问题, 本文主要说明 Camera HAL1, Camera HAL3, API1, API2之间的调用关系, 由于Camera HAL2只是一个过渡版本, 并且实际上并没有使用, 所以不做讨论.
说明: Camera API1泛指 android.hardware.camera
包下的相关API, Camera API2泛指
android.hardware.camera2
包下的相关API.
HAL版本和Java API版本是否能交叉调用?
由于HAL版本有HAL1和HAL3, Java API有 API1和API2. 两两组合调用就要4种方式, 那么这4种方式在Android中是否都有其使用场景呢?
答案是肯定的, 即4种组合(API1 -> HAL1, API1 -> HAL3, API2 -> HAL1, API2 -> HAL3)在Android系统中都能正常调用, 根据Android系统HAL层支持情况和API使用情况不同, 就会出现上述四种组合的使用场景, 下面就具体介绍一下4种组合是在那种情况下使用到的, 以及基本实现原理.
版本转换以及代码位置
对于Google最初的设计初衷, Camera API1就是用来调用HAL1的, Camera API2则是用来调用HAL3的, 但由于Android碎片化严重, 加上要兼容版本, 所以API1也可以调用HAL3, API2也可以调用HAL1,转换原理如下:
说明: 以下所有代码片段为高通平台, Android 7.1.1源码中的代码
API1调用HAL3
API1调用HAL3是在CameraService中完成转换, 使用了不同的Client(Client泛指CameraService和HAL层之间的接口, 有三个版本), 代码如下:
源码位置: frameworks/av/services/camera/libcameraservice/CameraService.cpp
代码:
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, int cameraId,
int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client) {
if (halVersion < 0 || halVersion == deviceVersion) {
// Default path: HAL version is unspecified by caller, create CameraClient
// based on device version reported by the HAL.
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName, cameraId, facing,
clientPid, clientUid, getpid(), legacyMode);
} else { // Camera2 API route
ALOGW("Camera using old HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
"Camera device \"%d\" HAL version %d does not support camera2 API",
cameraId, deviceVersion);
}
break;
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
case CAMERA_DEVICE_API_VERSION_3_2:
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new Camera2Client(cameraService, tmp, packageName, cameraId, facing,
clientPid, clientUid, servicePid, legacyMode);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
*client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
facing, clientPid, clientUid, servicePid);
}
break;
default:
// Should not be reachable
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Camera device \"%d\" has unknown HAL version %d",
cameraId, deviceVersion);
}
} else {
// A particular HAL version is requested by caller. Create CameraClient
// based on the requested HAL version.
if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
// Only support higher HAL version device opened as HAL1.0 device.
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName, cameraId, facing,
clientPid, clientUid, servicePid, legacyMode);
} else {
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
" opened as HAL %x device", halVersion, deviceVersion,
CAMERA_DEVICE_API_VERSION_1_0);
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"Camera device \"%d\" (HAL version %d) cannot be opened as HAL version %d",
cameraId, deviceVersion, halVersion);
}
}
return Status::ok();
}
上述代码中基本判断逻辑是:
- 打开HAL版本没有特殊指定(即正常调用Camera.open()),或者指定的HAL版本和要打开的Camera默认的HAL版本相同, 则根据要打开的Camera的默认HAL版本创建Client, 此时,如果HAL层默认使用HAL1,则创建
CameraClient
, 如果HAL层默认使用HAL3, 根据使用API进行判断, 使用API1则创建Camera2Client
, 使用API2则创建CameraDeviceClient
. - 如果指定了一个特殊的HAL版本, 并且不是默认的HAL版本, 此时只会创建
CameraClient
, 其他情况不支持, 原因注释里面也说明了.
上述几个Client代码位置为:
frameworks/av/services/camera/libcameraservice/api1/
frameworks/av/services/camera/libcameraservice/api2/
其中 CameraClient.cpp
是API调用HAL1的接口, Camera2Client.cpp
是API1调用HAL3接口, CameraDeviceClient
是API2调用HAL3接口, 是不是发现没有API2调用HAL1接口? 接下来就会讲这个了, 和上述转换有区别.
API2调用HAL1
在4种调用组合里面, 有3种是通过在CameraService中创建不同的Client来实现的, 只有API2调用HAL1是在API层面实现的,即把Camera API2的API调用转为Camera API1, 下面通过源码来看下使用API2调用HAL1时打开Camera操作是如何转换的.
Camera API2 打开Camera流程
frameworks/base/core/java/android/hardware/camera2/CameraManager.java
public void openCamera(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
}
调用 openCameraForUid
public void openCameraForUid(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
int clientUid)
throws 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(
"Handler argument is null, but no looper exists in the calling thread");
}
}
openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
}
调用 openCameraDeviceUserAsync
private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateCallback callback, Handler handler, final int uid)
throws CameraAccessException {
//部分源码省略......
ICameraDeviceUser cameraUser = null;
try {
if (supportsCamera2ApiLocked(cameraId)) {
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new ServiceSpecificException(
ICameraService.ERROR_DISCONNECTED,
"Camera service is currently unavailable");
}
cameraUser = cameraService.connectDevice(callbacks, id,
mContext.getOpPackageName(), uid);
} else {
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
}
}
//部分源码省略......
上面的 supportsCamera2ApiLocked(cameraId)
就是判断是否支持HAL3, 如果不支持, 就调用
CameraDeviceUserShim.connectBinderShim(callbacks, id);
来获取
ICameraDeviceUser
, 最后看下函数 connectBinderShim()
代码:
import android.hardware.Camera;
public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
int cameraId) {
//部分代码省略...
// TODO: Make this async instead of blocking
int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS);
Camera legacyCamera = init.getCamera();
//部分代码省略...
LegacyCameraDevice device = new LegacyCameraDevice(
cameraId, legacyCamera, characteristics, threadCallbacks);
return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks);
}
从上面import语句和 Camera legacyCamera = init.getCamera();
就能看出来,此处是调用Camera API1接口来实现的, 所以Camera API2调用HAL1其实是在Framework层将API2接口转为了API1, 对CameraService来说, 就是使用API1来调用. 转换代码路径在: frameworks/base/core/java/android/hardware/camera2/legacy/
, 有兴趣的可以看下.
各种版本调用使用场景
从上面的分析大家都了解了API和HAL之间不同版本转换关系, 之所以有这么多转换, 就是为了实现兼容, 因此下面简单说一下各个版本在什么场景下有用武之地.
- API1 调用 HAL1
Android 5.0之前, 几乎所有手机都使用的场景,也是最开始Camera API1设计的初衷 - API1 调用 HAL3
对于支持HAL3手机,并默认使用HAL3作为HAL的版本, 此种类型是手机想使用HAL3和Camera API2, 单由于没法限制第三方应用也使用Camera API2,而做出的一种兼容方式. - API2 调用 HAL1
手机不支持HAL3, 但应用使用了Camera API2, 常见于中低端手机, Android版本是5.0以上, 但底层不支持HAL3. - API2 调用 HAL3
Camera API2设计初衷就是调用HAL3的, 现在大多数中高端手机基本都是API2调用HAL3, 最大限度使用Camera提供的功能
总结
- 不同API和HAL版本直接调用可简单总结为以下几点:
API1 -> HAL1, 原始Camera流程, CameraService中使用的Client为CameraClient
API1 -> HAL3, 兼容使用API1的应用, CameraService中使用的Client为Camera2Client
API2 -> HAL1, 底层只支持HAL1, 为了兼容API2应用, 通过Framework层将API2转为API1实现
API2 -> HAL3, 未来趋势, 提供更好的Camera控制能力, CameraService使用的Client为CameraDeviceClient
- API1调用HAL3和API2调用HAL1对于应用来说, 都没有发挥HAL3和API2的功能, 只是一个兼容方案.
最后通过一张图片来直观明了的说明版本直接关系: