Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74e01fa7 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

Audioflinger: tracks monitor OP_PLAY_AUDIO

Mute/unmute tracks according to changes in OP_PLAY_AUDIO for
  the current usage.
In audio policy: always assign AUDIO_STREAM_ENFORCED_AUDIBLE
  to sonification tracks with AUDIBILITY_ENFORCED flag.
Do not mute tracks from root / audio server.
Do not mute UI sounds on AUDIO_STREAM_ENFORCED_AUDIBLE
  stream type.

Bug: 112339570
Test: enter DnD, play notifications, verify not heard
Change-Id: Ia5f1118481cf0573101acf2092fbd0ce2cf8c038
parent c70268aa
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -54,7 +54,9 @@
#include <utils/TypeHelpers.h>
#include <utils/Vector.h>

#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IAppOpsCallback.h>
#include <binder/MemoryDealer.h>

#include <system/audio.h>
+34 −0
Original line number Diff line number Diff line
@@ -19,6 +19,36 @@
    #error This header file should only be included from AudioFlinger.h
#endif

// Checks and monitors OP_PLAY_AUDIO
class OpPlayAudioMonitor : public RefBase {
public:
    OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType);
    ~OpPlayAudioMonitor() override;
    bool hasOpPlayAudio() const;

private:
    AppOpsManager mAppOpsManager;

    class PlayAudioOpCallback : public BnAppOpsCallback {
    public:
        explicit PlayAudioOpCallback(const wp<OpPlayAudioMonitor>& monitor);
        void opChanged(int32_t op, const String16& packageName) override;

    private:
        const wp<OpPlayAudioMonitor> mMonitor;
    };

    sp<PlayAudioOpCallback> mOpCallback;
    // called by PlayAudioOpCallback when OP_PLAY_AUDIO is updated in AppOp callback
    void checkPlayAudioForUsage();

    std::atomic_bool mHasOpPlayAudio;
    Vector<String16> mPackages;
    const uid_t mUid;
    const int32_t mUsage; // on purpose not audio_usage_t because always checked in appOps as int32_t
    const int mId; // for logging purposes only
};

// playback track
class Track : public TrackBase, public VolumeProvider {
public:
@@ -179,6 +209,8 @@ public:

    int fastIndex() const { return mFastIndex; }

    bool isPlaybackRestricted() const { return !mOpPlayAudioMonitor->hasOpPlayAudio(); }

protected:

    // FILLED state is used for suppressing volume ramp at begin of playing
@@ -207,6 +239,8 @@ protected:

    sp<media::VolumeHandler>  mVolumeHandler; // handles multiple VolumeShaper configs and operations

    sp<OpPlayAudioMonitor>  mOpPlayAudioMonitor;

    bool                mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not
    // intensity to play haptic data
    AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE;
+9 −3
Original line number Diff line number Diff line
@@ -4708,9 +4708,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
                // lacks any synchronization or barrier so VolumeProvider may read a stale value
                const float vh = track->getVolumeHandler()->getVolume(
                        proxy->framesReleased()).first;
                float volume = masterVolume
                float volume;
                if (track->isPlaybackRestricted()) {
                    volume = 0.f;
                } else {
                    volume = masterVolume
                        * mStreamTypes[track->streamType()].volume
                        * vh;
                }
                track->mCachedVolume = volume;
                gain_minifloat_packed_t vlr = proxy->getVolumeLR();
                float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr));
@@ -4860,7 +4865,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
            float typeVolume = mStreamTypes[track->streamType()].volume;
            float v = masterVolume * typeVolume;

            if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
            if (track->isPausing() || mStreamTypes[track->streamType()].mute
                    || track->isPlaybackRestricted()) {
                vl = vr = 0;
                vlf = vrf = vaf = 0.;
                if (track->isPausing()) {
@@ -5447,7 +5453,7 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr
{
    float left, right;

    if (mMasterMute || mStreamTypes[track->streamType()].mute) {
    if (mMasterMute || mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
        left = right = 0;
    } else {
        float typeVolume = mStreamTypes[track->streamType()].volume;
+77 −0
Original line number Diff line number Diff line
@@ -378,6 +378,82 @@ status_t AudioFlinger::TrackHandle::onTransact(
    return BnAudioTrack::onTransact(code, data, reply, flags);
}

// ----------------------------------------------------------------------------
//      AppOp for audio playback
// -------------------------------
AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor(uid_t uid, audio_usage_t usage,
        int id, audio_stream_type_t streamType)
            : mHasOpPlayAudio(true), mUid(uid), mUsage((int32_t) usage), mId(id)
{
    if (isAudioServerOrRootUid(uid)) {
        ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", mId, usage);
        return;
    }
    // stream type has been filtered by audio policy to indicate whether it can be muted
    if (streamType == AUDIO_STREAM_ENFORCED_AUDIBLE) {
        ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", mId, usage);
        return;
    }
    PermissionController permissionController;
    permissionController.getPackagesForUid(uid, mPackages);
    checkPlayAudioForUsage();
    if (!mPackages.isEmpty()) {
        mOpCallback = new PlayAudioOpCallback(this);
        mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO, mPackages[0], mOpCallback);
    }
}

AudioFlinger::PlaybackThread::OpPlayAudioMonitor::~OpPlayAudioMonitor()
{
    if (mOpCallback != 0) {
        mAppOpsManager.stopWatchingMode(mOpCallback);
    }
    mOpCallback.clear();
}

bool AudioFlinger::PlaybackThread::OpPlayAudioMonitor::hasOpPlayAudio() const {
    return mHasOpPlayAudio.load();
}

// Note this method is never called (and never to be) for audio server / root track
// - not called from constructor due to check on UID,
// - not called from PlayAudioOpCallback because the callback is not installed in this case
void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::checkPlayAudioForUsage()
{
    if (mPackages.isEmpty()) {
        mHasOpPlayAudio.store(false);
    } else {
        bool hasIt = true;
        for (const String16& packageName : mPackages) {
            const int32_t mode = mAppOpsManager.checkAudioOpNoThrow(AppOpsManager::OP_PLAY_AUDIO,
                    mUsage, mUid, packageName);
            if (mode != AppOpsManager::MODE_ALLOWED) {
                hasIt = false;
                break;
            }
        }
        ALOGD("OpPlayAudio: track:%d usage:%d %smuted", mId, mUsage, hasIt ? "not " : "");
        mHasOpPlayAudio.store(hasIt);
    }
}

AudioFlinger::PlaybackThread::OpPlayAudioMonitor::PlayAudioOpCallback::PlayAudioOpCallback(
        const wp<OpPlayAudioMonitor>& monitor) : mMonitor(monitor)
{ }

void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::PlayAudioOpCallback::opChanged(int32_t op,
            const String16& packageName) {
    // we only have uid, so we need to check all package names anyway
    UNUSED(packageName);
    if (op != AppOpsManager::OP_PLAY_AUDIO) {
        return;
    }
    sp<OpPlayAudioMonitor> monitor = mMonitor.promote();
    if (monitor != NULL) {
        monitor->checkPlayAudioForUsage();
    }
}

// ----------------------------------------------------------------------------
#undef LOG_TAG
#define LOG_TAG "AF::Track"
@@ -416,6 +492,7 @@ AudioFlinger::PlaybackThread::Track::Track(
    mPresentationCompleteFrames(0),
    mFrameMap(16 /* sink-frame-to-track-frame map memory */),
    mVolumeHandler(new media::VolumeHandler(sampleRate)),
    mOpPlayAudioMonitor(new OpPlayAudioMonitor(uid, attr.usage, id(), streamType)),
    // mSinkTimestamp
    mFastIndex(-1),
    mCachedVolume(1.0),
+14 −1
Original line number Diff line number Diff line
@@ -70,7 +70,20 @@ product_strategy_t EngineBase::getProductStrategyForAttributes(const audio_attri

audio_stream_type_t EngineBase::getStreamTypeForAttributes(const audio_attributes_t &attr) const
{
    return mProductStrategies.getStreamTypeForAttributes(attr);
    audio_stream_type_t engineStream = mProductStrategies.getStreamTypeForAttributes(attr);
    // ensure the audibility flag for sonification is honored for stream types
    // Note this is typically implemented in the product strategy configuration files, but is
    //   duplicated here for safety.
    if (attr.usage == AUDIO_USAGE_ASSISTANCE_SONIFICATION
            && ((attr.flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) != 0)) {
        engineStream = AUDIO_STREAM_ENFORCED_AUDIBLE;
    }
    // ensure the ENFORCED_AUDIBLE stream type reflects the "force use" setting:
    if ((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
            && (engineStream == AUDIO_STREAM_ENFORCED_AUDIBLE)) {
        return AUDIO_STREAM_SYSTEM;
    }
    return engineStream;
}

audio_attributes_t EngineBase::getAttributesForStreamType(audio_stream_type_t stream) const
Loading