diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl index 28252c0d3e..4db7f8573a 100644 --- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl +++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl @@ -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(); diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 6abfa81e73..5fc1fa331b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -33,14 +33,18 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include #include +#include #include #include #include @@ -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 args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(data.readString16()); + } + sp unusedCallback; + sp 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 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, active)); + } + isActive = isUidActiveLocked(uid); + } + if (wasActive != isActive && !isActive) { + sp 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& 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& 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& 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& 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 gets the uid state\n" + " set-uid-state overrides the uid state\n" + " reset-uid-state clears the uid state override\n" + " help print this message\n"); +} + }; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index e9373a6489..575cebf8ef 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,8 @@ #include #include #include +#include +#include namespace android { @@ -163,6 +166,8 @@ public: virtual status_t dump(int fd, const Vector& args); + virtual status_t shellCommand(int in, int out, int err, const Vector& 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, const sp& 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 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 mService; + std::unordered_set mActiveUids; + std::unordered_map mOverrideUids; + }; // class UidPolicy + + sp 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& args, int err); + + // Clears the override for the UID state + status_t handleResetUidState(const Vector& args, int err); + + // Gets the UID state + status_t handleGetUidState(const Vector& args, int out, int err); + + // Prints the shell command help + status_t printHelp(int out); + static int getCallingPid(); static int getCallingUid();