/* * Copyright (C) 2020 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 "CameraServiceProxyWrapper" #define ATRACE_TAG ATRACE_TAG_CAMERA //#define LOG_NDEBUG 0 #include #include #include // remove with WB_LIBCAMERASERVICE_WITH_DEPENDENCIES #include #include #include #include #include #include "aidl/android/hardware/graphics/common/Dataspace.h" #include "CameraServiceProxyWrapper.h" namespace android { using hardware::CameraExtensionSessionStats; using hardware::CameraFeatureCombinationStats; using hardware::CameraSessionStats; using hardware::ICameraServiceProxy; using hardware::camera2::params::SessionConfiguration; namespace { // Sentinel value to be returned when extension session with a stale or invalid key is reported. const std::string POISON_EXT_STATS_KEY("poisoned_stats"); } // anonymous namespace /** * CameraSessionStatsWrapper functions */ void CameraServiceProxyWrapper::CameraSessionStatsWrapper::updateProxyDeviceState( sp& proxyBinder) { if (proxyBinder == nullptr) return; proxyBinder->notifyCameraState(mSessionStats); } void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onOpen( sp& proxyBinder) { Mutex::Autolock l(mLock); updateProxyDeviceState(proxyBinder); } void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onClose( sp& proxyBinder, int32_t latencyMs, bool deviceError) { Mutex::Autolock l(mLock); mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_CLOSED; mSessionStats.mLatencyMs = latencyMs; mSessionStats.mDeviceError = deviceError; mSessionStats.mSessionIndex = 0; updateProxyDeviceState(proxyBinder); } void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onStreamConfigured( int operatingMode, bool internalReconfig, int32_t latencyMs) { Mutex::Autolock l(mLock); if (internalReconfig) { mSessionStats.mInternalReconfigure++; } else { mSessionStats.mLatencyMs = latencyMs; mSessionStats.mSessionType = operatingMode; } } void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onActive( sp& proxyBinder, float maxPreviewFps) { Mutex::Autolock l(mLock); mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_ACTIVE; mSessionStats.mMaxPreviewFps = maxPreviewFps; mSessionStats.mSessionIndex++; updateProxyDeviceState(proxyBinder); // Reset mCreationDuration to -1 to distinguish between 1st session // after configuration, and all other sessions after configuration. mSessionStats.mLatencyMs = -1; } void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle( sp& proxyBinder, int64_t requestCount, int64_t resultErrorCount, bool deviceError, const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide, bool usedZoomOverride, std::pair mostRequestedFpsRange, const std::vector& streamStats) { Mutex::Autolock l(mLock); mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_IDLE; mSessionStats.mRequestCount = requestCount; mSessionStats.mResultErrorCount = resultErrorCount; mSessionStats.mDeviceError = deviceError; mSessionStats.mUserTag = userTag; mSessionStats.mVideoStabilizationMode = videoStabilizationMode; mSessionStats.mUsedUltraWide = usedUltraWide; mSessionStats.mUsedZoomOverride = usedZoomOverride; mSessionStats.mMostRequestedFpsRange = mostRequestedFpsRange; mSessionStats.mStreamStats = streamStats; updateProxyDeviceState(proxyBinder); mSessionStats.mInternalReconfigure = 0; mSessionStats.mStreamStats.clear(); mSessionStats.mCameraExtensionSessionStats = {}; } int64_t CameraServiceProxyWrapper::CameraSessionStatsWrapper::getLogId() { Mutex::Autolock l(mLock); return mSessionStats.mLogId; } std::string CameraServiceProxyWrapper::CameraSessionStatsWrapper::updateExtensionSessionStats( const hardware::CameraExtensionSessionStats& extStats) { Mutex::Autolock l(mLock); CameraExtensionSessionStats& currStats = mSessionStats.mCameraExtensionSessionStats; if (currStats.key != extStats.key) { // Mismatched keys. Extensions stats likely reported for a closed session ALOGW("%s: mismatched extensions stats key: current='%s' reported='%s'. Dropping stats.", __FUNCTION__, toStdString(currStats.key).c_str(), toStdString(extStats.key).c_str()); return POISON_EXT_STATS_KEY; // return poisoned key to so future calls are // definitely dropped. } // Matching keys... if (currStats.key.size()) { // non-empty matching keys. overwrite. ALOGV("%s: Overwriting extension session stats: %s", __FUNCTION__, extStats.toString().c_str()); currStats = extStats; return toStdString(currStats.key); } // Matching empty keys... if (mSessionStats.mClientName != toStdString(extStats.clientName)) { ALOGW("%s: extension stats reported for unexpected package: current='%s' reported='%s'. " "Dropping stats.", __FUNCTION__, mSessionStats.mClientName.c_str(), toStdString(extStats.clientName).c_str()); return POISON_EXT_STATS_KEY; } // Matching empty keys for the current client... if (mSessionStats.mNewCameraState == CameraSessionStats::CAMERA_STATE_OPEN || mSessionStats.mNewCameraState == CameraSessionStats::CAMERA_STATE_IDLE) { // Camera is open, but not active. It is possible that the active callback hasn't // occurred yet. Keep the stats, but don't associate it with any session. ALOGV("%s: extension stat reported for an open, but not active camera. " "Saving stats, but not generating key.", __FUNCTION__); currStats = extStats; return {}; // Subsequent calls will handle setting the correct key. } if (mSessionStats.mNewCameraState == CameraSessionStats::CAMERA_STATE_ACTIVE) { // camera is active. First call for the session! currStats = extStats; // Generate a new key from logId and sessionIndex. std::ostringstream key; key << mSessionStats.mSessionIndex << '/' << mSessionStats.mLogId; currStats.key = String16(key.str().c_str()); ALOGV("%s: New extension session stats: %s", __FUNCTION__, currStats.toString().c_str()); return toStdString(currStats.key); } // Camera is closed. Probably a stale call. ALOGW("%s: extension stats reported for closed camera id '%s'. Dropping stats.", __FUNCTION__, mSessionStats.mCameraId.c_str()); return {}; } /** * CameraServiceProxyWrapper functions */ sp CameraServiceProxyWrapper::getCameraServiceProxy() { #ifndef __BRILLO__ Mutex::Autolock al(mProxyMutex); if (mCameraServiceProxy == nullptr) { mCameraServiceProxy = getDefaultCameraServiceProxy(); } #endif return mCameraServiceProxy; } sp CameraServiceProxyWrapper::getDefaultCameraServiceProxy() { #ifndef __BRILLO__ sp sm = defaultServiceManager(); // Use checkService because cameraserver normally starts before the // system server and the proxy service. So the long timeout that getService // has before giving up is inappropriate. sp binder = sm->checkService(String16("media.camera.proxy")); if (binder != nullptr) { return interface_cast(binder); } #endif return nullptr; } void CameraServiceProxyWrapper::pingCameraServiceProxy() { sp proxyBinder = getCameraServiceProxy(); if (proxyBinder == nullptr) return; proxyBinder->pingForUserUpdate(); } int64_t CameraServiceProxyWrapper::encodeSessionConfiguration( const SessionConfiguration& sessionConfig) { int64_t features = CameraFeatureCombinationStats::CAMERA_FEATURE_UNKNOWN; const static int32_t WIDTH_4K = 3840; const static int32_t HEIGHT_4K = 2160; // Check session parameters if (sessionConfig.hasSessionParameters()) { const auto& parameters = sessionConfig.getSessionParameters(); camera_metadata_ro_entry fpsEntry = parameters.find(ANDROID_CONTROL_AE_TARGET_FPS_RANGE); if (fpsEntry.count == 2 && fpsEntry.data.i32[1] == 60) { features |= CameraFeatureCombinationStats::CAMERA_FEATURE_60_FPS; } camera_metadata_ro_entry stabEntry = parameters.find(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE); if (stabEntry.count == 1 && stabEntry.data.u8[0] == ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) { features |= CameraFeatureCombinationStats::CAMERA_FEATURE_STABILIZATION; } } // Check output configurations const auto& outputConfigs = sessionConfig.getOutputConfigurations(); for (const auto& config : outputConfigs) { int format = config.getFormat(); int dataSpace = config.getDataspace(); int64_t dynamicRangeProfile = config.getDynamicRangeProfile(); // Check JPEG and JPEG_R features if (format == HAL_PIXEL_FORMAT_BLOB) { if (dataSpace == HAL_DATASPACE_V0_JFIF) { features |= CameraFeatureCombinationStats::CAMERA_FEATURE_JPEG; } else if (dataSpace == static_cast( aidl::android::hardware::graphics::common::Dataspace::JPEG_R)) { features |= CameraFeatureCombinationStats::CAMERA_FEATURE_JPEG_R; } } else { if (dynamicRangeProfile == HAL_DATASPACE_BT2020_HLG) { features |= CameraFeatureCombinationStats::CAMERA_FEATURE_HLG10; } // Check 4K const std::vector& surfaces = config.getSurfaces(); int32_t width = 0, height = 0; if (surfaces.size() > 0) { #if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES if (surfaces[0].isEmpty()) { #else if (surfaces[0] == nullptr) { #endif ALOGE("%s: Failed to query size due to abandoned surface.", __FUNCTION__); return CameraFeatureCombinationStats::CAMERA_FEATURE_UNKNOWN; } #if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES sp surface = surfaces[0].toSurface(); #else sp surface = new Surface(surfaces[0], /*useAsync*/false); #endif ANativeWindow *anw = surface.get(); width = ANativeWindow_getWidth(anw); if (width < 0) { ALOGE("%s: Failed to query Surface width: %s (%d)", __FUNCTION__, strerror(-width), width); return CameraFeatureCombinationStats::CAMERA_FEATURE_UNKNOWN; } height = ANativeWindow_getHeight(anw); if (height < 0) { ALOGE("%s: Failed to query Surface height: %s (%d)", __FUNCTION__, strerror(-height), height); return CameraFeatureCombinationStats::CAMERA_FEATURE_UNKNOWN; } } else { width = config.getWidth(); height = config.getHeight(); } if (width == WIDTH_4K && height == HEIGHT_4K) { features |= CameraFeatureCombinationStats::CAMERA_FEATURE_4K; } } } return features; } // Note: The `ret` parameter is the return value of the // `isSessionConfigurationWithParametersSupporteed` binder call from the app. void CameraServiceProxyWrapper::logFeatureCombinationInternal( const std::string &cameraId, int clientUid, const SessionConfiguration& sessionConfiguration, binder::Status ret, int type) { sp proxyBinder = getCameraServiceProxy(); if (proxyBinder == nullptr) return; int64_t featureCombination = encodeSessionConfiguration(sessionConfiguration); int queryStatus = ret.isOk() ? OK : ret.serviceSpecificErrorCode(); CameraFeatureCombinationStats stats; stats.mCameraId = cameraId; stats.mUid = clientUid; stats.mFeatureCombination = featureCombination; stats.mQueryType = type; stats.mStatus = queryStatus; auto status = proxyBinder->notifyFeatureCombinationStats(stats); if (!status.isOk()) { ALOGE("%s: Failed to notify feature combination stats: %s", __FUNCTION__, status.exceptionMessage().c_str()); } } int CameraServiceProxyWrapper::getRotateAndCropOverride(const std::string &packageName, int lensFacing, int userId) { sp proxyBinder = getCameraServiceProxy(); if (proxyBinder == nullptr) return true; int ret = 0; auto status = proxyBinder->getRotateAndCropOverride(packageName, lensFacing, userId, &ret); if (!status.isOk()) { ALOGE("%s: Failed during top activity orientation query: %s", __FUNCTION__, status.exceptionMessage().c_str()); } return ret; } int CameraServiceProxyWrapper::getAutoframingOverride(const std::string& packageName) { sp proxyBinder = getCameraServiceProxy(); if (proxyBinder == nullptr) { return ANDROID_CONTROL_AUTOFRAMING_OFF; } int ret = 0; auto status = proxyBinder->getAutoframingOverride(packageName, &ret); if (!status.isOk()) { ALOGE("%s: Failed during autoframing override query: %s", __FUNCTION__, status.exceptionMessage().c_str()); } return ret; } void CameraServiceProxyWrapper::logStreamConfigured(const std::string& id, int operatingMode, bool internalConfig, int32_t latencyMs) { std::shared_ptr sessionStats; { Mutex::Autolock l(mLock); if (mSessionStatsMap.count(id) == 0) { ALOGE("%s: SessionStatsMap should contain camera %s", __FUNCTION__, id.c_str()); return; } sessionStats = mSessionStatsMap[id]; } ALOGV("%s: id %s, operatingMode %d, internalConfig %d, latencyMs %d", __FUNCTION__, id.c_str(), operatingMode, internalConfig, latencyMs); sessionStats->onStreamConfigured(operatingMode, internalConfig, latencyMs); } void CameraServiceProxyWrapper::logActive(const std::string& id, float maxPreviewFps) { std::shared_ptr sessionStats; { Mutex::Autolock l(mLock); if (mSessionStatsMap.count(id) == 0) { ALOGE("%s: SessionStatsMap should contain camera %s when logActive is called", __FUNCTION__, id.c_str()); return; } sessionStats = mSessionStatsMap[id]; } ALOGV("%s: id %s", __FUNCTION__, id.c_str()); sp proxyBinder = getCameraServiceProxy(); sessionStats->onActive(proxyBinder, maxPreviewFps); } void CameraServiceProxyWrapper::logIdle(const std::string& id, int64_t requestCount, int64_t resultErrorCount, bool deviceError, const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide, bool usedZoomOverride, std::pair mostRequestedFpsRange, const std::vector& streamStats) { std::shared_ptr sessionStats; { Mutex::Autolock l(mLock); if (mSessionStatsMap.count(id) == 0) { ALOGE("%s: SessionStatsMap should contain camera %s when logIdle is called", __FUNCTION__, id.c_str()); return; } sessionStats = mSessionStatsMap[id]; } ALOGV("%s: id %s, requestCount %" PRId64 ", resultErrorCount %" PRId64 ", deviceError %d" ", userTag %s, videoStabilizationMode %d, most common FPS [%d,%d]", __FUNCTION__, id.c_str(), requestCount, resultErrorCount, deviceError, userTag.c_str(), videoStabilizationMode, mostRequestedFpsRange.first, mostRequestedFpsRange.second); for (size_t i = 0; i < streamStats.size(); i++) { ALOGV("%s: streamStats[%zu]: w %d h %d, requestedCount %" PRId64 ", dropCount %" PRId64 ", startTimeMs %d" , __FUNCTION__, i, streamStats[i].mWidth, streamStats[i].mHeight, streamStats[i].mRequestCount, streamStats[i].mErrorCount, streamStats[i].mStartLatencyMs); } sp proxyBinder = getCameraServiceProxy(); sessionStats->onIdle(proxyBinder, requestCount, resultErrorCount, deviceError, userTag, videoStabilizationMode, usedUltraWide, usedZoomOverride, mostRequestedFpsRange, streamStats); } void CameraServiceProxyWrapper::logOpen(const std::string& id, int facing, const std::string& clientPackageName, int effectiveApiLevel, bool isNdk, int32_t latencyMs) { std::shared_ptr sessionStats; { Mutex::Autolock l(mLock); if (mSessionStatsMap.count(id) > 0) { ALOGE("%s: SessionStatsMap shouldn't contain camera %s", __FUNCTION__, id.c_str()); return; } int apiLevel = CameraSessionStats::CAMERA_API_LEVEL_1; if (effectiveApiLevel == 2) { apiLevel = CameraSessionStats::CAMERA_API_LEVEL_2; } // Generate a new log ID for open events int64_t logId = generateLogId(mRandomDevice); sessionStats = std::make_shared( id, facing, CameraSessionStats::CAMERA_STATE_OPEN, clientPackageName, apiLevel, isNdk, latencyMs, logId); mSessionStatsMap.emplace(id, sessionStats); ALOGV("%s: Adding id %s", __FUNCTION__, id.c_str()); } ALOGV("%s: id %s, facing %d, effectiveApiLevel %d, isNdk %d, latencyMs %d", __FUNCTION__, id.c_str(), facing, effectiveApiLevel, isNdk, latencyMs); sp proxyBinder = getCameraServiceProxy(); sessionStats->onOpen(proxyBinder); } void CameraServiceProxyWrapper::logClose(const std::string& id, int32_t latencyMs, bool deviceError) { std::shared_ptr sessionStats; { Mutex::Autolock l(mLock); if (mSessionStatsMap.count(id) == 0) { ALOGE("%s: SessionStatsMap should contain camera %s before it's closed", __FUNCTION__, id.c_str()); return; } sessionStats = mSessionStatsMap[id]; if (sessionStats == nullptr) { ALOGE("%s: SessionStatsMap should contain camera %s", __FUNCTION__, id.c_str()); return; } mSessionStatsMap.erase(id); ALOGV("%s: Erasing id %s, deviceError %d", __FUNCTION__, id.c_str(), deviceError); } ALOGV("%s: id %s, latencyMs %d, deviceError %d", __FUNCTION__, id.c_str(), latencyMs, deviceError); sp proxyBinder = getCameraServiceProxy(); sessionStats->onClose(proxyBinder, latencyMs, deviceError); } bool CameraServiceProxyWrapper::isCameraDisabled(int userId) { sp proxyBinder = getCameraServiceProxy(); if (proxyBinder == nullptr) return true; bool ret = false; auto status = proxyBinder->isCameraDisabled(userId, &ret); if (!status.isOk()) { ALOGE("%s: Failed during camera disabled query: %s", __FUNCTION__, status.exceptionMessage().c_str()); } return ret; } int64_t CameraServiceProxyWrapper::getCurrentLogIdForCamera(const std::string& cameraId) { std::shared_ptr stats; { Mutex::Autolock _l(mLock); if (mSessionStatsMap.count(cameraId) == 0) { ALOGE("%s: SessionStatsMap should contain camera %s before asking for its logging ID.", __FUNCTION__, cameraId.c_str()); return 0; } stats = mSessionStatsMap[cameraId]; } return stats->getLogId(); } int64_t CameraServiceProxyWrapper::generateLogId(std::random_device& randomDevice) { int64_t ret = 0; do { // std::random_device generates 32 bits per call, so we call it twice ret = randomDevice(); ret = ret << 32; ret = ret | randomDevice(); } while (ret == 0); // 0 is not a valid identifier return ret; } std::string CameraServiceProxyWrapper::updateExtensionStats( const hardware::CameraExtensionSessionStats& extStats) { std::shared_ptr stats; std::string cameraId = toStdString(extStats.cameraId); { Mutex::Autolock _l(mLock); if (mSessionStatsMap.count(cameraId) == 0) { ALOGE("%s CameraExtensionSessionStats reported for camera id that isn't open: %s", __FUNCTION__, cameraId.c_str()); return {}; } stats = mSessionStatsMap[cameraId]; return stats->updateExtensionSessionStats(extStats); } } } // namespace android