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/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 50fe385..16150f6 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -39,8 +39,7 @@
sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL;
-record_config_callback AudioSystem::gRecordConfigCallback = NULL;
-
+record_config_callback AudioSystem::gRecordConfigCallback = NULL;
// establish binder interface to AudioFlinger service
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
@@ -917,11 +916,14 @@
}
status_t AudioSystem::startInput(audio_io_handle_t input,
- audio_session_t session)
+ audio_session_t session,
+ audio_devices_t device,
+ uid_t uid,
+ bool *silenced)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->startInput(input, session);
+ return aps->startInput(input, session, device, uid, silenced);
}
status_t AudioSystem::stopInput(audio_io_handle_t input,
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 56ddd4f..6507a5c 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -48,6 +48,7 @@
SET_MODE,
SET_MIC_MUTE,
GET_MIC_MUTE,
+ SET_RECORD_SILENCED,
SET_PARAMETERS,
GET_PARAMETERS,
REGISTER_CLIENT,
@@ -306,6 +307,15 @@
return reply.readInt32();
}
+ virtual void setRecordSilenced(uid_t uid, bool silenced)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(uid);
+ data.writeInt32(silenced ? 1 : 0);
+ remote()->transact(SET_RECORD_SILENCED, data, &reply);
+ }
+
virtual status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
{
Parcel data, reply;
@@ -859,6 +869,7 @@
case RELEASE_AUDIO_PATCH:
case LIST_AUDIO_PATCHES:
case SET_AUDIO_PORT_CONFIG:
+ case SET_RECORD_SILENCED:
ALOGW("%s: transaction %d received from PID %d",
__func__, code, IPCThreadState::self()->getCallingPid());
return INVALID_OPERATION;
@@ -1024,6 +1035,15 @@
reply->writeInt32( getMicMute() );
return NO_ERROR;
} break;
+ case SET_RECORD_SILENCED: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ uid_t uid = data.readInt32();
+ audio_source_t source;
+ data.read(&source, sizeof(audio_source_t));
+ bool silenced = data.readInt32() == 1;
+ setRecordSilenced(uid, silenced);
+ return NO_ERROR;
+ } break;
case SET_PARAMETERS: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 53bc1b7..c0e53b3 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -330,14 +330,22 @@
}
virtual status_t startInput(audio_io_handle_t input,
- audio_session_t session)
+ audio_session_t session,
+ audio_devices_t device,
+ uid_t uid,
+ bool *silenced)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeInt32(input);
data.writeInt32(session);
+ data.writeInt32(device);
+ data.writeInt32(uid);
+ data.writeInt32(*silenced ? 1 : 0);
remote()->transact(START_INPUT, data, &reply);
- return static_cast <status_t> (reply.readInt32());
+ status_t status = static_cast <status_t> (reply.readInt32());
+ *silenced = reply.readInt32() == 1;
+ return status;
}
virtual status_t stopInput(audio_io_handle_t input,
@@ -1045,7 +1053,12 @@
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
audio_session_t session = static_cast <audio_session_t>(data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(startInput(input, session)));
+ audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
+ uid_t uid = static_cast <uid_t>(data.readInt32());
+ bool silenced = data.readInt32() == 1;
+ status_t status = startInput(input, session, device, uid, &silenced);
+ reply->writeInt32(static_cast <uint32_t>(status));
+ reply->writeInt32(silenced ? 1 : 0);
return NO_ERROR;
} break;
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 24a6e22..d6cc37a 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -244,7 +244,10 @@
audio_port_handle_t *portId);
static status_t startInput(audio_io_handle_t input,
- audio_session_t session);
+ audio_session_t session,
+ audio_devices_t device,
+ uid_t uid,
+ bool *silenced);
static status_t stopInput(audio_io_handle_t input,
audio_session_t session);
static void releaseInput(audio_io_handle_t input,
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 57d9778..472a3da 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -368,6 +368,7 @@
// mic mute/state
virtual status_t setMicMute(bool state) = 0;
virtual bool getMicMute() const = 0;
+ virtual void setRecordSilenced(uid_t uid, bool silenced) = 0;
virtual status_t setParameters(audio_io_handle_t ioHandle,
const String8& keyValuePairs) = 0;
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 5558b77..7e9413d 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -84,7 +84,10 @@
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
virtual status_t startInput(audio_io_handle_t input,
- audio_session_t session) = 0;
+ audio_session_t session,
+ audio_devices_t device,
+ uid_t uid,
+ bool *silenced) = 0;
virtual status_t stopInput(audio_io_handle_t input,
audio_session_t session) = 0;
virtual void releaseInput(audio_io_handle_t input,
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 4d5e094..bbdd89c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -987,6 +987,21 @@
return mute;
}
+void AudioFlinger::setRecordSilenced(uid_t uid, bool silenced)
+{
+ ALOGV("AudioFlinger::setRecordSilenced(uid:%d, silenced:%d)", uid, silenced);
+
+ // TODO: Notify MmapThreads
+
+ AutoMutex lock(mLock);
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ sp<RecordThread> thread = mRecordThreads.valueAt(i);
+ if (thread != 0) {
+ thread->setRecordSilenced(uid, silenced);
+ }
+ }
+}
+
status_t AudioFlinger::setMasterMute(bool muted)
{
status_t ret = initCheck();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index bc73ffd..1a35066 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -147,6 +147,8 @@
virtual status_t setMicMute(bool state);
virtual bool getMicMute() const;
+ virtual void setRecordSilenced(uid_t uid, bool silenced);
+
virtual status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) const;
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index f8da780..63a3d98 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -63,6 +63,9 @@
virtual bool isFastTrack() const { return (mFlags & AUDIO_INPUT_FLAG_FAST) != 0; }
+ void setSilenced(bool silenced) { mSilenced = silenced; }
+ bool isSilenced() const { return mSilenced; }
+
private:
friend class AudioFlinger; // for mState
@@ -91,6 +94,8 @@
// used by the record thread to convert frames to proper destination format
RecordBufferConverter *mRecordBufferConverter;
audio_input_flags_t mFlags;
+
+ bool mSilenced;
};
// playback track, used by PatchPanel
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d5def48..7308c41 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6528,6 +6528,7 @@
rear = mRsmpInRear += framesRead;
size = activeTracks.size();
+
// loop over each active track
for (size_t i = 0; i < size; i++) {
activeTrack = activeTracks[i];
@@ -6584,6 +6585,11 @@
if (activeTrack->mFramesToDrop == 0) {
if (framesOut > 0) {
activeTrack->mSink.frameCount = framesOut;
+ // Sanitize before releasing if the track has no access to the source data
+ // An idle UID receives silence from non virtual devices until active
+ if (activeTrack->isSilenced()) {
+ memset(activeTrack->mSink.raw, 0, framesOut * mFrameSize);
+ }
activeTrack->releaseBuffer(&activeTrack->mSink);
}
} else {
@@ -6923,7 +6929,9 @@
status_t status = NO_ERROR;
if (recordTrack->isExternalTrack()) {
mLock.unlock();
- status = AudioSystem::startInput(mId, recordTrack->sessionId());
+ bool silenced;
+ status = AudioSystem::startInput(mId, recordTrack->sessionId(),
+ mInDevice, recordTrack->uid(), &silenced);
mLock.lock();
// FIXME should verify that recordTrack is still in mActiveTracks
if (status != NO_ERROR) {
@@ -6932,6 +6940,7 @@
ALOGV("RecordThread::start error %d", status);
return status;
}
+ recordTrack->setSilenced(silenced);
}
// Catch up with current buffer indices if thread is already running.
// This is what makes a new client discard all buffered data. If the track's mRsmpInFront
@@ -7135,6 +7144,16 @@
write(fd, result.string(), result.size());
}
+void AudioFlinger::RecordThread::setRecordSilenced(uid_t uid, bool silenced)
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mTracks.size() ; i++) {
+ sp<RecordTrack> track = mTracks[i];
+ if (track != 0 && track->uid() == uid) {
+ track->setSilenced(silenced);
+ }
+ }
+}
void AudioFlinger::RecordThread::ResamplerBufferProvider::reset()
{
@@ -7827,7 +7846,9 @@
if (isOutput()) {
ret = AudioSystem::startOutput(mId, streamType(), mSessionId);
} else {
- ret = AudioSystem::startInput(mId, mSessionId);
+ // TODO: Block recording for idle UIDs (b/72134552)
+ bool silenced;
+ ret = AudioSystem::startInput(mId, mSessionId, mInDevice, client.clientUid, &silenced);
}
// abort if start is rejected by audio policy manager
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 17f26c5..41d87a4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1396,6 +1396,9 @@
void checkBtNrec();
+ // Sets the UID records silence
+ void setRecordSilenced(uid_t uid, bool silenced);
+
private:
// Enter standby if not already in standby, and set mStandby flag
void standbyIfNotAlreadyInStandby();
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index f2cb25f..2a8f54d 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -65,15 +65,15 @@
API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path
} input_type_t;
- enum {
+ enum {
API_INPUT_CONCURRENCY_NONE = 0,
API_INPUT_CONCURRENCY_CALL = (1 << 0), // Concurrency with a call
API_INPUT_CONCURRENCY_CAPTURE = (1 << 1), // Concurrency with another capture
API_INPUT_CONCURRENCY_ALL = (API_INPUT_CONCURRENCY_CALL | API_INPUT_CONCURRENCY_CAPTURE),
- };
+ };
- typedef uint32_t concurrency_type__mask_t;
+ typedef uint32_t concurrency_type__mask_t;
public:
virtual ~AudioPolicyInterface() {}
@@ -145,6 +145,7 @@
// indicates to the audio policy manager that the input starts being used.
virtual status_t startInput(audio_io_handle_t input,
audio_session_t session,
+ bool silenced,
concurrency_type__mask_t *concurrency) = 0;
// indicates to the audio policy manager that the input stops being used.
virtual status_t stopInput(audio_io_handle_t input,
@@ -239,6 +240,8 @@
virtual float getStreamVolumeDB(
audio_stream_type_t stream, int index, audio_devices_t device) = 0;
+
+ virtual void setRecordSilenced(uid_t uid, bool silenced);
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
index 0d19373..dd5247d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
@@ -55,6 +55,8 @@
void setUid(uid_t uid) { mRecordClientInfo.uid = uid; }
bool matches(const sp<AudioSession> &other) const;
bool isSoundTrigger() const { return mIsSoundTrigger; }
+ void setSilenced(bool silenced) { mSilenced = silenced; }
+ bool isSilenced() const { return mSilenced; }
uint32_t openCount() const { return mOpenCount; } ;
uint32_t activeCount() const { return mActiveCount; } ;
@@ -70,6 +72,7 @@
const struct audio_config_base mConfig;
const audio_input_flags_t mFlags;
bool mIsSoundTrigger;
+ bool mSilenced;
uint32_t mOpenCount;
uint32_t mActiveCount;
AudioMix* mPolicyMix; // non NULL when used by a dynamic policy
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 68730a5..f072adc 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1801,10 +1801,15 @@
status_t AudioPolicyManager::startInput(audio_io_handle_t input,
audio_session_t session,
+ bool silenced,
concurrency_type__mask_t *concurrency)
{
- ALOGV("startInput() input %d", input);
+
+ ALOGV("AudioPolicyManager::startInput(input:%d, session:%d, silenced:%d, concurrency:%d)",
+ input, session, silenced, *concurrency);
+
*concurrency = API_INPUT_CONCURRENCY_NONE;
+
ssize_t index = mInputs.indexOfKey(input);
if (index < 0) {
ALOGW("startInput() unknown input %d", input);
@@ -1839,12 +1844,33 @@
return INVALID_OPERATION;
}
- Vector< sp<AudioInputDescriptor> > activeInputs = mInputs.getActiveInputs();
- for (const auto& activeDesc : activeInputs) {
- if (is_virtual_input_device(activeDesc->mDevice)) {
- continue;
- }
+ Vector<sp<AudioInputDescriptor>> activeInputs = mInputs.getActiveInputs();
+ // If a UID is idle and records silence and another not silenced recording starts
+ // from another UID (idle or active) we stop the current idle UID recording in
+ // favor of the new one - "There can be only one" TM
+ if (!silenced) {
+ for (const auto& activeDesc : activeInputs) {
+ if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+ activeDesc->getId() == inputDesc->getId()) {
+ continue;
+ }
+
+ AudioSessionCollection activeSessions = activeDesc->getAudioSessions(
+ true /*activeOnly*/);
+ sp<AudioSession> activeSession = activeSessions.valueAt(0);
+ if (activeSession->isSilenced()) {
+ audio_io_handle_t activeInput = activeDesc->mIoHandle;
+ audio_session_t activeSessionId = activeSession->session();
+ stopInput(activeInput, activeSessionId);
+ releaseInput(activeInput, activeSessionId);
+ ALOGV("startInput(%d) stopping silenced input %d", input, activeInput);
+ activeInputs = mInputs.getActiveInputs();
+ }
+ }
+ }
+
+ for (const auto& activeDesc : activeInputs) {
if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
activeDesc->getId() == inputDesc->getId()) {
continue;
@@ -1881,10 +1907,6 @@
// if capture is allowed, preempt currently active HOTWORD captures
for (const auto& activeDesc : activeInputs) {
- if (is_virtual_input_device(activeDesc->mDevice)) {
- continue;
- }
-
if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) {
continue;
}
@@ -1907,6 +1929,9 @@
}
#endif
+ // Make sure we start with the correct silence state
+ audioSession->setSilenced(silenced);
+
// increment activity count before calling getNewInputDevice() below as only active sessions
// are considered for device selection
audioSession->changeActiveCount(1);
@@ -2039,7 +2064,6 @@
void AudioPolicyManager::releaseInput(audio_io_handle_t input,
audio_session_t session)
{
-
ALOGV("releaseInput() %d", input);
ssize_t index = mInputs.indexOfKey(input);
if (index < 0) {
@@ -3391,6 +3415,23 @@
return computeVolume(stream, index, device);
}
+void AudioPolicyManager::setRecordSilenced(uid_t uid, bool silenced)
+{
+ ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced);
+
+ Vector<sp<AudioInputDescriptor> > activeInputs = mInputs.getActiveInputs();
+ for (size_t i = 0; i < activeInputs.size(); i++) {
+ sp<AudioInputDescriptor> activeDesc = activeInputs[i];
+ AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true);
+ for (size_t j = 0; j < activeSessions.size(); j++) {
+ sp<AudioSession> activeSession = activeSessions.valueAt(j);
+ if (activeSession->uid() == uid) {
+ activeSession->setSilenced(silenced);
+ }
+ }
+ }
+}
+
status_t AudioPolicyManager::disconnectAudioSource(const sp<AudioSourceDescriptor>& sourceDesc)
{
ALOGV("%s handle %d", __FUNCTION__, sourceDesc->getHandle());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 611edec..4fd73e6 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -135,6 +135,7 @@
// indicates to the audio policy manager that the input starts being used.
virtual status_t startInput(audio_io_handle_t input,
audio_session_t session,
+ bool silenced,
concurrency_type__mask_t *concurrency);
// indicates to the audio policy manager that the input stops being used.
@@ -235,6 +236,8 @@
// return the strategy corresponding to a given stream type
routing_strategy getStrategy(audio_stream_type_t stream) const;
+ virtual void setRecordSilenced(uid_t uid, bool silenced);
+
protected:
// A constructor that allows more fine-grained control over initialization process,
// used in automatic tests.
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 7dd6d70..dc676d0 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -362,14 +362,22 @@
}
status_t AudioPolicyService::startInput(audio_io_handle_t input,
- audio_session_t session)
+ audio_session_t session,
+ audio_devices_t device,
+ uid_t uid,
+ bool *silenced)
{
+ // If UID inactive it records silence until becoming active
+ *silenced = !mUidPolicy->isUidActive(uid) && !is_virtual_input_device(device);
+
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
+
Mutex::Autolock _l(mLock);
- AudioPolicyInterface::concurrency_type__mask_t concurrency;
- status_t status = mAudioPolicyManager->startInput(input, session, &concurrency);
+ AudioPolicyInterface::concurrency_type__mask_t concurrency =
+ AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE;
+ status_t status = mAudioPolicyManager->startInput(input, session, silenced, &concurrency);
if (status == NO_ERROR) {
LOG_ALWAYS_FATAL_IF(concurrency & ~AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL,
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 ----------
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 833a230..9bc13c2 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -24,6 +24,7 @@
#include <utils/Vector.h>
#include <utils/SortedVector.h>
#include <binder/BinderService.h>
+#include <binder/IUidObserver.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <media/IAudioPolicyService.h>
@@ -33,9 +34,13 @@
#include "AudioPolicyEffects.h"
#include "managerdefault/AudioPolicyManager.h"
+#include <unordered_map>
+#include <unordered_set>
namespace android {
+using namespace std;
+
// ----------------------------------------------------------------------------
class AudioPolicyService :
@@ -97,7 +102,10 @@
audio_port_handle_t *selectedDeviceId = NULL,
audio_port_handle_t *portId = NULL);
virtual status_t startInput(audio_io_handle_t input,
- audio_session_t session);
+ audio_session_t session,
+ audio_devices_t device,
+ uid_t uid,
+ bool *silenced);
virtual status_t stopInput(audio_io_handle_t input,
audio_session_t session);
virtual void releaseInput(audio_io_handle_t input,
@@ -235,6 +243,57 @@
status_t dumpInternals(int fd);
+ // Handles binder shell commands
+ virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
+
+ // Sets whether the given UID records only silence
+ virtual void setRecordSilenced(uid_t uid, bool silenced);
+
+ // Overrides the UID state as if it is idle
+ status_t handleSetUidState(Vector<String16>& args, int err);
+
+ // Clears the override for the UID state
+ status_t handleResetUidState(Vector<String16>& args, int err);
+
+ // Gets the UID state
+ status_t handleGetUidState(Vector<String16>& args, int out, int err);
+
+ // Prints the shell command help
+ status_t printHelp(int out);
+
+ // If recording we need to make sure the UID is allowed to do that. If the UID is idle
+ // then it cannot record and gets buffers with zeros - silence. As soon as the UID
+ // transitions to an active state we will start reporting buffers with data. This approach
+ // transparently handles recording while the UID transitions between idle/active state
+ // avoiding to get stuck in a state receiving non-empty buffers while idle or in a state
+ // receiving empty buffers while active.
+ class UidPolicy : public BnUidObserver {
+ public:
+ explicit UidPolicy(wp<AudioPolicyService> 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<AudioPolicyService> mService;
+ std::unordered_set<uid_t> mActiveUids;
+ std::unordered_map<uid_t, bool> mOverrideUids;
+ };
+
// Thread used for tone playback and to send audio config commands to audio flinger
// For tone playback, using a separate thread is necessary to avoid deadlock with mLock because
// startTone() and stopTone() are normally called with mLock locked and requesting a tone start
@@ -306,7 +365,6 @@
const audio_config_base_t *deviceConfig,
audio_patch_handle_t patchHandle);
void insertCommand_l(AudioCommand *command, int delayMs = 0);
-
private:
class AudioCommandData;
@@ -575,6 +633,8 @@
// Manage all effects configured in audio_effects.conf
sp<AudioPolicyEffects> mAudioPolicyEffects;
audio_mode_t mPhoneState;
+
+ sp<UidPolicy> mUidPolicy;
};
} // namespace android