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
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 8ac3366..3804418 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -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>
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 357370e..4fd72a7 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -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 @@
 
     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 @@
 
     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;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 984d9fe..0e1e97f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4708,9 +4708,14 @@
                 // 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 @@
             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 @@
 {
     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;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index fbf8fef..bbda17f 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -379,6 +379,82 @@
 }
 
 // ----------------------------------------------------------------------------
+//      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 @@
     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),
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 07a7e65..530a2e4 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -70,7 +70,20 @@
 
 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
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 762a4b1..651017d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1832,7 +1832,7 @@
 
         if (stream == AUDIO_STREAM_ENFORCED_AUDIBLE &&
                 mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
-            setStrategyMute(streamToStrategy(AUDIO_STREAM_RING), false, outputDesc);
+            setStrategyMute(streamToStrategy(AUDIO_STREAM_ALARM), false, outputDesc);
         }
 
         if (followsSameRouting(client->attributes(), attributes_initializer(AUDIO_USAGE_MEDIA))) {