Implement camera privacy allowlist.
This feature enables the user to access the camera for certain
driver assistance applications regardless of the device-wide global
camera privacy toggle in order to preserve user safety and meet
safety regulations.
Flag: ACONFIG com.android.internal.camera.flags.privacy_allowlist DEVELOPMENT
Bug: 300986585
Test: Build and test using kitchensink.
Change-Id: I04713c8146be2de3ac4531cb205bbe217f31b2f4
diff --git a/camera/camera_platform.aconfig b/camera/camera_platform.aconfig
index 2266483..70d6f1b 100644
--- a/camera/camera_platform.aconfig
+++ b/camera/camera_platform.aconfig
@@ -97,3 +97,10 @@
description: "Enable returning graphics buffers to buffer queues without holding the in-flight mutex"
bug: "315526878"
}
+
+flag {
+ namespace: "camera_platform"
+ name: "privacy_allowlist"
+ description: "Allowlisting to exempt safety-relevant cameras from privacy control for automotive devices"
+ bug: "282814430"
+}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 9a8a056..ac78372 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -540,6 +540,10 @@
binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
bool enabled);
+ binder::Status onSensorPrivacyStateChanged(int, int, int) {
+ return binder::Status::ok();
+ }
+
private:
wp<AudioPolicyService> mService;
std::atomic_bool mSensorPrivacyEnabled = false;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3f43af5..3874915 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -135,6 +135,8 @@
static const std::string sSystemCameraPermission("android.permission.SYSTEM_CAMERA");
static const std::string sCameraHeadlessSystemUserPermission(
"android.permission.CAMERA_HEADLESS_SYSTEM_USER");
+static const std::string sCameraPrivacyAllowlistPermission(
+ "android.permission.CAMERA_PRIVACY_ALLOWLIST");
static const std::string
sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
static const std::string sCameraOpenCloseListenerPermission(
@@ -820,6 +822,14 @@
std::string(), AppOpsManager::OP_NONE);
}
+bool CameraService::hasPermissionsForCameraPrivacyAllowlist(int callingPid, int callingUid) const{
+ AttributionSourceState attributionSource{};
+ attributionSource.pid = callingPid;
+ attributionSource.uid = callingUid;
+ return checkPermission(std::string(), sCameraPrivacyAllowlistPermission, attributionSource,
+ std::string(), AppOpsManager::OP_NONE);
+}
+
Status CameraService::getNumberOfCameras(int32_t type, int32_t* numCameras) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
@@ -2345,6 +2355,39 @@
return ret;
}
+bool CameraService::isCameraPrivacyEnabled(const String16& packageName, const std::string& cam_id,
+ int callingPid, int callingUid) {
+ if (!isAutomotiveDevice()) {
+ return mSensorPrivacyPolicy->isCameraPrivacyEnabled();
+ }
+
+ // Automotive privileged client AID_AUTOMOTIVE_EVS using exterior system camera for
+ // safety-critical use cases cannot be disabled and are exempt from camera privacy policy.
+ if ((isAutomotivePrivilegedClient(callingUid) && isAutomotiveExteriorSystemCamera(cam_id))) {
+ ALOGI("Camera privacy cannot be enabled for automotive privileged client %d "
+ "using camera %s", callingUid, cam_id.c_str());
+ return false;
+ }
+
+ if (mSensorPrivacyPolicy->isCameraPrivacyEnabled(packageName)) {
+ return true;
+ } else if (mSensorPrivacyPolicy->getCameraPrivacyState() == SensorPrivacyManager::DISABLED) {
+ return false;
+ } else if ((mSensorPrivacyPolicy->getCameraPrivacyState()
+ == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS) ||
+ (mSensorPrivacyPolicy->getCameraPrivacyState()
+ == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS) ||
+ (mSensorPrivacyPolicy->getCameraPrivacyState() ==
+ SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_APPS)) {
+ if (hasPermissionsForCameraPrivacyAllowlist(callingPid, callingUid)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return false;
+}
+
std::string CameraService::getPackageNameFromUid(int clientUid) {
std::string packageName("");
@@ -2591,38 +2634,39 @@
clientPackageName));
}
- // Automotive privileged client AID_AUTOMOTIVE_EVS using exterior system camera for use
- // cases such as rear view and surround view cannot be disabled and are exempt from camera
- // privacy policy.
- if ((!isAutomotivePrivilegedClient(packageUid) ||
- !isAutomotiveExteriorSystemCamera(cameraId))) {
+ bool isCameraPrivacyEnabled;
+ if (flags::privacy_allowlist()) {
// Set camera muting behavior.
- bool isCameraPrivacyEnabled =
+ isCameraPrivacyEnabled = this->isCameraPrivacyEnabled(
+ toString16(client->getPackageName()), cameraId, packagePid, packageUid);
+ } else {
+ isCameraPrivacyEnabled =
mSensorPrivacyPolicy->isCameraPrivacyEnabled();
- if (client->supportsCameraMute()) {
- client->setCameraMute(
- mOverrideCameraMuteMode || isCameraPrivacyEnabled);
- } else if (isCameraPrivacyEnabled) {
- // no camera mute supported, but privacy is on! => disconnect
- ALOGI("Camera mute not supported for package: %s, camera id: %s",
- client->getPackageName().c_str(), cameraId.c_str());
- // 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 = CameraThreadState::clearCallingIdentity();
- // Note AppOp to trigger the "Unblock" dialog
- client->noteAppOp();
- client->disconnect();
- CameraThreadState::restoreCallingIdentity(token);
- // Reacquire mServiceLock
- mServiceLock.lock();
+ }
- return STATUS_ERROR_FMT(ERROR_DISABLED,
- "Camera \"%s\" disabled due to camera mute", cameraId.c_str());
- }
+ if (client->supportsCameraMute()) {
+ client->setCameraMute(
+ mOverrideCameraMuteMode || isCameraPrivacyEnabled);
+ } else if (isCameraPrivacyEnabled) {
+ // no camera mute supported, but privacy is on! => disconnect
+ ALOGI("Camera mute not supported for package: %s, camera id: %s",
+ client->getPackageName().c_str(), cameraId.c_str());
+ // 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 = CameraThreadState::clearCallingIdentity();
+ // Note AppOp to trigger the "Unblock" dialog
+ client->noteAppOp();
+ client->disconnect();
+ CameraThreadState::restoreCallingIdentity(token);
+ // Reacquire mServiceLock
+ mServiceLock.lock();
+
+ return STATUS_ERROR_FMT(ERROR_DISABLED,
+ "Camera \"%s\" disabled due to camera mute", cameraId.c_str());
}
if (shimUpdateOnly) {
@@ -4143,8 +4187,15 @@
// return MODE_IGNORED. Do not treat such case as error.
bool isUidActive = sCameraService->mUidPolicy->isUidActive(mClientUid,
mClientPackageName);
- bool isCameraPrivacyEnabled =
+
+ bool isCameraPrivacyEnabled;
+ if (flags::privacy_allowlist()) {
+ isCameraPrivacyEnabled = sCameraService->isCameraPrivacyEnabled(
+ toString16(mClientPackageName), std::string(), mClientPid, mClientUid);
+ } else {
+ isCameraPrivacyEnabled =
sCameraService->mSensorPrivacyPolicy->isCameraPrivacyEnabled();
+ }
// We don't want to return EACCESS if the CameraPrivacy is enabled.
// We prefer to successfully open the camera and perform camera muting
// or blocking in connectHelper as handleAppOpMode can be called before the
@@ -4331,11 +4382,19 @@
block();
} else if (res == AppOpsManager::MODE_IGNORED) {
bool isUidActive = sCameraService->mUidPolicy->isUidActive(mClientUid, mClientPackageName);
- bool isCameraPrivacyEnabled =
+
+ bool isCameraPrivacyEnabled;
+ if (flags::privacy_allowlist()) {
+ isCameraPrivacyEnabled = sCameraService->isCameraPrivacyEnabled(
+ toString16(mClientPackageName),std::string(),mClientPid,mClientUid);
+ } else {
+ isCameraPrivacyEnabled =
sCameraService->mSensorPrivacyPolicy->isCameraPrivacyEnabled();
- ALOGI("Camera %s: Access for \"%s\" has been restricted, isUidTrusted %d, isUidActive %d",
- mCameraIdStr.c_str(), mClientPackageName.c_str(),
- mUidIsTrusted, isUidActive);
+ }
+
+ ALOGI("Camera %s: Access for \"%s\" has been restricted, isUidTrusted %d, isUidActive %d"
+ " isCameraPrivacyEnabled %d", mCameraIdStr.c_str(), mClientPackageName.c_str(),
+ mUidIsTrusted, isUidActive, isCameraPrivacyEnabled);
// If the calling Uid is trusted (a native service), or the client Uid is active (WAR for
// b/175320666), the AppOpsManager could return MODE_IGNORED. Do not treat such cases as
// error.
@@ -4715,7 +4774,15 @@
}
hasCameraPrivacyFeature(); // Called so the result is cached
mSpm.addSensorPrivacyListener(this);
+ if (isAutomotiveDevice()) {
+ mSpm.addToggleSensorPrivacyListener(this);
+ }
mSensorPrivacyEnabled = mSpm.isSensorPrivacyEnabled();
+ if (flags::privacy_allowlist()) {
+ mCameraPrivacyState = mSpm.getToggleSensorPrivacyState(
+ SensorPrivacyManager::TOGGLE_TYPE_SOFTWARE,
+ SensorPrivacyManager::TOGGLE_SENSOR_CAMERA);
+ }
status_t res = mSpm.linkToDeath(this);
if (res == OK) {
mRegistered = true;
@@ -4747,6 +4814,9 @@
void CameraService::SensorPrivacyPolicy::unregisterSelf() {
Mutex::Autolock _l(mSensorPrivacyLock);
mSpm.removeSensorPrivacyListener(this);
+ if (isAutomotiveDevice()) {
+ mSpm.removeToggleSensorPrivacyListener(this);
+ }
mSpm.unlinkToDeath(this);
mRegistered = false;
ALOGV("SensorPrivacyPolicy: Unregistered with SensorPrivacyManager");
@@ -4761,6 +4831,15 @@
return mSensorPrivacyEnabled;
}
+int CameraService::SensorPrivacyPolicy::getCameraPrivacyState() {
+ if (!mRegistered) {
+ registerWithSensorPrivacyManager();
+ }
+
+ Mutex::Autolock _l(mSensorPrivacyLock);
+ return mCameraPrivacyState;
+}
+
bool CameraService::SensorPrivacyPolicy::isCameraPrivacyEnabled() {
if (!hasCameraPrivacyFeature()) {
return false;
@@ -4768,18 +4847,52 @@
return mSpm.isToggleSensorPrivacyEnabled(SensorPrivacyManager::TOGGLE_SENSOR_CAMERA);
}
+bool CameraService::SensorPrivacyPolicy::isCameraPrivacyEnabled(const String16& packageName) {
+ if (!hasCameraPrivacyFeature()) {
+ return SensorPrivacyManager::DISABLED;
+ }
+ return mSpm.isCameraPrivacyEnabled(packageName);
+}
+
binder::Status CameraService::SensorPrivacyPolicy::onSensorPrivacyChanged(
- int toggleType __unused, int sensor __unused, bool enabled) {
+ int toggleType, int sensor, bool enabled) {
+ if ((toggleType == SensorPrivacyManager::TOGGLE_TYPE_UNKNOWN)
+ && (sensor == SensorPrivacyManager::TOGGLE_SENSOR_UNKNOWN)) {
+ {
+ Mutex::Autolock _l(mSensorPrivacyLock);
+ mSensorPrivacyEnabled = enabled;
+ }
+ // if sensor privacy is enabled then block all clients from accessing the camera
+ if (enabled) {
+ sp<CameraService> service = mService.promote();
+ if (service != nullptr) {
+ service->blockAllClients();
+ }
+ }
+ }
+ return binder::Status::ok();
+}
+
+binder::Status CameraService::SensorPrivacyPolicy::onSensorPrivacyStateChanged(
+ int, int sensor, int state) {
+ if (!flags::privacy_allowlist() || (sensor != SensorPrivacyManager::TOGGLE_SENSOR_CAMERA)) {
+ return binder::Status::ok();
+ }
{
Mutex::Autolock _l(mSensorPrivacyLock);
- mSensorPrivacyEnabled = enabled;
+ mCameraPrivacyState = state;
+ }
+ sp<CameraService> service = mService.promote();
+ if (!service) {
+ return binder::Status::ok();
}
// if sensor privacy is enabled then block all clients from accessing the camera
- if (enabled) {
- sp<CameraService> service = mService.promote();
- if (service != nullptr) {
- service->blockAllClients();
- }
+ if (state == SensorPrivacyManager::ENABLED) {
+ service->blockAllClients();
+ } else if ((state == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_APPS)
+ || (state == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS)
+ || (state == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS)) {
+ service->blockPrivacyEnabledClients();
}
return binder::Status::ok();
}
@@ -5651,6 +5764,23 @@
}
}
+void CameraService::blockPrivacyEnabledClients() {
+ const auto clients = mActiveClientManager.getAll();
+ for (auto& current : clients) {
+ if (current != nullptr) {
+ const auto basicClient = current->getValue();
+ if (basicClient.get() != nullptr) {
+ std::string pkgName = basicClient->getPackageName();
+ bool cameraPrivacyEnabled =
+ mSensorPrivacyPolicy->isCameraPrivacyEnabled(toString16(pkgName));
+ if (cameraPrivacyEnabled) {
+ basicClient->block();
+ }
+ }
+ }
+ }
+}
+
// NOTE: This is a remote API - make sure all args are validated
status_t CameraService::shellCommand(int in, int out, int err, const Vector<String16>& args) {
if (!checkCallingPermission(toString16(sManageCameraPermission), nullptr, nullptr)) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1487013..197926c 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -681,6 +681,9 @@
int callingUid) const;
bool hasCameraPermissions() const;
+
+ bool hasPermissionsForCameraPrivacyAllowlist(int callingPid, int callingUid) const;
+
/**
* Typesafe version of device status, containing both the HAL-layer and the service interface-
* layer values.
@@ -868,16 +871,20 @@
public virtual IServiceManager::LocalRegistrationCallback {
public:
explicit SensorPrivacyPolicy(wp<CameraService> service)
- : mService(service), mSensorPrivacyEnabled(false), mRegistered(false) {}
+ : mService(service), mSensorPrivacyEnabled(false),
+ mCameraPrivacyState(SensorPrivacyManager::DISABLED), mRegistered(false) {}
void registerSelf();
void unregisterSelf();
bool isSensorPrivacyEnabled();
bool isCameraPrivacyEnabled();
+ int getCameraPrivacyState();
+ bool isCameraPrivacyEnabled(const String16& packageName);
binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
bool enabled);
+ binder::Status onSensorPrivacyStateChanged(int toggleType, int sensor, int state);
// Implementation of IServiceManager::LocalRegistrationCallback
virtual void onServiceRegistration(const String16& name,
@@ -890,6 +897,7 @@
wp<CameraService> mService;
Mutex mSensorPrivacyLock;
bool mSensorPrivacyEnabled;
+ int mCameraPrivacyState;
bool mRegistered;
bool hasCameraPrivacyFeature();
@@ -926,6 +934,9 @@
const std::string& clientName, /*inout*/int& clientUid, /*inout*/int& clientPid,
/*out*/int& originalClientPid) const;
+ bool isCameraPrivacyEnabled(const String16& packageName,const std::string& cameraId,
+ int clientPid, int ClientUid);
+
// Handle active client evictions, and update service state.
// Only call with with mServiceLock held.
status_t handleEvictionsLocked(const std::string& cameraId, int clientPid,
@@ -1385,6 +1396,9 @@
// Blocks all active clients.
void blockAllClients();
+ // Blocks clients whose privacy is enabled.
+ void blockPrivacyEnabledClients();
+
// Overrides the UID state as if it is idle
status_t handleSetUidState(const Vector<String16>& args, int err);