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/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),