在Android中Camera启动也是有优先级的,在实际开发中可能有时需要将特定的APP的Camera优先级提高或者降低来实现一些特殊需求,下面就一起看学习一下Camera启动时的优先级策略吧,最后会介绍如何设置特定APP使用Camera 的优先级
优先级特性:
优先级高,可以防止Camera 被别的APP抢走,在其他App使用Camera时可以抢到使用权, 优先级低,可以被其他App抢走Camera使用权,在其他App使用Camera时无法获取到使用权
Camera 优先级策略
当有多个APP使用Camera时就用到了优先级策略, 这里需要通过优先级判断Camera 的使用权交给谁。在CameraService中启动过程中进行判断, 这里直接看相关调用函数,启动过程不介绍,不了解的请阅读《Android14 源码- Camera-API2-openCamera执行过程》
framework/av/services/camera/libcameraservice/CameraService.cpp
status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,
apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,
int oomScoreOffset, bool systemNativeClient,
/*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;
{
// 省略部无关代码
// Get current active client PIDs
// 1. 获取所有使用Camera 的pid , getAllOwners() 函数返回的是pid ,将当前app的pid 放到最后面,这个很重要,后面会用到
std::vector<int> ownerPids(mActiveClientManager.getAllOwners());
ownerPids.push_back(clientPid);
// 2. 初始化大小
std::vector<int> priorityScores(ownerPids.size());
std::vector<int> states(ownerPids.size());
// Get priority scores of all active PIDs
// 3. 获取第一步中获取到的pid应用的priorityScore和state 值
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
std::map<int,resource_policy::ClientPriority> pidToPriorityMap;
// 4. 将或取到的app 对应的priorityScore和state 放到map 中
for (size_t i = 0; i < ownerPids.size() - 1; i++) {
pidToPriorityMap.emplace(ownerPids[i],
resource_policy::ClientPriority(priorityScores[i], states[i],
/* isVendorClient won't get copied over*/ false,
/* oomScoreOffset won't get copied over*/ 0));
}
// 5. 更新正在使用Camera的Prioritie
mActiveClientManager.updatePriorities(pidToPriorityMap);
// 省略部无关代码
// 6. 获取最后一个 Score 和 State , 这个是当前app 的数值
int32_t actualScore = priorityScores[priorityScores.size() - 1];
int32_t actualState = states[states.size() - 1];
// Make descriptor for incoming client. We store the oomScoreOffset
// since we might need it later on new handleEvictionsLocked and
// ProcessInfoService would not take that into account.
// 7 . 创建一个当前app的ClientDescriptor
clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),
state->getConflicting(), actualScore, clientPid, actualState,
oomScoreOffset, systemNativeClient);
resource_policy::ClientPriority clientPriority = clientDescriptor->getPriority();
// Find clients that would be evicted
// 8. 比较当前的 ClientDescriptor 是否需要被拒绝
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
// 9 . 如果当前app 的 ClientDescriptor 在evicted 中, 则返回异常code , CAMERA_IN_USE , MAX_CAMERAS_IN_USE
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();
auto incompatibleClients =
mActiveClientManager.getIncompatibleClients(clientDescriptor);
for (auto& i : incompatibleClients) {
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());
}
auto current = mActiveClientManager.get(cameraId);
if (current != nullptr) {
return -EBUSY; // CAMERA_IN_USE
} else {
return -EUSERS; // MAX_CAMERAS_IN_USE
}
}
// 省略部无关代码
}
// 省略部无关代码
return NO_ERROR;
}
分析过程都写到代码块注释中了, 下面看一下mActiveClientManager.updatePriorities(pidToPriorityMap);
framework/av/services/camera/libcameraservice/utils/ClientManager.h
template<class KEY, class VALUE, class LISTENER>
void ClientManager<KEY, VALUE, LISTENER>::updatePriorities(
const std::map<int32_t,ClientPriority>& ownerPriorityList) {
Mutex::Autolock lock(mLock);
for (auto& i : mClients) {
auto j = ownerPriorityList.find(i->getOwnerId());
if (j != ownerPriorityList.end()) {
i->setPriority(j->second);
}
}
}
遍历mClients中的每个连接,重新设置Priority, mClients是什么后面会介绍。
下面看一下核心的优先级是如何确定的,函数auto evicted = mActiveClientManager.wouldEvict(clientDescriptor); 。
template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::wouldEvict(
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
Mutex::Autolock lock(mLock);
return wouldEvictLocked(client);
}
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();
int32_t cost = client->getCost();
ClientPriority priority = client->getPriority();
int32_t owner = client->getOwnerId();
int64_t totalCost = getCurrentCostLocked() + cost;
// Determine the MRU of the owners tied for having the highest priority
int32_t highestPriorityOwner = owner;
ClientPriority highestPriority = priority;
for (const auto& i : mClients) {
// 遍历当前正在使用的app的连接,比较Priority, 找到优先级最高的cline
ClientPriority curPriority = i->getPriority();
if (curPriority <= highestPriority) {
highestPriority = curPriority;
highestPriorityOwner = i->getOwnerId();
}
}
// 如果最高优先级和当前app 的优先级相同, 将highestPriorityOwners设置为当前app
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
// 遍历正在使用app的 clinet
for (const auto& i : mClients) {
const KEY& curKey = i->getKey();
int32_t curCost = i->getCost();
ClientPriority curPriority = i->getPriority();
int32_t curOwner = i->getOwnerId();
// 检查是否有冲突
bool conflicting = (curKey == key || i->isConflicting(key) ||
client->isConflicting(curKey));
// returnIncompatibleClients 默认false
if (!returnIncompatibleClients) {
// Find evicted clients
// 有冲突并且是同一个app
if (conflicting && owner == curOwner) {
// Pre-existing conflicting client with the same client owner exists
// Open the same device twice -> most recent open wins
// Otherwise let the existing client wins to avoid behaviors difference
// due to how HAL advertising conflicting devices (which is hidden from
// application)
if (curKey == key) {
// 打开了相同的 CameraId , 把i(i 是一个 ClientDescriptor)放到list中
evictList.push_back(i);
totalCost -= curCost;
} else {
// 如果是不同的CameraId , 把当前的client(client 是一个 ClientDescriptor)放到list中
evictList.clear();
evictList.push_back(client);
return evictList;
}
} else if (conflicting && curPriority < priority) {
// Pre-existing conflicting client with higher priority exists
// 有冲突并且,当前app的priority大于已经打开的curPriority, 则会把当前的client(client 是一个 ClientDescriptor)放到list中
evictList.clear();
evictList.push_back(client);
return evictList;
} else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
(curPriority >= priority) &&
!(highestPriorityOwner == owner && owner == curOwner))) {
// 有冲突
// 或总的Cost 大于 最大的Cost 并且 已经打开的priority 大于当前打开app的priority 并且已经打开Camera的app和当前不是同一个app
// 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
if (totalCost > mMaxCost && highestPriorityOwner != owner) {
evictList.clear();
evictList.push_back(client);
return evictList;
}
return evictList;
}
上面这段代码主要两步:
- 判断是否有冲突
- 有冲突,
- 是不是同一个app的场景冲突
- priority 优先级冲突
- Cost 冲突
priority 优先级: 这里说一个priority优先级,这个priority哪里来的, priority是在app中获取到的,不同类型的应用priority会不同, 系统应用priority较高,普通应用priority一般默认为0,需要注意一下:priority 这个值越大优先级越低
Cost 代表相机的资源,每种设备都有一个最大值,实际使用中不能超过最大值
查缺补漏
mClients在上面介绍中一直出现,到底是个什么,下面一起从源码中找一下
framework/av/services/camera/libcameraservice/utils/ClientManager.h
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::addAndEvict(
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
// 省略部分代码
mClients.push_back(client);
mRemovedCondition.broadcast();
return evicted;
}
mClients是一个ClientDescriptor集合,看到这里就知道为什么上面代码中提到的i和client是ClientDescriptor了吧。 在调用addAndEvict函数将ClientDescriptor添加到mClients中, 那么这个函数是怎么调用过来的呢
framework/av/services/camera/libcameraservice/CameraService.cpp
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
int api1CameraId, const String16& clientPackageNameMaybe, bool systemNativeClient,
const std::optional<String16>& clientFeatureId, int clientUid, int clientPid,
apiLevel effectiveApiLevel, bool shimUpdateOnly, int oomScoreOffset, int targetSdkVersion,
bool overrideToPortrait, bool forceSlowJpegMode,
/*out*/sp<CLIENT>& device) {
// 省略部分代码
{
// 省略部分代码
if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel,
IInterface::asBinder(cameraCb), clientName8, oomScoreOffset, systemNativeClient,
/*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());
case -EUSERS:
return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
"Too many cameras already open, cannot open camera \"%s\"",
cameraId.string());
default:
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Unexpected error %s (%d) opening camera \"%s\"",
strerror(-err), err, cameraId.string());
}
}
// 省略部分代码
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, oomScoreOffset, systemNativeClient);
}
client->setImageDumpMask(mImageDumpMask);
client->setStreamUseCaseOverrides(mStreamUseCaseOverrides);
client->setZoomOverride(mZoomOverrideValue);
} // lock is destroyed, allow further connect calls
// 省略部分代码
return ret;
}
CameraService::connectHelper函数调用了handleEvictionsLocked和finishConnectLocked两个函数,handleEvictionsLocked不介绍了上面说了, 那么看一下finishConnectLocked函数
void CameraService::finishConnectLocked(const sp<BasicClient>& client,
const CameraService::DescriptorPtr& desc, int oomScoreOffset, bool systemNativeClient) {
// Make a descriptor for the incoming client
auto clientDescriptor =
CameraService::CameraClientManager::makeClientDescriptor(client, desc,
oomScoreOffset, systemNativeClient);
clientDescriptor->getValue().get()->mClientPackageName
auto evicted = mActiveClientManager.addAndEvict(clientDescriptor);
// 省略部分代码
}
这里面创建一个ClientDescriptor然后调用addAndEvict函数将ClientDescriptor 设置到mClients中
定制特定APP使用Camera优先及方法
现在我们知道了打开相机优先级是怎么回事之后就容易了, 如果我们想让指定的APP一直享有Camera的使用权,那么就让该App有较高的优先级就可以了,反之则有较低优先级。直接上代码吧
示例代码
framework/av/services/camera/libcameraservice/CameraService.cpp
status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,
apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,
int oomScoreOffset, bool systemNativeClient,
/*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;
{
// 省略部无关代码
// Get current active client PIDs
std::vector<int> ownerPids(mActiveClientManager.getAllOwners());
ownerPids.push_back(clientPid);
std::vector<int> priorityScores(ownerPids.size());
std::vector<int> states(ownerPids.size());
// Get priority scores of all active PIDs
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;
}
// 第一步:打开时,修改指定app的优先级
if(strcmp("AppPackageName", packageName) == 0) {
priorityScores[priorityScores.size() - 1] = 1000;
}
// Update all active clients' priorities
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],
/* isVendorClient won't get copied over*/ false,
/* oomScoreOffset won't get copied over*/ 0));
}
mActiveClientManager.updatePriorities(pidToPriorityMap);
// 第二步:获取到所有已经打开的ClientDescriptor, 遍历找到特定的app 在次修改优先级
for (auto& i : mActiveClientManager.getAll()) {
auto clientSp = i->getValue();
if (strcmp("AppPackageName", String8(clientSp->mClientPackageName)) == 0) {
i->setPriority(resource_policy::ClientPriority(1000, i->getPriority().getState(),
/* isVendorClient won't get copied over*/ false,
/* oomScoreOffset won't get copied over*/ 0));
}
}
// Get state for the given cameraId
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;
}
int32_t actualScore = priorityScores[priorityScores.size() - 1];
int32_t actualState = states[states.size() - 1];
// 省略部无关代码
}
// 省略部无关代码
return NO_ERROR;
}
上面代码中AppPackageName是要指定的app包名。注释中,第一步打开的时候调整优先级, 第二步是在有多个app竞争的时候保持第一次打开时设置的优先级,如果没有第二步,当有多个app竞争的时候,打开时设置的优先级将被重置,无法一直延续