diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 4a801a7a70..488641d757 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -7758,6 +7758,13 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { */ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13, + /** + *

The camera device is only accessible by Android's system components and privileged + * applications. Processes need to have the android.permission.SYSTEM_CAMERA in + * addition to android.permission.CAMERA in order to connect to this camera device.

+ */ + ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14, + } acamera_metadata_enum_android_request_available_capabilities_t; diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index a4868bfbd2..9a91ea0689 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -117,7 +117,12 @@ static void setLogLevel(int level) { // ---------------------------------------------------------------------------- +static const String16 sDumpPermission("android.permission.DUMP"); static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA"); +static const String16 sCameraPermission("android.permission.CAMERA"); +static const String16 sSystemCameraPermission("android.permission.SYSTEM_CAMERA"); +static const String16 + sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS"); // Matches with PERCEPTIBLE_APP_ADJ in ProcessList.java static constexpr int32_t kVendorClientScore = 200; @@ -239,7 +244,7 @@ void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeS Mutex::Autolock lock(mStatusListenerLock); for (auto& i : mListenerList) { - i.second->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); + i->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); } } @@ -514,6 +519,11 @@ Status CameraService::getCameraCharacteristics(const String16& cameraId, "Camera subsystem is not available");; } + if (shouldRejectSystemCameraConnection(String8(cameraId))) { + return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera" + "characteristics for system only device %s: ", String8(cameraId).string()); + } + Status ret{}; status_t res = mCameraProviderManager->getCameraCharacteristics( @@ -527,9 +537,12 @@ Status CameraService::getCameraCharacteristics(const String16& cameraId, int callingPid = CameraThreadState::getCallingPid(); int callingUid = CameraThreadState::getCallingUid(); std::vector tagsRemoved; - // If it's not calling from cameraserver, check the permission. + // If it's not calling from cameraserver, check the permission only if + // android.permission.CAMERA is required. If android.permission.SYSTEM_CAMERA was needed, + // it would've already been checked in shouldRejectSystemCameraConnection. if ((callingPid != getpid()) && - !checkPermission(String16("android.permission.CAMERA"), callingPid, callingUid)) { + (getSystemCameraKind(String8(cameraId)) != SystemCameraKind::SYSTEM_ONLY_CAMERA) && + !checkPermission(sCameraPermission, callingPid, callingUid)) { res = cameraInfo->removePermissionEntries( mCameraProviderManager->getProviderTagIdLocked(String8(cameraId).string()), &tagsRemoved); @@ -969,9 +982,18 @@ Status CameraService::validateClientPermissionsLocked(const String8& cameraId, clientName8.string(), clientUid, clientPid); } - // If it's not calling from cameraserver, check the permission. + if (shouldRejectSystemCameraConnection(cameraId)) { + ALOGW("Attempting to connect to system-only camera id %s, connection rejected", + cameraId.c_str()); + return STATUS_ERROR_FMT(ERROR_DISCONNECTED, "No camera device with ID \"%s\" is" + "available", cameraId.string()); + } + // If it's not calling from cameraserver, check the permission if the + // device isn't a system only camera (shouldRejectSystemCameraConnection already checks for + // android.permission.SYSTEM_CAMERA for system only camera devices). if (callingPid != getpid() && - !checkPermission(String16("android.permission.CAMERA"), clientPid, clientUid)) { + (getSystemCameraKind(cameraId) != SystemCameraKind::SYSTEM_ONLY_CAMERA) && + !checkPermission(sCameraPermission, clientPid, clientUid)) { ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", clientPid, clientUid); return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED, "Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" without camera permission", @@ -1324,15 +1346,63 @@ Status CameraService::connectLegacy( return ret; } -bool CameraService::shouldRejectHiddenCameraConnection(const String8 & cameraId) { - // If the thread serving this call is not a hwbinder thread and the caller - // isn't the cameraserver itself, and the camera id being requested is to be - // publically hidden, we should reject the connection. +static bool hasPermissionsForSystemCamera(int callingPid, int callingUid) { + return checkPermission(sSystemCameraPermission, callingPid, callingUid) && + checkPermission(sCameraPermission, callingPid, callingUid); +} + +bool CameraService::shouldSkipStatusUpdates(const String8& cameraId, bool isVendorListener, + int clientPid, int clientUid) const { + SystemCameraKind systemCameraKind = getSystemCameraKind(cameraId); + // If the client is not a vendor client, don't add listener if + // a) the camera is a publicly hidden secure camera OR + // b) the camera is a system only camera and the client doesn't + // have android.permission.SYSTEM_CAMERA permissions. + if (!isVendorListener && (systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA || + (systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA && + !hasPermissionsForSystemCamera(clientPid, clientUid)))) { + return true; + } + return false; +} + +bool CameraService::shouldRejectSystemCameraConnection(const String8& cameraId) const { + // Rules for rejection: + // 1) If cameraserver tries to access this camera device, accept the + // connection. + // 2) The camera device is a publicly hidden secure camera device AND some + // component is trying to access it on a non-hwbinder thread (generally a non HAL client), + // reject it. + // 3) if the camera device is advertised by the camera HAL as SYSTEM_ONLY + // and the serving thread is a non hwbinder thread, the client must have + // android.permission.SYSTEM_CAMERA permissions to connect. + + int cPid = CameraThreadState::getCallingPid(); + int cUid = CameraThreadState::getCallingUid(); + SystemCameraKind systemCameraKind = getSystemCameraKind(cameraId); + + // (1) Cameraserver trying to connect, accept. + if (CameraThreadState::getCallingPid() == getpid()) { + return false; + } + // (2) + if (!hardware::IPCThreadState::self()->isServingCall() && + systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA) { + ALOGW("Rejecting access to secure hidden camera %s", cameraId.c_str()); + return true; + } + // (3) Here we only check for permissions if it is a system only camera device. This is since + // getCameraCharacteristics() allows for calls to succeed (albeit after hiding some + // characteristics) even if clients don't have android.permission.CAMERA. We do not want the + // same behavior for system camera devices. if (!hardware::IPCThreadState::self()->isServingCall() && - CameraThreadState::getCallingPid() != getpid() && - mCameraProviderManager->isPublicallyHiddenSecureCamera(cameraId.c_str())) { + systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA && + !hasPermissionsForSystemCamera(cPid, cUid)) { + ALOGW("Rejecting access to system only camera %s, inadequete permissions", + cameraId.c_str()); return true; } + return false; } @@ -1385,14 +1455,6 @@ Status CameraService::connectHelper(const sp& cameraCb, const String8& (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(), static_cast(effectiveApiLevel)); - if (shouldRejectHiddenCameraConnection(cameraId)) { - ALOGW("Attempting to connect to system-only camera id %s, connection rejected", - cameraId.c_str()); - return STATUS_ERROR_FMT(ERROR_DISCONNECTED, - "No camera device with ID \"%s\" currently available", - cameraId.string()); - - } sp client = nullptr; { // Acquire mServiceLock and prevent other clients from connecting @@ -1668,8 +1730,7 @@ Status CameraService::notifySystemEvent(int32_t eventId, if (pid != selfPid) { // Ensure we're being called by system_server, or similar process with // permissions to notify the camera service about system events - if (!checkCallingPermission( - String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) { + if (!checkCallingPermission(sCameraSendSystemEventsPermission)) { const int uid = CameraThreadState::getCallingUid(); ALOGE("Permission Denial: cannot send updates to camera service about system" " events from pid=%d, uid=%d", pid, uid); @@ -1704,7 +1765,7 @@ void CameraService::notifyMonitoredUids() { Mutex::Autolock lock(mStatusListenerLock); for (const auto& it : mListenerList) { - auto ret = it.second->getListener()->onCameraAccessPrioritiesChanged(); + auto ret = it->getListener()->onCameraAccessPrioritiesChanged(); if (!ret.isOk()) { ALOGE("%s: Failed to trigger permission callback: %d", __FUNCTION__, ret.exceptionCode()); @@ -1720,8 +1781,7 @@ Status CameraService::notifyDeviceStateChange(int64_t newState) { if (pid != selfPid) { // Ensure we're being called by system_server, or similar process with // permissions to notify the camera service about system events - if (!checkCallingPermission( - String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) { + if (!checkCallingPermission(sCameraSendSystemEventsPermission)) { const int uid = CameraThreadState::getCallingUid(); ALOGE("Permission Denial: cannot send updates to camera service about device" " state changes from pid=%d, uid=%d", pid, uid); @@ -1775,20 +1835,23 @@ Status CameraService::addListenerHelper(const sp& listen return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null listener given to addListener"); } + auto clientUid = CameraThreadState::getCallingUid(); + auto clientPid = CameraThreadState::getCallingPid(); + Mutex::Autolock lock(mServiceLock); { Mutex::Autolock lock(mStatusListenerLock); for (const auto &it : mListenerList) { - if (IInterface::asBinder(it.second->getListener()) == IInterface::asBinder(listener)) { + if (IInterface::asBinder(it->getListener()) == IInterface::asBinder(listener)) { ALOGW("%s: Tried to add listener %p which was already subscribed", __FUNCTION__, listener.get()); return STATUS_ERROR(ERROR_ALREADY_EXISTS, "Listener already registered"); } } - auto clientUid = CameraThreadState::getCallingUid(); - sp serviceListener = new ServiceListener(this, listener, clientUid); + sp serviceListener = + new ServiceListener(this, listener, clientUid, clientPid, isVendorListener); auto ret = serviceListener->initialize(); if (ret != NO_ERROR) { String8 msg = String8::format("Failed to initialize service listener: %s (%d)", @@ -1796,7 +1859,10 @@ Status CameraService::addListenerHelper(const sp& listen ALOGE("%s: %s", __FUNCTION__, msg.string()); return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string()); } - mListenerList.emplace_back(isVendorListener, serviceListener); + // The listener still needs to be added to the list of listeners, regardless of what + // permissions the listener process has / whether it is a vendor listener. Since it might be + // eligible to listen to other camera ids. + mListenerList.emplace_back(serviceListener); mUidPolicy->registerMonitorUid(clientUid); } @@ -1804,8 +1870,7 @@ Status CameraService::addListenerHelper(const sp& listen { Mutex::Autolock lock(mCameraStatesLock); for (auto& i : mCameraStates) { - if (!isVendorListener && - mCameraProviderManager->isPublicallyHiddenSecureCamera(i.first.c_str())) { + if (shouldSkipStatusUpdates(i.first, isVendorListener, clientPid, clientUid)) { ALOGV("Cannot add public listener for hidden system-only %s for pid %d", i.first.c_str(), CameraThreadState::getCallingPid()); continue; @@ -1844,9 +1909,9 @@ Status CameraService::removeListener(const sp& listener) { Mutex::Autolock lock(mStatusListenerLock); for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { - if (IInterface::asBinder(it->second->getListener()) == IInterface::asBinder(listener)) { - mUidPolicy->unregisterMonitorUid(it->second->getListenerUid()); - IInterface::asBinder(listener)->unlinkToDeath(it->second); + if (IInterface::asBinder((*it)->getListener()) == IInterface::asBinder(listener)) { + mUidPolicy->unregisterMonitorUid((*it)->getListenerUid()); + IInterface::asBinder(listener)->unlinkToDeath(*it); mListenerList.erase(it); return Status::ok(); } @@ -3029,7 +3094,7 @@ static bool tryLock(Mutex& mutex) status_t CameraService::dump(int fd, const Vector& args) { ATRACE_CALL(); - if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + if (checkCallingPermission(sDumpPermission) == false) { dprintf(fd, "Permission Denial: can't dump CameraService from pid=%d, uid=%d\n", CameraThreadState::getCallingPid(), CameraThreadState::getCallingUid()); @@ -3261,13 +3326,13 @@ void CameraService::updateStatus(StatusInternal status, const String8& cameraId, Mutex::Autolock lock(mStatusListenerLock); for (auto& listener : mListenerList) { - if (!listener.first && - mCameraProviderManager->isPublicallyHiddenSecureCamera(cameraId.c_str())) { + if (shouldSkipStatusUpdates(cameraId, listener->isVendorListener(), + listener->getListenerPid(), listener->getListenerUid())) { ALOGV("Skipping camera discovery callback for system-only camera %s", - cameraId.c_str()); + cameraId.c_str()); continue; } - listener.second->getListener()->onStatusChanged(mapToInterface(status), + listener->getListener()->onStatusChanged(mapToInterface(status), String16(cameraId)); } }); diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index cf93a4147c..67829dd5fc 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -633,9 +633,20 @@ private: sp* client, std::shared_ptr>>* partial); - // Should an operation attempt on a cameraId be rejected, if the camera id is - // advertised as a publically hidden secure camera, by the camera HAL ? - bool shouldRejectHiddenCameraConnection(const String8 & cameraId); + // Should an operation attempt on a cameraId be rejected ? (this can happen + // under various conditions. For example if a camera device is advertised as + // system only or hidden secure camera, amongst possible others. + bool shouldRejectSystemCameraConnection(const String8 & cameraId) const; + + // Should a device status update be skipped for a particular camera device ? (this can happen + // under various conditions. For example if a camera device is advertised as + // system only or hidden secure camera, amongst possible others. + bool shouldSkipStatusUpdates(const String8& cameraId, bool isVendorListener, int clientPid, + int clientUid) const; + + inline SystemCameraKind getSystemCameraKind(const String8& cameraId) const { + return mCameraProviderManager->getSystemCameraKind(cameraId.c_str()); + } // Single implementation shared between the various connect calls template @@ -810,7 +821,9 @@ private: class ServiceListener : public virtual IBinder::DeathRecipient { public: ServiceListener(sp parent, sp listener, - int uid) : mParent(parent), mListener(listener), mListenerUid(uid) {} + int uid, int pid, bool isVendorClient) + : mParent(parent), mListener(listener), mListenerUid(uid), mListenerPid(pid), + mIsVendorListener(isVendorClient) { } status_t initialize() { return IInterface::asBinder(mListener)->linkToDeath(this); @@ -824,16 +837,20 @@ private: } int getListenerUid() { return mListenerUid; } + int getListenerPid() { return mListenerPid; } sp getListener() { return mListener; } + bool isVendorListener() { return mIsVendorListener; } private: wp mParent; sp mListener; - int mListenerUid; + int mListenerUid = -1; + int mListenerPid = -1; + bool mIsVendorListener = false; }; // Guarded by mStatusListenerMutex - std::vector>> mListenerList; + std::vector> mListenerList; Mutex mStatusListenerLock; diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index c72029ff5a..c21bd69b3e 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -534,15 +534,23 @@ void CameraProviderManager::ProviderInfo::DeviceInfo3::queryPhysicalCameraIds() } } -bool CameraProviderManager::ProviderInfo::DeviceInfo3::isPublicallyHiddenSecureCamera() { +SystemCameraKind CameraProviderManager::ProviderInfo::DeviceInfo3::getSystemCameraKind() { camera_metadata_entry_t entryCap; entryCap = mCameraCharacteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES); - if (entryCap.count != 1) { - // Do NOT hide this camera device if the capabilities specify anything more - // than ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA. - return false; + if (entryCap.count == 1 && + entryCap.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA) { + return SystemCameraKind::HIDDEN_SECURE_CAMERA; } - return entryCap.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA; + + // Go through the capabilities and check if it has + // ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA + for (size_t i = 0; i < entryCap.count; ++i) { + uint8_t capability = entryCap.data.u8[i]; + if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA) { + return SystemCameraKind::SYSTEM_ONLY_CAMERA; + } + } + return SystemCameraKind::PUBLIC; } void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedSizes( @@ -1046,14 +1054,14 @@ bool CameraProviderManager::isLogicalCamera(const std::string& id, return deviceInfo->mIsLogicalCamera; } -bool CameraProviderManager::isPublicallyHiddenSecureCamera(const std::string& id) { +SystemCameraKind CameraProviderManager::getSystemCameraKind(const std::string& id) { std::lock_guard lock(mInterfaceMutex); auto deviceInfo = findDeviceInfoLocked(id); if (deviceInfo == nullptr) { - return false; + return SystemCameraKind::PUBLIC; } - return deviceInfo->mIsPublicallyHiddenSecureCamera; + return deviceInfo->mSystemCameraKind; } bool CameraProviderManager::isHiddenPhysicalCamera(const std::string& cameraId) { @@ -1937,7 +1945,7 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& return; } - mIsPublicallyHiddenSecureCamera = isPublicallyHiddenSecureCamera(); + mSystemCameraKind = getSystemCameraKind(); status_t res = fixupMonochromeTags(); if (OK != res) { diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index 8cdfc24a9c..801e978fe5 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -54,6 +54,26 @@ public: sp& descriptor); }; +enum SystemCameraKind { + /** + * These camera devices are visible to all apps and system components alike + */ + PUBLIC = 0, + + /** + * These camera devices are visible only to processes having the + * android.permission.SYSTEM_CAMERA permission. They are not exposed to 3P + * apps. + */ + SYSTEM_ONLY_CAMERA, + + /** + * These camera devices are visible only to HAL clients (that try to connect + * on a hwbinder thread). + */ + HIDDEN_SECURE_CAMERA +}; + /** * A manager for all camera providers available on an Android device. * @@ -272,7 +292,7 @@ public: */ bool isLogicalCamera(const std::string& id, std::vector* physicalCameraIds); - bool isPublicallyHiddenSecureCamera(const std::string& id); + SystemCameraKind getSystemCameraKind(const std::string& id); bool isHiddenPhysicalCamera(const std::string& cameraId); static const float kDepthARTolerance; @@ -379,7 +399,7 @@ private: std::vector mPhysicalIds; hardware::CameraInfo mInfo; sp mSavedInterface; - bool mIsPublicallyHiddenSecureCamera = false; + SystemCameraKind mSystemCameraKind = SystemCameraKind::PUBLIC; const hardware::camera::common::V1_0::CameraResourceCost mResourceCost; @@ -497,7 +517,7 @@ private: CameraMetadata mCameraCharacteristics; std::unordered_map mPhysicalCameraCharacteristics; void queryPhysicalCameraIds(); - bool isPublicallyHiddenSecureCamera(); + SystemCameraKind getSystemCameraKind(); status_t fixupMonochromeTags(); status_t addDynamicDepthTags(); static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag,