只要满足一定的条件是可以多进程打开相机
1、CameraService打开相机的入口
Status CameraService::connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
const String16& cameraId,
const String16& clientPackageName,
int clientUid,
/*out*/
sp<hardware::camera2::ICameraDeviceUser>* device) {
ATRACE_CALL();
Status ret = Status::ok();
String8 id = String8(cameraId);
sp<CameraDeviceClient> client = nullptr;
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
/*api1CameraId*/-1,
CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
clientUid, USE_CALLING_PID, API_2,
/*legacyMode*/ false, /*shimUpdateOnly*/ false,
/*out*/client);
if(!ret.isOk()) {
logRejected(id, getCallingPid(), String8(clientPackageName),
ret.toString8());
return ret;
}
*device = client;
return ret;
}
2、进入connectHelper
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid,
int clientPid, apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device) {
binder::Status ret = binder::Status::ok();
String8 clientName8(clientPackageName);
int originalClientPid = 0;
ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
"Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
(halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
static_cast<int>(effectiveApiLevel));
sp<CLIENT> client = nullptr;
{
// Acquire mServiceLock and prevent other clients from connecting
//1)加这个锁的主要目的是防止不同应用同一时刻尝试打开相机,比如一个应用正在打开相机,另外一个应用也在尝试打开相机,这个时候就需要等待了
std::unique_ptr<AutoConditionLock> lock =
AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);
if (lock == nullptr) {
ALOGE("CameraService::connect (PID %d) rejected (too many other clients connecting)."
, clientPid);
return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
"Cannot open camera %s for \"%s\" (PID %d): Too many other clients connecting",
cameraId.string(), clientName8.string(), clientPid);
}
// Enforce client permissions and do basic sanity checks
//2)这个主要是对应用做一些基本的检查
//包括:
//1.检测UID是否可信
//2.检测PID是否可信
//3.检测是否有anddroid.permission.CAMERA权限
//4.检测用户是否在mAllowedusers中,即检测是否是有效用户,
//mAllowedusers的赋值实在CameraserviceProxy.java
if(!(ret = validateConnectLocked(cameraId, clientName8,
/*inout*/clientUid, /*inout*/clientPid, /*out*/originalClientPid)).isOk()) {
return ret;
}
// Check the shim parameters after acquiring lock, if they have already been updated and
// we were doing a shim update, return immediately
if (shimUpdateOnly) {
auto cameraState = getCameraState(cameraId);
if (cameraState != nullptr) {
if (!cameraState->getShimParams().isEmpty()) return ret;
}
}
status_t err;
sp<BasicClient> clientTmp = nullptr;
std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;
//3)handleEvictionsLocked是处理多进程互斥逻辑的地方,
//多进程同时打开相机的互斥逻辑就是在这个函数实现。
//我们重点分析下这个函数
if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel,
IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,
/*out*/&partial)) != NO_ERROR) {
switch (err) {
case -ENODEV:
return STATUS_ERROR_FMT(ERROR_DISCONNECTED,
"No camera device with ID \"%s\" currently available",
cameraId.string());
case -EBUSY:
return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
"Higher-priority client using camera, ID \"%s\" currently unavailable",
cameraId.string());
default:
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Unexpected error %s (%d) opening camera \"%s\"",
strerror(-err), err, cameraId.string());
}
}
.......
.......
sp<BasicClient> tmp = nullptr;
if(!(ret = makeClient(this, cameraCb, clientPackageName,
cameraId, api1CameraId, facing,
clientPid, clientUid, getpid(), legacyMode,
halVersion, deviceVersion, effectiveApiLevel,
/*out*/&tmp)).isOk()) {
return ret;
}
client = static_cast<CLIENT*>(tmp.get());
LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
__FUNCTION__);
err = client->initialize(mCameraProviderManager, mMonitorTags);
......
......
if (shimUpdateOnly) {
// If only updating legacy shim parameters, immediately disconnect client
mServiceLock.unlock();
client->disconnect();
mServiceLock.lock();
} else {
// Otherwise, add client to active clients list
finishConnectLocked(client, partial);
}
} // lock is destroyed, allow further connect calls
// Important: release the mutex here so the client can call back into the service from its
// destructor (can be at the end of the call)
device = client;
return ret;
}
3、分析handleEvictionsLocked
status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,
apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,
/*out*/
sp<BasicClient>* client,
std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
ATRACE_CALL();
status_t ret = NO_ERROR;
std::vector<DescriptorPtr> evictedClients;
DescriptorPtr clientDescriptor;
{
if (effectiveApiLevel == API_1) {
// If we are using API1, any existing client for this camera ID with the same remote
// should be returned rather than evicted to allow MediaRecorder to work properly.
......
......
}
// Get current active client PIDs 获取已经打开相机的所有pid
std::vector<int> ownerPids(mActiveClientManager.getAllOwners());
//将当前正在打开相机的应用进程号也保存到ownerPids 中
ownerPids.push_back(clientPid);
//创建进程priorityScores,states ventor
std::vector<int> priorityScores(ownerPids.size());
std::vector<int> states(ownerPids.size());
// Get priority scores of all active PIDs
//获取所有ownerPids中的进程priority和states
status_t err = ProcessInfoService::getProcessStatesScoresFromPids(
ownerPids.size(), &ownerPids[0], /*out*/&states[0],
/*out*/&priorityScores[0]);
if (err != OK) {
ALOGE("%s: Priority score query failed: %d",
__FUNCTION__, err);
return err;
}
// Update all active clients' priorities
//将上一步获取的进程优先级更新到mActiveClientManager中
std::map<int,resource_policy::ClientPriority> pidToPriorityMap;
for (size_t i = 0; i < ownerPids.size() - 1; i++) {
pidToPriorityMap.emplace(ownerPids[i],
resource_policy::ClientPriority(priorityScores[i], states[i]));
}
mActiveClientManager.updatePriorities(pidToPriorityMap);
// Get state for the given cameraId
// 获取cameraId相机的状态,Cameraservice启动的时候会获取所有相机的状态
auto state = getCameraState(cameraId);
if (state == nullptr) {
ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)",
clientPid, cameraId.string());
// Should never get here because validateConnectLocked should have errored out
return BAD_VALUE;
}
// Make descriptor for incoming client
//由当前正在打开相机的cameraId,priorityScores,states,clientPid等信息
//创建ClientDescriptor对象,key=cameraId;value=null;
//ClientDescriptor未填充client,因为还未makeclient
clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),
state->getConflicting(),
//当前正在打开相机的priorityScores
priorityScores[priorityScores.size() - 1],
clientPid,
//当前正在打开相机的states
states[states.size() - 1]);
// Find clients that would be evicted
// 在mActiveClientManager查找和上一步创建的ClientDescriptor存在冲突的所有client
// 如果有冲突的client,都保存到evicted队列中
// evicted队列中的所有client都需要关闭正在使用的相机,
// 如果当前正在打开相机的client也在该evicted,说明当前client不能打开相机
// 接下来会重点分析该函数实现
auto evicted = mActiveClientManager.wouldEvict(clientDescriptor);
// If the incoming client was 'evicted,' higher priority clients have the camera in the
// background, so we cannot do evictions
//如果clientDescriptor(即当前应用)在evicted列表中,则说明当前相机应用的进程优先级较低,则本次打开相机失败,上报的错误log如下
if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) {
ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher"
" priority).", clientPid);
sp<BasicClient> clientSp = clientDescriptor->getValue();
String8 curTime = getFormattedCurrentTime();
//获取与当前client存在冲突的client
auto incompatibleClients =
mActiveClientManager.getIncompatibleClients(clientDescriptor);
//打印当前client信息
String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
"(PID %d, score %d state %d) due to eviction policy", curTime.string(),
cameraId.string(), packageName.string(), clientPid,
priorityScores[priorityScores.size() - 1],
states[states.size() - 1]);
//打印与当前client冲突的client信息
for (auto& i : incompatibleClients) {
msg.appendFormat("\n - Blocked by existing device %s client for package %s"
"(PID %" PRId32 ", score %" PRId32 ", state %" PRId32 ")",
i->getKey().string(),
String8{i->getValue()->getPackageName()}.string(),
i->getOwnerId(), i->getPriority().getScore(),
i->getPriority().getState());
ALOGE(" Conflicts with: Device %s, client package %s (PID %"
PRId32 ", score %" PRId32 ", state %" PRId32 ")", i->getKey().string(),
String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),
i->getPriority().getScore(), i->getPriority().getState());
}
// Log the client's attempt
Mutex::Autolock l(mLogLock);
mEventLog.add(msg);
return -EBUSY;
}
//关闭evicted中client正在使用的相机 如果clientDescriptor不在evicted,则强制关闭evicted中其他正在使用相机的应用
for (auto& i : evicted) {
sp<BasicClient> clientSp = i->getValue();
if (clientSp.get() == nullptr) {
ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__);
// TODO: Remove this
LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list",
__FUNCTION__);
mActiveClientManager.remove(i);
continue;
}
ALOGE("CameraService::connect evicting conflicting client for camera ID %s",
i->getKey().string());
//将有效evicted的client push到evictedClients
evictedClients.push_back(i);
// Log the clients evicted
logEvent(String8::format("EVICT device %s client held by package %s (PID"
" %" PRId32 ", score %" PRId32 ", state %" PRId32 ")\n - Evicted by device %s client for"
" package %s (PID %d, score %" PRId32 ", state %" PRId32 ")",
i->getKey().string(), String8{clientSp->getPackageName()}.string(),
i->getOwnerId(), i->getPriority().getScore(),
i->getPriority().getState(), cameraId.string(),
packageName.string(), clientPid,
priorityScores[priorityScores.size() - 1],
states[states.size() - 1]));
// Notify the client of disconnection
//通知evicted中client,相机马上就要关闭了
clientSp->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
CaptureResultExtras());
}
}
// Do not hold mServiceLock while disconnecting clients, but retain the condition blocking
// other clients from connecting in mServiceLockWrapper if held
mServiceLock.unlock();
// Clear caller identity temporarily so client disconnect PID checks work correctly
int64_t token = IPCThreadState::self()->clearCallingIdentity();
// Destroy evicted clients
//关闭所有在evictedClients中的正在使用相机的应用
for (auto& i : evictedClients) {
// Disconnect is blocking, and should only have returned when HAL has cleaned up
i->getValue()->disconnect(); // Clients will remove themselves from the active client list
}
IPCThreadState::self()->restoreCallingIdentity(token);
for (const auto& i : evictedClients) {
ALOGV("%s: Waiting for disconnect to complete for client for device %s (PID %" PRId32 ")",
__FUNCTION__, i->getKey().string(), i->getOwnerId());
ret = mActiveClientManager.waitUntilRemoved(i, DEFAULT_DISCONNECT_TIMEOUT_NS);
if (ret == TIMED_OUT) {
ALOGE("%s: Timed out waiting for client for device %s to disconnect, "
"current clients:\n%s", __FUNCTION__, i->getKey().string(),
mActiveClientManager.toString().string());
return -EBUSY;
}
if (ret != NO_ERROR) {
ALOGE("%s: Received error waiting for client for device %s to disconnect: %s (%d), "
"current clients:\n%s", __FUNCTION__, i->getKey().string(), strerror(-ret),
ret, mActiveClientManager.toString().string());
return ret;
}
}
evictedClients.clear();
// Once clients have been disconnected, relock
mServiceLock.lock();
// Check again if the device was unplugged or something while we weren't holding mServiceLock
if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) {
return ret;
}
*partial = clientDescriptor;
return NO_ERROR;
}
4、分析mActiveClientManager.wouldEvict(clientDescriptor);
实现函数为wouldEvictLocked
ClientManager<KEY, VALUE, LISTENER>::wouldEvict(
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
Mutex::Autolock lock(mLock);
return wouldEvictLocked(client);
}
//returnIncompatibleClients为默认值false
template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::wouldEvictLocked(
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
bool returnIncompatibleClients) const {
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;
// Disallow null clients, return input
if (client == nullptr) {
evictList.push_back(client);
return evictList;
}
//获取当前进程的现关信息
const KEY& key = client->getKey(); //key就是cameraid
int32_t cost = client->getCost(); //cost 在getCameraState会介绍
ClientPriority priority = client->getPriority(); //获取优先级
int32_t owner = client->getOwnerId(); //获取进程pid
int64_t totalCost = getCurrentCostLocked() + cost;
// Determine the MRU of the owners tied for having the highest priority
//获取所有打开相机应用中优先级最高的pid
int32_t highestPriorityOwner = owner;
ClientPriority highestPriority = priority;
for (const auto& i : mClients) {
ClientPriority curPriority = i->getPriority();
if (curPriority <= highestPriority) {
// 值越小优先级越高
highestPriority = curPriority;
highestPriorityOwner = i->getOwnerId();
}
}
if (highestPriority == priority) {
// Switch back owner if the incoming client has the highest priority, as it is MRU
highestPriorityOwner = owner;
}
// Build eviction list of clients to remove
//创建eviction list,这个队列就是存在冲突的client
for (const auto& i : mClients) {
const KEY& curKey = i->getKey();
int32_t curCost = i->getCost();
ClientPriority curPriority = i->getPriority();
int32_t curOwner = i->getOwnerId();
// 判断是否冲突:
//1.camera id 相同,存在冲突
//isConflicting检查底层是否存在冲突
bool conflicting = (curKey == key || i->isConflicting(key) ||
client->isConflicting(curKey));
if (!returnIncompatibleClients) { //默认returnIncompatibleClients=false
// Find evicted clients
//查找冲突client
if (conflicting && curPriority < priority) {
// Pre-existing conflicting client with higher priority exists
//之前存在的client具有较高优先级,在将当前client添加到evictList中,
//表示当前clieng不能打开相机
evictList.clear();
evictList.push_back(client);
return evictList;
} else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
(curPriority >= priority) &&
!(highestPriorityOwner == owner && owner == curOwner))) {
//在满足下边的条件时,将正在使用相机的client添加到evictList,表示在打开当前相机时,
//正在使用相机的client因为和正在打开相机的client冲突需要关闭正在使用相机的client使用的相机。
//1)存在冲突,且正在使用相机的client优先级低
//2) 不存在冲突,但是totalCost 大于最大允许值mMaxCost
//在当满足如下条件时也需要将正在使用相机的client添加到evictList:
//1.如果正在打开相机的client和正在使用相机的client不是同一进程而且
//正在打开相机的client优先级高于或者等于正在使用相机的client
//2.如果正在打开相机的client和正在使用相机的client是同一进程
//但是该进程不是最高优先级。
// Add a pre-existing client to the eviction list if:
// - We are adding a client with higher priority that conflicts with this one.
// - The total cost including the incoming client's is more than the allowable
// maximum, and the client has a non-zero cost, lower priority, and a different
// owner than the incoming client when the incoming client has the
// highest priority.
evictList.push_back(i);
totalCost -= curCost;
}
} else {
// Find clients preventing the incoming client from being added
// 这个条件上一步已经涵盖了
if (curPriority < priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {
// Pre-existing conflicting client with higher priority exists
evictList.push_back(i);
}
}
}
// Immediately return the incompatible clients if we are calculating these instead
if (returnIncompatibleClients) {
return evictList;
}
// If the total cost is too high, return the input unless the input has the highest priority
//如果经过上一步检测,totalCost还是超过最大允许值且当前client不具有最高优先级,则将当前client添加到evictList,
//表示当前client不能打开相机
if (totalCost > mMaxCost && highestPriorityOwner != owner) {
evictList.clear();
evictList.push_back(client);
return evictList;
}
return evictList;
}
4、分析getCameraState
std::shared_ptr<CameraService::CameraState> CameraService::getCameraState(
const String8& cameraId) const {
std::shared_ptr<CameraState> state;
{
Mutex::Autolock lock(mCameraStatesLock);
auto iter = mCameraStates.find(cameraId);
if (iter != mCameraStates.end()) {
state = iter->second;
}
}
return state;
}
mCameraStates的赋值时在CameraService::enumerateProviders(),
主要是 mCameraStates.emplace(id8, std::make_shared(id8, cost.resourceCost, conflicting));
就是从HAL层获取,cameraid为id8的相机的resourceCost和conflicting列表(和那些相机存在冲突)
至此分析完成,可以得到的结论是:
多进程可以在满足一定条件是能都同时打开相机,需要满足的条件有:
evictList为空且totalCost <= mMaxCost.
如果totalCost <= mMaxCost不成立,可以适当调大mMaxCost。如果evictList不为空,则可能需要调剂下进程优先级或者在cameraservice中强制改下进程优先级