No camera for idle uids - av framework

If a UID is idle (being in the background for more than
cartain amount of time) it should not be able to use the
camera. If the UID becomes idle we generate an eror and
close the cameras for this UID. If an app in an idle UID
tries to use the camera we immediately generate an error.
Since apps already should handle these errors it is safe
to apply this policy to all apps to protect user privacy.

Test: Pass - cts-tradefed run cts -m CtsCameraTestCases
      Added - CameraTest#testCameraAccessForIdleUid

Bug: 63938985

Change-Id: Ic0111d7c651b3d84c644b9f3d30e24133544f4fa
gugelfrei
Svet Ganov 7 years ago
parent d6be64a3f2
commit a453d0d278

@ -30,6 +30,7 @@ interface ICameraDeviceCallbacks
const int ERROR_CAMERA_REQUEST = 3;
const int ERROR_CAMERA_RESULT = 4;
const int ERROR_CAMERA_BUFFER = 5;
const int ERROR_CAMERA_DISABLED = 6;
oneway void onDeviceError(int errorCode, in CaptureResultExtras resultExtras);
oneway void onDeviceIdle();

@ -33,14 +33,18 @@
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <binder/ActivityManager.h>
#include <binder/AppOpsManager.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <binder/PermissionController.h>
#include <binder/ProcessInfoService.h>
#include <binder/IResultReceiver.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <cutils/misc.h>
#include <gui/Surface.h>
#include <hardware/hardware.h>
#include <memunreachable/memunreachable.h>
@ -165,6 +169,8 @@ static void torch_mode_status_change(
// ----------------------------------------------------------------------------
static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
CameraService::CameraService() :
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mNumberOfCameras(0), mNumberOfNormalCameras(0),
@ -196,6 +202,9 @@ void CameraService::onFirstRef()
}
CameraService::pingCameraServiceProxy();
mUidPolicy = new UidPolicy(this);
mUidPolicy->registerSelf();
}
status_t CameraService::enumerateProviders() {
@ -275,6 +284,7 @@ void CameraService::pingCameraServiceProxy() {
CameraService::~CameraService() {
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
mUidPolicy->unregisterSelf();
}
void CameraService::onNewProviderRegistered() {
@ -933,6 +943,15 @@ Status CameraService::validateClientPermissionsLocked(const String8& cameraId,
clientName8.string(), clientUid, clientPid, cameraId.string());
}
// Make sure the UID is in an active state to use the camera
if (!mUidPolicy->isUidActive(callingUid)) {
ALOGE("Access Denial: can't use the camera from an idle UID pid=%d, uid=%d",
clientPid, clientUid);
return STATUS_ERROR_FMT(ERROR_DISABLED,
"Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" from background",
clientName8.string(), clientUid, clientPid, cameraId.string());
}
// Only use passed in clientPid to check permission. Use calling PID as the client PID that's
// connected to camera service directly.
originalClientPid = clientPid;
@ -1969,6 +1988,30 @@ status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* re
// Permission checks
switch (code) {
case SHELL_COMMAND_TRANSACTION: {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
int argc = data.readInt32();
Vector<String16> args;
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(data.readString16());
}
sp<IBinder> unusedCallback;
sp<IResultReceiver> resultReceiver;
status_t status;
if ((status = data.readNullableStrongBinder(&unusedCallback)) != NO_ERROR) {
return status;
}
if ((status = data.readNullableStrongBinder(&resultReceiver)) != NO_ERROR) {
return status;
}
status = shellCommand(in, out, err, args);
if (resultReceiver != nullptr) {
resultReceiver->send(status);
}
return NO_ERROR;
}
case BnCameraService::NOTIFYSYSTEMEVENT: {
if (pid != selfPid) {
// Ensure we're being called by system_server, or similar process with
@ -2286,15 +2329,21 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa
if (res != AppOpsManager::MODE_ALLOWED) {
ALOGI("Camera %s: Access for \"%s\" revoked", mCameraIdStr.string(),
myName.string());
// Reset the client PID to allow server-initiated disconnect,
// and to prevent further calls by client.
mClientPid = getCallingPid();
CaptureResultExtras resultExtras; // a dummy result (invalid)
notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE, resultExtras);
disconnect();
block();
}
}
void CameraService::BasicClient::block() {
ATRACE_CALL();
// Reset the client PID to allow server-initiated disconnect,
// and to prevent further calls by client.
mClientPid = getCallingPid();
CaptureResultExtras resultExtras; // a dummy result (invalid)
notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISABLED, resultExtras);
disconnect();
}
// ----------------------------------------------------------------------------
void CameraService::Client::notifyError(int32_t errorCode,
@ -2330,6 +2379,98 @@ void CameraService::Client::OpsCallback::opChanged(int32_t op,
}
}
// ----------------------------------------------------------------------------
// UidPolicy
// ----------------------------------------------------------------------------
void CameraService::UidPolicy::registerSelf() {
ActivityManager am;
am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
| ActivityManager::UID_OBSERVER_IDLE
| ActivityManager::UID_OBSERVER_ACTIVE,
ActivityManager::PROCESS_STATE_UNKNOWN,
String16("cameraserver"));
}
void CameraService::UidPolicy::unregisterSelf() {
ActivityManager am;
am.unregisterUidObserver(this);
}
void CameraService::UidPolicy::onUidGone(uid_t uid, bool disabled) {
onUidIdle(uid, disabled);
}
void CameraService::UidPolicy::onUidActive(uid_t uid) {
Mutex::Autolock _l(mUidLock);
mActiveUids.insert(uid);
}
void CameraService::UidPolicy::onUidIdle(uid_t uid, bool /* disabled */) {
bool deleted = false;
{
Mutex::Autolock _l(mUidLock);
if (mActiveUids.erase(uid) > 0) {
deleted = true;
}
}
if (deleted) {
sp<CameraService> service = mService.promote();
if (service != nullptr) {
service->blockClientsForUid(uid);
}
}
}
bool CameraService::UidPolicy::isUidActive(uid_t uid) {
// Non-app UIDs are considered always active
if (uid < FIRST_APPLICATION_UID) {
return true;
}
Mutex::Autolock _l(mUidLock);
return isUidActiveLocked(uid);
}
bool CameraService::UidPolicy::isUidActiveLocked(uid_t uid) {
// Non-app UIDs are considered always active
if (uid < FIRST_APPLICATION_UID) {
return true;
}
auto it = mOverrideUids.find(uid);
if (it != mOverrideUids.end()) {
return it->second;
}
return mActiveUids.find(uid) != mActiveUids.end();
}
void CameraService::UidPolicy::UidPolicy::addOverrideUid(uid_t uid, bool active) {
updateOverrideUid(uid, active, true);
}
void CameraService::UidPolicy::removeOverrideUid(uid_t uid) {
updateOverrideUid(uid, false, false);
}
void CameraService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) {
bool wasActive = false;
bool isActive = false;
{
Mutex::Autolock _l(mUidLock);
wasActive = isUidActiveLocked(uid);
mOverrideUids.erase(uid);
if (insert) {
mOverrideUids.insert(std::pair<uid_t, bool>(uid, active));
}
isActive = isUidActiveLocked(uid);
}
if (wasActive != isActive && !isActive) {
sp<CameraService> service = mService.promote();
if (service != nullptr) {
service->blockClientsForUid(uid);
}
}
}
// ----------------------------------------------------------------------------
// CameraState
// ----------------------------------------------------------------------------
@ -2791,4 +2932,92 @@ status_t CameraService::setTorchStatusLocked(const String8& cameraId,
return OK;
}
void CameraService::blockClientsForUid(uid_t uid) {
const auto clients = mActiveClientManager.getAll();
for (auto& current : clients) {
if (current != nullptr) {
const auto basicClient = current->getValue();
if (basicClient.get() != nullptr && basicClient->getClientUid() == uid) {
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(sManageCameraPermission, nullptr, nullptr)) {
return PERMISSION_DENIED;
}
if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
return BAD_VALUE;
}
if (args.size() == 3 && args[0] == String16("set-uid-state")) {
return handleSetUidState(args, err);
} else if (args.size() == 2 && args[0] == String16("reset-uid-state")) {
return handleResetUidState(args, err);
} else if (args.size() == 2 && args[0] == String16("get-uid-state")) {
return handleGetUidState(args, out, err);
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
return NO_ERROR;
}
printHelp(err);
return BAD_VALUE;
}
status_t CameraService::handleSetUidState(const Vector<String16>& args, int err) {
PermissionController pc;
int uid = pc.getPackageUid(args[1], 0);
if (uid <= 0) {
ALOGE("Unknown package: '%s'", String8(args[1]).string());
dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
return BAD_VALUE;
}
bool active = false;
if (args[2] == String16("active")) {
active = true;
} else if ((args[2] != String16("idle"))) {
ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
return BAD_VALUE;
}
mUidPolicy->addOverrideUid(uid, active);
return NO_ERROR;
}
status_t CameraService::handleResetUidState(const Vector<String16>& args, int err) {
PermissionController pc;
int uid = pc.getPackageUid(args[1], 0);
if (uid < 0) {
ALOGE("Unknown package: '%s'", String8(args[1]).string());
dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
return BAD_VALUE;
}
mUidPolicy->removeOverrideUid(uid);
return NO_ERROR;
}
status_t CameraService::handleGetUidState(const Vector<String16>& args, int out, int err) {
PermissionController pc;
int uid = pc.getPackageUid(args[1], 0);
if (uid <= 0) {
ALOGE("Unknown package: '%s'", String8(args[1]).string());
dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
return BAD_VALUE;
}
if (mUidPolicy->isUidActive(uid)) {
return dprintf(out, "active\n");
} else {
return dprintf(out, "idle\n");
}
}
status_t CameraService::printHelp(int out) {
return dprintf(out, "Camera service commands:\n"
" get-uid-state <PACKAGE> gets the uid state\n"
" set-uid-state <PACKAGE> <active|idle> overrides the uid state\n"
" reset-uid-state <PACKAGE> clears the uid state override\n"
" help print this message\n");
}
}; // namespace android

@ -27,6 +27,7 @@
#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IAppOpsCallback.h>
#include <binder/IUidObserver.h>
#include <hardware/camera.h>
#include <android/hardware/camera/common/1.0/types.h>
@ -47,6 +48,8 @@
#include <map>
#include <memory>
#include <utility>
#include <unordered_map>
#include <unordered_set>
namespace android {
@ -163,6 +166,8 @@ public:
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t shellCommand(int in, int out, int err, const Vector<String16>& args);
/////////////////////////////////////////////////////////////////////
// Client functionality
@ -233,6 +238,9 @@ public:
// Check what API level is used for this client. This is used to determine which
// superclass this can be cast to.
virtual bool canCastToApiClient(apiLevel level) const;
// Block the client form using the camera
virtual void block();
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
@ -506,6 +514,37 @@ private:
CameraParameters mShimParams;
}; // class CameraState
// Observer for UID lifecycle enforcing that UIDs in idle
// state cannot use the camera to protect user privacy.
class UidPolicy : public BnUidObserver {
public:
explicit UidPolicy(sp<CameraService> service)
: mService(service) {}
void registerSelf();
void unregisterSelf();
bool isUidActive(uid_t uid);
void onUidGone(uid_t uid, bool disabled);
void onUidActive(uid_t uid);
void onUidIdle(uid_t uid, bool disabled);
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
private:
bool isUidActiveLocked(uid_t uid);
void updateOverrideUid(uid_t uid, bool active, bool insert);
Mutex mUidLock;
wp<CameraService> mService;
std::unordered_set<uid_t> mActiveUids;
std::unordered_map<uid_t, bool> mOverrideUids;
}; // class UidPolicy
sp<UidPolicy> mUidPolicy;
// Delay-load the Camera HAL module
virtual void onFirstRef();
@ -755,6 +794,21 @@ private:
*/
binder::Status getLegacyParametersLazy(int cameraId, /*out*/CameraParameters* parameters);
// Blocks all clients from the UID
void blockClientsForUid(uid_t uid);
// Overrides the UID state as if it is idle
status_t handleSetUidState(const Vector<String16>& args, int err);
// Clears the override for the UID state
status_t handleResetUidState(const Vector<String16>& args, int err);
// Gets the UID state
status_t handleGetUidState(const Vector<String16>& args, int out, int err);
// Prints the shell command help
status_t printHelp(int out);
static int getCallingPid();
static int getCallingUid();

Loading…
Cancel
Save