| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "CameraOfflineClient" |
| #define ATRACE_TAG ATRACE_TAG_CAMERA |
| //#define LOG_NDEBUG 0 |
| |
| #include "CameraOfflineSessionClient.h" |
| #include "utils/CameraThreadState.h" |
| #include <utils/Trace.h> |
| #include <camera/StringUtils.h> |
| |
| namespace android { |
| |
| using binder::Status; |
| |
| status_t CameraOfflineSessionClient::initialize(sp<CameraProviderManager>, const std::string&) { |
| ATRACE_CALL(); |
| |
| if (mFrameProcessor.get() != nullptr) { |
| // Already initialized |
| return OK; |
| } |
| |
| // Verify ops permissions |
| auto res = startCameraOps(); |
| if (res != OK) { |
| return res; |
| } |
| |
| if (mOfflineSession.get() == nullptr) { |
| ALOGE("%s: Camera %s: No valid offline session", |
| __FUNCTION__, mCameraIdStr.c_str()); |
| return NO_INIT; |
| } |
| |
| mFrameProcessor = new camera2::FrameProcessorBase(mOfflineSession); |
| std::string threadName = fmt::sprintf("Offline-%s-FrameProc", mCameraIdStr.c_str()); |
| res = mFrameProcessor->run(threadName.c_str()); |
| if (res != OK) { |
| ALOGE("%s: Unable to start frame processor thread: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| return res; |
| } |
| |
| mFrameProcessor->registerListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID, |
| camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID, |
| /*listener*/this, |
| /*sendPartials*/true); |
| |
| wp<NotificationListener> weakThis(this); |
| res = mOfflineSession->initialize(weakThis); |
| if (res != OK) { |
| ALOGE("%s: Camera %s: unable to initialize device: %s (%d)", |
| __FUNCTION__, mCameraIdStr.c_str(), strerror(-res), res); |
| return res; |
| } |
| |
| for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { |
| mCompositeStreamMap.valueAt(i)->switchToOffline(); |
| } |
| |
| return OK; |
| } |
| |
| status_t CameraOfflineSessionClient::setCameraServiceWatchdog(bool) { |
| return OK; |
| } |
| |
| status_t CameraOfflineSessionClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/, |
| bool /*fromHal*/) { |
| // Since we're not submitting more capture requests, changes to rotateAndCrop override |
| // make no difference. |
| return OK; |
| } |
| |
| status_t CameraOfflineSessionClient::setAutoframingOverride(uint8_t) { |
| return OK; |
| } |
| |
| bool CameraOfflineSessionClient::supportsCameraMute() { |
| // Offline mode doesn't support muting |
| return false; |
| } |
| |
| status_t CameraOfflineSessionClient::setCameraMute(bool) { |
| return INVALID_OPERATION; |
| } |
| |
| void CameraOfflineSessionClient::setStreamUseCaseOverrides( |
| const std::vector<int64_t>& /*useCaseOverrides*/) { |
| } |
| |
| void CameraOfflineSessionClient::clearStreamUseCaseOverrides() { |
| } |
| |
| bool CameraOfflineSessionClient::supportsZoomOverride() { |
| return false; |
| } |
| |
| status_t CameraOfflineSessionClient::setZoomOverride(int32_t /*zoomOverride*/) { |
| return INVALID_OPERATION; |
| } |
| |
| status_t CameraOfflineSessionClient::dump(int fd, const Vector<String16>& args) { |
| return BasicClient::dump(fd, args); |
| } |
| |
| status_t CameraOfflineSessionClient::dumpClient(int fd, const Vector<String16>& args) { |
| std::string result; |
| |
| result = " Offline session dump:\n"; |
| write(fd, result.c_str(), result.size()); |
| |
| if (mOfflineSession.get() == nullptr) { |
| result = " *** Offline session is detached\n"; |
| write(fd, result.c_str(), result.size()); |
| return NO_ERROR; |
| } |
| |
| mFrameProcessor->dump(fd, args); |
| |
| auto res = mOfflineSession->dump(fd); |
| if (res != OK) { |
| result = fmt::sprintf(" Error dumping offline session: %s (%d)", |
| strerror(-res), res); |
| write(fd, result.c_str(), result.size()); |
| } |
| |
| return OK; |
| } |
| |
| status_t CameraOfflineSessionClient::startWatchingTags(const std::string &tags, int outFd) { |
| return BasicClient::startWatchingTags(tags, outFd); |
| } |
| |
| status_t CameraOfflineSessionClient::stopWatchingTags(int outFd) { |
| return BasicClient::stopWatchingTags(outFd); |
| } |
| |
| status_t CameraOfflineSessionClient::dumpWatchedEventsToVector(std::vector<std::string> &out) { |
| return BasicClient::dumpWatchedEventsToVector(out); |
| } |
| |
| binder::Status CameraOfflineSessionClient::disconnect() { |
| Mutex::Autolock icl(mBinderSerializationLock); |
| |
| binder::Status res = Status::ok(); |
| if (mDisconnected) { |
| return res; |
| } |
| // Allow both client and the media server to disconnect at all times |
| int callingPid = CameraThreadState::getCallingPid(); |
| if (callingPid != mClientPid && |
| callingPid != mServicePid) { |
| return res; |
| } |
| |
| mDisconnected = true; |
| |
| sCameraService->removeByClient(this); |
| sCameraService->logDisconnectedOffline(mCameraIdStr, mClientPid, mClientPackageName); |
| |
| sp<IBinder> remote = getRemote(); |
| if (remote != nullptr) { |
| remote->unlinkToDeath(sCameraService); |
| } |
| |
| mFrameProcessor->removeListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID, |
| camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID, |
| /*listener*/this); |
| mFrameProcessor->requestExit(); |
| mFrameProcessor->join(); |
| |
| finishCameraOps(); |
| ALOGI("%s: Disconnected client for offline camera %s for PID %d", __FUNCTION__, |
| mCameraIdStr.c_str(), mClientPid); |
| |
| // client shouldn't be able to call into us anymore |
| mClientPid = 0; |
| |
| if (mOfflineSession.get() != nullptr) { |
| auto ret = mOfflineSession->disconnect(); |
| if (ret != OK) { |
| ALOGE("%s: Failed disconnecting from offline session %s (%d)", __FUNCTION__, |
| strerror(-ret), ret); |
| } |
| mOfflineSession = nullptr; |
| } |
| |
| for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { |
| auto ret = mCompositeStreamMap.valueAt(i)->deleteInternalStreams(); |
| if (ret != OK) { |
| ALOGE("%s: Failed removing composite stream %s (%d)", __FUNCTION__, |
| strerror(-ret), ret); |
| } |
| } |
| mCompositeStreamMap.clear(); |
| |
| return res; |
| } |
| |
| void CameraOfflineSessionClient::notifyError(int32_t errorCode, |
| const CaptureResultExtras& resultExtras) { |
| // Thread safe. Don't bother locking. |
| // Composites can have multiple internal streams. Error notifications coming from such internal |
| // streams may need to remain within camera service. |
| bool skipClientNotification = false; |
| for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { |
| skipClientNotification |= mCompositeStreamMap.valueAt(i)->onError(errorCode, resultExtras); |
| } |
| |
| if ((mRemoteCallback.get() != nullptr) && (!skipClientNotification)) { |
| mRemoteCallback->onDeviceError(errorCode, resultExtras); |
| } |
| } |
| |
| status_t CameraOfflineSessionClient::startCameraOps() { |
| ATRACE_CALL(); |
| { |
| ALOGV("%s: Start camera ops, package name = %s, client UID = %d", |
| __FUNCTION__, mClientPackageName.c_str(), mClientUid); |
| } |
| |
| if (mAppOpsManager != nullptr) { |
| // Notify app ops that the camera is not available |
| mOpsCallback = new OpsCallback(this); |
| int32_t res; |
| // TODO : possibly change this to OP_OFFLINE_CAMERA_SESSION |
| mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA, |
| toString16(mClientPackageName), mOpsCallback); |
| // TODO : possibly change this to OP_OFFLINE_CAMERA_SESSION |
| res = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA, |
| mClientUid, toString16(mClientPackageName), /*startIfModeDefault*/ false); |
| |
| if (res == AppOpsManager::MODE_ERRORED) { |
| ALOGI("Offline Camera %s: Access for \"%s\" has been revoked", |
| mCameraIdStr.c_str(), mClientPackageName.c_str()); |
| return PERMISSION_DENIED; |
| } |
| |
| // If the calling Uid is trusted (a native service), the AppOpsManager could |
| // return MODE_IGNORED. Do not treat such case as error. |
| if (!mUidIsTrusted && res == AppOpsManager::MODE_IGNORED) { |
| ALOGI("Offline Camera %s: Access for \"%s\" has been restricted", |
| mCameraIdStr.c_str(), mClientPackageName.c_str()); |
| // Return the same error as for device policy manager rejection |
| return -EACCES; |
| } |
| } |
| |
| mOpsActive = true; |
| |
| // Transition device state to OPEN |
| sCameraService->mUidPolicy->registerMonitorUid(mClientUid, /*openCamera*/true); |
| |
| return OK; |
| } |
| |
| status_t CameraOfflineSessionClient::finishCameraOps() { |
| ATRACE_CALL(); |
| |
| // Check if startCameraOps succeeded, and if so, finish the camera op |
| if (mOpsActive) { |
| // Notify app ops that the camera is available again |
| if (mAppOpsManager != nullptr) { |
| // TODO : possibly change this to OP_OFFLINE_CAMERA_SESSION |
| mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, mClientUid, |
| toString16(mClientPackageName)); |
| mOpsActive = false; |
| } |
| } |
| // Always stop watching, even if no camera op is active |
| if (mOpsCallback != nullptr && mAppOpsManager != nullptr) { |
| mAppOpsManager->stopWatchingMode(mOpsCallback); |
| } |
| mOpsCallback.clear(); |
| |
| sCameraService->mUidPolicy->unregisterMonitorUid(mClientUid, /*closeCamera*/true); |
| |
| return OK; |
| } |
| |
| void CameraOfflineSessionClient::onResultAvailable(const CaptureResult& result) { |
| ATRACE_CALL(); |
| ALOGV("%s", __FUNCTION__); |
| |
| if (mRemoteCallback.get() != NULL) { |
| mRemoteCallback->onResultReceived(result.mMetadata, result.mResultExtras, |
| result.mPhysicalMetadatas); |
| } |
| |
| for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { |
| mCompositeStreamMap.valueAt(i)->onResultAvailable(result); |
| } |
| } |
| |
| void CameraOfflineSessionClient::notifyShutter(const CaptureResultExtras& resultExtras, |
| nsecs_t timestamp) { |
| |
| if (mRemoteCallback.get() != nullptr) { |
| mRemoteCallback->onCaptureStarted(resultExtras, timestamp); |
| } |
| |
| for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { |
| mCompositeStreamMap.valueAt(i)->onShutter(resultExtras, timestamp); |
| } |
| } |
| |
| status_t CameraOfflineSessionClient::notifyActive(float maxPreviewFps __unused) { |
| return startCameraStreamingOps(); |
| } |
| |
| void CameraOfflineSessionClient::notifyIdle( |
| int64_t /*requestCount*/, int64_t /*resultErrorCount*/, bool /*deviceError*/, |
| const std::vector<hardware::CameraStreamStats>& /*streamStats*/) { |
| if (mRemoteCallback.get() != nullptr) { |
| mRemoteCallback->onDeviceIdle(); |
| } |
| finishCameraStreamingOps(); |
| } |
| |
| void CameraOfflineSessionClient::notifyAutoFocus([[maybe_unused]] uint8_t newState, |
| [[maybe_unused]] int triggerId) { |
| ALOGV("%s: Autofocus state now %d, last trigger %d", |
| __FUNCTION__, newState, triggerId); |
| } |
| |
| void CameraOfflineSessionClient::notifyAutoExposure([[maybe_unused]] uint8_t newState, |
| [[maybe_unused]] int triggerId) { |
| ALOGV("%s: Autoexposure state now %d, last trigger %d", |
| __FUNCTION__, newState, triggerId); |
| } |
| |
| void CameraOfflineSessionClient::notifyAutoWhitebalance([[maybe_unused]] uint8_t newState, |
| [[maybe_unused]] int triggerId) { |
| ALOGV("%s: Auto-whitebalance state now %d, last trigger %d", __FUNCTION__, newState, |
| triggerId); |
| } |
| |
| void CameraOfflineSessionClient::notifyPrepared(int /*streamId*/) { |
| ALOGE("%s: Unexpected stream prepare notification in offline mode!", __FUNCTION__); |
| notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE, |
| CaptureResultExtras()); |
| } |
| |
| void CameraOfflineSessionClient::notifyRequestQueueEmpty() { |
| if (mRemoteCallback.get() != nullptr) { |
| mRemoteCallback->onRequestQueueEmpty(); |
| } |
| } |
| |
| void CameraOfflineSessionClient::notifyRepeatingRequestError(long /*lastFrameNumber*/) { |
| ALOGE("%s: Unexpected repeating request error in offline mode!", __FUNCTION__); |
| notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE, |
| CaptureResultExtras()); |
| } |
| |
| status_t CameraOfflineSessionClient::injectCamera(const std::string& injectedCamId, |
| sp<CameraProviderManager> manager) { |
| ALOGV("%s: This client doesn't support the injection camera. injectedCamId: %s providerPtr: %p", |
| __FUNCTION__, injectedCamId.c_str(), manager.get()); |
| |
| return OK; |
| } |
| |
| status_t CameraOfflineSessionClient::stopInjection() { |
| ALOGV("%s: This client doesn't support the injection camera.", __FUNCTION__); |
| |
| return OK; |
| } |
| |
| status_t CameraOfflineSessionClient::injectSessionParams( |
| const hardware::camera2::impl::CameraMetadataNative& sessionParams) { |
| ALOGV("%s: This client doesn't support the injecting session parameters camera.", |
| __FUNCTION__); |
| (void)sessionParams; |
| return OK; |
| } |
| // ---------------------------------------------------------------------------- |
| }; // namespace android |