diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 50fe385ec8..16150f6e72 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -39,8 +39,7 @@ sp AudioSystem::gAudioFlinger; sp 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 AudioSystem::get_audio_flinger() @@ -917,11 +916,14 @@ status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, } 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& 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 56ddd4f9e6..6507a5cf4c 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -48,6 +48,7 @@ enum { SET_MODE, SET_MIC_MUTE, GET_MIC_MUTE, + SET_RECORD_SILENCED, SET_PARAMETERS, GET_PARAMETERS, REGISTER_CLIENT, @@ -306,6 +307,15 @@ public: 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 @@ status_t BnAudioFlinger::onTransact( 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 @@ status_t BnAudioFlinger::onTransact( 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 53bc1b7066..c0e53b37b4 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -330,14 +330,22 @@ public: } 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 (reply.readInt32()); + status_t status = static_cast (reply.readInt32()); + *silenced = reply.readInt32() == 1; + return status; } virtual status_t stopInput(audio_io_handle_t input, @@ -1045,7 +1053,12 @@ status_t BnAudioPolicyService::onTransact( CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_io_handle_t input = static_cast (data.readInt32()); audio_session_t session = static_cast (data.readInt32()); - reply->writeInt32(static_cast (startInput(input, session))); + audio_devices_t device = static_cast (data.readInt32()); + uid_t uid = static_cast (data.readInt32()); + bool silenced = data.readInt32() == 1; + status_t status = startInput(input, session, device, uid, &silenced); + reply->writeInt32(static_cast (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 24a6e22ed6..d6cc37ac39 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -244,7 +244,10 @@ public: 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 57d9778774..472a3daa67 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -368,6 +368,7 @@ public: // 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 5558b7770d..7e9413d489 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -84,7 +84,10 @@ public: 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 4d5e09498c..bbdd89c1c6 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -987,6 +987,21 @@ bool AudioFlinger::getMicMute() const 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 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 bc73ffdcf4..1a35066002 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -147,6 +147,8 @@ public: 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 f8da7805d2..63a3d9882d 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -63,6 +63,9 @@ public: 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 @@ private: // 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 d5def4858c..7308c4121b 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -6528,6 +6528,7 @@ reacquire_wakelock: 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 @@ reacquire_wakelock: 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 AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac 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 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac 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 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector& args 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 track = mTracks[i]; + if (track != 0 && track->uid() == uid) { + track->setSilenced(silenced); + } + } +} void AudioFlinger::RecordThread::ResamplerBufferProvider::reset() { @@ -7827,7 +7846,9 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, 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 17f26c5f03..41d87a452b 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1396,6 +1396,9 @@ public: 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 f2cb25fd39..2a8f54d9e6 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -65,15 +65,15 @@ public: 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 @@ public: // 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 @@ public: 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 0d193735b3..dd5247d9d2 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h @@ -55,6 +55,8 @@ public: void setUid(uid_t uid) { mRecordClientInfo.uid = uid; } bool matches(const sp &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 @@ private: 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 68730a59db..f072adca92 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1801,10 +1801,15 @@ bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() { 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 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, return INVALID_OPERATION; } - Vector< sp > activeInputs = mInputs.getActiveInputs(); - for (const auto& activeDesc : activeInputs) { - if (is_virtual_input_device(activeDesc->mDevice)) { - continue; + Vector> 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 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 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, // 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 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, } #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 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, 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 @@ float AudioPolicyManager::getStreamVolumeDB( return computeVolume(stream, index, device); } +void AudioPolicyManager::setRecordSilenced(uid_t uid, bool silenced) +{ + ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced); + + Vector > activeInputs = mInputs.getActiveInputs(); + for (size_t i = 0; i < activeInputs.size(); i++) { + sp activeDesc = activeInputs[i]; + AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true); + for (size_t j = 0; j < activeSessions.size(); j++) { + sp activeSession = activeSessions.valueAt(j); + if (activeSession->uid() == uid) { + activeSession->setSilenced(silenced); + } + } + } +} + status_t AudioPolicyManager::disconnectAudioSource(const sp& sourceDesc) { ALOGV("%s handle %d", __FUNCTION__, sourceDesc->getHandle()); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 611edec822..4fd73e6aed 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -135,6 +135,7 @@ public: // 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 @@ public: // 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 7dd6d70ab5..dc676d0d36 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -362,14 +362,22 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, } 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 af0c823543..e5aed9a237 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include #include "AudioPolicyService.h" @@ -39,6 +42,8 @@ #include #include +#include + namespace android { static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n"; @@ -49,6 +54,7 @@ static const int kDumpLockSleepUs = 20000; static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds +static const String16 sManageAudioPolicyPermission("android.permission.MANAGE_AUDIO_POLICY"); // ---------------------------------------------------------------------------- @@ -79,6 +85,9 @@ void AudioPolicyService::onFirstRef() Mutex::Autolock _l(mLock); mAudioPolicyEffects = audioPolicyEffects; } + + mUidPolicy = new UidPolicy(this); + mUidPolicy->registerSelf(); } AudioPolicyService::~AudioPolicyService() @@ -92,6 +101,9 @@ AudioPolicyService::~AudioPolicyService() mNotificationClients.clear(); mAudioPolicyEffects.clear(); + + mUidPolicy->unregisterSelf(); + mUidPolicy.clear(); } // A notification client is always registered by AudioSystem when the client process @@ -318,6 +330,20 @@ status_t AudioPolicyService::dumpInternals(int fd) return NO_ERROR; } +void AudioPolicyService::setRecordSilenced(uid_t uid, bool silenced) +{ + { + Mutex::Autolock _l(mLock); + if (mAudioPolicyManager) { + mAudioPolicyManager->setRecordSilenced(uid, silenced); + } + } + sp af = AudioSystem::get_audio_flinger(); + if (af) { + af->setRecordSilenced(uid, silenced); + } +} + status_t AudioPolicyService::dump(int fd, const Vector& args __unused) { if (!dumpAllowed()) { @@ -361,11 +387,210 @@ status_t AudioPolicyService::dumpPermissionDenial(int fd) } 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 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; + } + } + 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& 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& 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& 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& 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 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"); +} + +// ----------- 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 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 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, active)); + } + isActive = isUidActiveLocked(uid); + } + if (wasActive != isActive) { + sp 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 833a2301ce..9bc13c26ba 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -33,9 +34,13 @@ #include "AudioPolicyEffects.h" #include "managerdefault/AudioPolicyManager.h" +#include +#include namespace android { +using namespace std; + // ---------------------------------------------------------------------------- class AudioPolicyService : @@ -97,7 +102,10 @@ public: 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 @@ private: status_t dumpInternals(int fd); + // Handles binder shell commands + virtual status_t shellCommand(int in, int out, int err, Vector& 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& args, int err); + + // Clears the override for the UID state + status_t handleResetUidState(Vector& args, int err); + + // Gets the UID state + status_t handleGetUidState(Vector& 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 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; + }; + // 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 @@ private: 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 @@ private: // Manage all effects configured in audio_effects.conf sp mAudioPolicyEffects; audio_mode_t mPhoneState; + + sp mUidPolicy; }; } // namespace android