status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,
apiLevel effectiveApiLevel, const sp& remoteCallback, const String8& packageName,
/*out*/
sp* client,
std::shared_ptr>>* partial) {
ATRACE_CALL();
status_t ret = NO_ERROR;
std::vector 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.
auto current = mActiveClientManager.get(cameraId);
if (current != nullptr) {
auto clientSp = current->getValue();
if (clientSp.get() != nullptr) { // should never be needed
if (!clientSp->canCastToApiClient(effectiveApiLevel)) {
ALOGW("CameraService connect called from same client, but with a different"
" API level, evicting prior client...");
} else if (clientSp->getRemote() == remoteCallback) {
ALOGI("CameraService::connect X (PID %d) (second call from same"
" app binder, returning the same client)", clientPid);
*client = clientSp;
return NO_ERROR;
}
}
}
}
// Get current active client PIDs
std::vector ownerPids(mActiveClientManager.getAllOwners());
ownerPids.push_back(clientPid);
// Use the value +PROCESS_STATE_NONEXISTENT, to avoid taking
// address of PROCESS_STATE_NONEXISTENT as a reference argument
// for the vector constructor. PROCESS_STATE_NONEXISTENT does
// not have an out-of-class definition.
std::vector priorities(ownerPids.size(), +PROCESS_STATE_NONEXISTENT);
// Get priorites of all active PIDs
ProcessInfoService::getProcessStatesFromPids(ownerPids.size(), &ownerPids[0],
/*out*/&priorities[0]);
// Update all active clients' priorities
std::map pidToPriorityMap;
//!++
ALOGI("Incoming clientPid(%d) packageName(%s) priorities(%d)",clientPid,packageName.string(),priorities[priorities.size() - 1]);
if(strcmp("mtk_vt_use", packageName) == 0)
{
ALOGI("Incoming client is mtk_vt_use, set priorities(old:%d) = 0",priorities[priorities.size() - 1]);
priorities[priorities.size() - 1] = 0;
}
//!--
// modify for preventing 3rd party pre-empty begin
for(size_t n = 0; n< 2; n++ )
{
String8 id = String8::format("%zu", n);
String8 pkg;
if(mActiveClientManager.getCameraClient(id)!=0)
{
pkg = String8(mActiveClientManager.getCameraClient(id)->getPackageName().string());
}
else
{
ALOGI("Camera[%zu] has no client NOW",n);
continue;
}
if(0 == strcmp("com.mediatek.camera",pkg.string())&& 0 == strcmp("com.google.android.apps.photos",packageName.string()))
{
ALOGI("mediatek camera is running, google photo is excluded");
return BAD_VALUE;
}
}
//modify for preventing 3rd party pre-empty end
for (size_t i = 0; i < ownerPids.size() - 1; i++) {
//!++
//make sure VT has top priority
if( mActiveClientManager.getCameraClient(cameraId) != 0 &&
strcmp("mtk_vt_use", String8(mActiveClientManager.getCameraClient(cameraId)->getPackageName().string())) == 0)
{
int clientPid_vt = mActiveClientManager.getCameraClient(cameraId)->getClientPid();
if(clientPid_vt == ownerPids[i])
{
priorities[i] = 0;
ALOGI("i(%zu) pid(%d) => mtk_vt_use, set priorities(old:%d) = 0",i,ownerPids[i],priorities[i]);
}
}
else if(strcmp("mtk_vt_use", packageName) == 0)
{
priorities[i] = INT_MAX;
ALOGI("i(%zu) pid(%d) not mtk_vt_use, set priorities(old:%d) = INT_MAX",i,ownerPids[i],priorities[i]);
}
ALOGI("i(%zu) pid(%d) priorities(%d),Priority(%d)",i,ownerPids[i],priorities[i],getCameraPriorityFromProcState(priorities[i]));
//!--
pidToPriorityMap.emplace(ownerPids[i], getCameraPriorityFromProcState(priorities[i]));
}
mActiveClientManager.updatePriorities(pidToPriorityMap);
// 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;
}
// Make descriptor for incoming client
clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
sp{nullptr}, static_cast(state->getCost()),
state->getConflicting(),
getCameraPriorityFromProcState(priorities[priorities.size() - 1]), clientPid);
// Find clients that would be evicted
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
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 clientSp = clientDescriptor->getValue();
String8 curTime = getFormattedCurrentTime();
auto incompatibleClients =
mActiveClientManager.getIncompatibleClients(clientDescriptor);
String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
"(PID %d, priority %d) due to eviction policy", curTime.string(),
cameraId.string(), packageName.string(), clientPid,
getCameraPriorityFromProcState(priorities[priorities.size() - 1]));
for (auto& i : incompatibleClients) {
msg.appendFormat("\n - Blocked by existing device %s client for package %s"
"(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(),
String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),
i->getPriority());
ALOGE(" Conflicts with: Device %s, client package %s (PID %"
PRId32 ", priority %" PRId32 ")", i->getKey().string(),
String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),
i->getPriority());
}
// Log the client's attempt
Mutex::Autolock l(mLogLock);
mEventLog.add(msg);
return -EBUSY;
}
for (auto& i : evicted) {
sp 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());
evictedClients.push_back(i);
// Log the clients evicted
logEvent(String8::format("EVICT device %s client held by package %s (PID"
" %" PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for"
" package %s (PID %d, priority %" PRId32 ")",
i->getKey().string(), String8{clientSp->getPackageName()}.string(),
i->getOwnerId(), i->getPriority(), cameraId.string(),
packageName.string(), clientPid,
getCameraPriorityFromProcState(priorities[priorities.size() - 1])));
// Notify the client of disconnection
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
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;
}