Don't record audio if UID is idle - media framework
To protect user's privacy if a UID is in an idle state we allow
recording but report silence (all zeros in the byte array) and once
the process goes in an active state we report the real mic data.
This avoids the race between the app being notified aboout its
lifecycle and the audio system being notified about the state
of a UID.
Test: Added - AudioRecordTest#testRecordNoDataForIdleUids
Passing - cts-tradefed run cts-dev -m CtsMediaTestCases
-t android.media.cts.AudioRecordTest
bug:63938985
Change-Id: I1442a9dda1553e9ea7a4a654c50555ac1ec06aa0
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index af0c823..e5aed9a 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -28,6 +28,9 @@
#include <utils/Log.h>
#include <cutils/properties.h>
#include <binder/IPCThreadState.h>
+#include <binder/ActivityManager.h>
+#include <binder/PermissionController.h>
+#include <binder/IResultReceiver.h>
#include <utils/String16.h>
#include <utils/threads.h>
#include "AudioPolicyService.h"
@@ -39,6 +42,8 @@
#include <system/audio.h>
#include <system/audio_policy.h>
+#include <private/android_filesystem_config.h>
+
namespace android {
static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n";
@@ -49,6 +54,7 @@
static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds
+static const String16 sManageAudioPolicyPermission("android.permission.MANAGE_AUDIO_POLICY");
// ----------------------------------------------------------------------------
@@ -79,6 +85,9 @@
Mutex::Autolock _l(mLock);
mAudioPolicyEffects = audioPolicyEffects;
}
+
+ mUidPolicy = new UidPolicy(this);
+ mUidPolicy->registerSelf();
}
AudioPolicyService::~AudioPolicyService()
@@ -92,6 +101,9 @@
mNotificationClients.clear();
mAudioPolicyEffects.clear();
+
+ mUidPolicy->unregisterSelf();
+ mUidPolicy.clear();
}
// A notification client is always registered by AudioSystem when the client process
@@ -318,6 +330,20 @@
return NO_ERROR;
}
+void AudioPolicyService::setRecordSilenced(uid_t uid, bool silenced)
+{
+ {
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager) {
+ mAudioPolicyManager->setRecordSilenced(uid, silenced);
+ }
+ }
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af) {
+ af->setRecordSilenced(uid, silenced);
+ }
+}
+
status_t AudioPolicyService::dump(int fd, const Vector<String16>& args __unused)
{
if (!dumpAllowed()) {
@@ -361,11 +387,210 @@
}
status_t AudioPolicyService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ 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;
+ }
+ }
+
return BnAudioPolicyService::onTransact(code, data, reply, flags);
}
+// ------------------- Shell command implementation -------------------
+
+// NOTE: This is a remote API - make sure all args are validated
+status_t AudioPolicyService::shellCommand(int in, int out, int err, Vector<String16>& args) {
+ if (!checkCallingPermission(sManageAudioPolicyPermission, 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 AudioPolicyService::handleSetUidState(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 AudioPolicyService::handleResetUidState(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 AudioPolicyService::handleGetUidState(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 AudioPolicyService::printHelp(int out) {
+ return dprintf(out, "Audio policy 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");
+}
+
+// ----------- AudioPolicyService::UidPolicy implementation ----------
+
+void AudioPolicyService::UidPolicy::registerSelf() {
+ ActivityManager am;
+ am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+ | ActivityManager::UID_OBSERVER_IDLE
+ | ActivityManager::UID_OBSERVER_ACTIVE,
+ ActivityManager::PROCESS_STATE_UNKNOWN,
+ String16("audioserver"));
+}
+
+void AudioPolicyService::UidPolicy::unregisterSelf() {
+ ActivityManager am;
+ am.unregisterUidObserver(this);
+}
+
+void AudioPolicyService::UidPolicy::onUidGone(uid_t uid, __unused bool disabled) {
+ onUidIdle(uid, disabled);
+}
+
+void AudioPolicyService::UidPolicy::onUidActive(uid_t uid) {
+ {
+ Mutex::Autolock _l(mUidLock);
+ mActiveUids.insert(uid);
+ }
+ sp<AudioPolicyService> service = mService.promote();
+ if (service != nullptr) {
+ service->setRecordSilenced(uid, false);
+ }
+}
+
+void AudioPolicyService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) {
+ bool deleted = false;
+ {
+ Mutex::Autolock _l(mUidLock);
+ if (mActiveUids.erase(uid) > 0) {
+ deleted = true;
+ }
+ }
+ if (deleted) {
+ sp<AudioPolicyService> service = mService.promote();
+ if (service != nullptr) {
+ service->setRecordSilenced(uid, true);
+ }
+ }
+}
+
+void AudioPolicyService::UidPolicy::addOverrideUid(uid_t uid, bool active) {
+ updateOverrideUid(uid, active, true);
+}
+
+void AudioPolicyService::UidPolicy::removeOverrideUid(uid_t uid) {
+ updateOverrideUid(uid, false, false);
+}
+
+void AudioPolicyService::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) {
+ sp<AudioPolicyService> service = mService.promote();
+ if (service != nullptr) {
+ service->setRecordSilenced(uid, !isActive);
+ }
+ }
+}
+
+bool AudioPolicyService::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 AudioPolicyService::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();
+}
// ----------- AudioPolicyService::AudioCommandThread implementation ----------