Check for app manifest opt-out from playback capture

Query the package manager to check if the app has not opt-out of its
playback being captured.

Test: adb shell audiorecorder   --target /data/file1.raw &
      adb shell am start -a android.intent.action.VIEW -d file:///system/media/audio/ringtones/Lollipop.ogg -t audio/ogg
      adb dumpsys media.audio_policy # check playback is not recorded
      # change media player manifest to allowPlaybackCapture=true
      adb dumpsys media.audio_policy # check playback is recorded
      kill %1
      adb pull /data/file1.raw && sox -r 48000 -e signed -b 16 -c 2 file1.raw file.wav&& audacity file.wav
      # check silence then sound

Bug: 111453086
Change-Id: Id6fb7d0e10c02b0473bcbc0786e8360536996f48
Signed-off-by: Kevin Rocard <krocard@google.com>
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
index 10d8b13..1f24413 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -16,6 +16,7 @@
 	libhwbinder \
 	libmedia \
 	libmedialogservice \
+	libmediautils \
 	libnbaio \
 	libnblog \
 	libsoundtriggerservice \
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 599c446..2fb24f5 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -22,6 +22,9 @@
 #include <binder/PermissionCache.h>
 #include "mediautils/ServiceUtilities.h"
 
+#include <iterator>
+#include <algorithm>
+
 /* When performing permission checks we do not use permission cache for
  * runtime permissions (protection level dangerous) as they may change at
  * runtime. All other permissions (protection level normal and dangerous)
@@ -220,4 +223,85 @@
     return NO_ERROR;
 }
 
+sp<content::pm::IPackageManagerNative> MediaPackageManager::retreivePackageManager() {
+    const sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+        ALOGW("%s: failed to retrieve defaultServiceManager", __func__);
+        return nullptr;
+    }
+    sp<IBinder> packageManager = sm->checkService(String16(nativePackageManagerName));
+    if (packageManager == nullptr) {
+        ALOGW("%s: failed to retrieve native package manager", __func__);
+        return nullptr;
+    }
+    return interface_cast<content::pm::IPackageManagerNative>(packageManager);
+}
+
+std::optional<bool> MediaPackageManager::doIsAllowed(uid_t uid) {
+    if (mPackageManager == nullptr) {
+        /** Can not fetch package manager at construction it may not yet be registered. */
+        mPackageManager = retreivePackageManager();
+        if (mPackageManager == nullptr) {
+            ALOGW("%s: Playback capture is denied as package manager is not reachable", __func__);
+            return std::nullopt;
+        }
+    }
+
+    std::vector<std::string> packageNames;
+    auto status = mPackageManager->getNamesForUids({(int32_t)uid}, &packageNames);
+    if (!status.isOk()) {
+        ALOGW("%s: Playback capture is denied for uid %u as the package names could not be "
+              "retrieved from the package manager: %s", __func__, uid, status.toString8().c_str());
+        return std::nullopt;
+    }
+    if (packageNames.empty()) {
+        ALOGW("%s: Playback capture for uid %u is denied as no package name could be retrieved "
+              "from the package manager: %s", __func__, uid, status.toString8().c_str());
+        return std::nullopt;
+    }
+    std::vector<bool> isAllowed;
+    status = mPackageManager->isAudioPlaybackCaptureAllowed(packageNames, &isAllowed);
+    if (!status.isOk()) {
+        ALOGW("%s: Playback capture is denied for uid %u as the manifest property could not be "
+              "retrieved from the package manager: %s", __func__, uid, status.toString8().c_str());
+        return std::nullopt;
+    }
+    if (packageNames.size() != isAllowed.size()) {
+        ALOGW("%s: Playback capture is denied for uid %u as the package manager returned incoherent"
+              " response size: %zu != %zu", __func__, uid, packageNames.size(), isAllowed.size());
+        return std::nullopt;
+    }
+
+    // Zip together packageNames and isAllowed for debug logs
+    Packages& packages = mDebugLog[uid];
+    packages.resize(packageNames.size()); // Reuse all objects
+    std::transform(begin(packageNames), end(packageNames), begin(isAllowed),
+                   begin(packages), [] (auto& name, bool isAllowed) -> Package {
+                       return {std::move(name), isAllowed};
+                   });
+
+    // Only allow playback record if all packages in this UID allow it
+    bool playbackCaptureAllowed = std::all_of(begin(isAllowed), end(isAllowed),
+                                                  [](bool b) { return b; });
+
+    return playbackCaptureAllowed;
+}
+
+void MediaPackageManager::dump(int fd, int spaces) const {
+    dprintf(fd, "%*sAllow playback capture log:\n", spaces, "");
+    if (mPackageManager == nullptr) {
+        dprintf(fd, "%*sNo package manager\n", spaces + 2, "");
+    }
+    dprintf(fd, "%*sPackage manager errors: %u\n", spaces + 2, "", mPackageManagerErrors);
+
+    for (const auto& uidCache : mDebugLog) {
+        for (const auto& package : std::get<Packages>(uidCache)) {
+            dprintf(fd, "%*s- uid=%5u, allowPlaybackCapture=%s, packageName=%s\n", spaces + 2, "",
+                    std::get<const uid_t>(uidCache),
+                    package.playbackCaptureAllowed ? "true " : "false",
+                    package.name.c_str());
+        }
+    }
+}
+
 } // namespace android
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index 98f54c2..94370ee 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -14,13 +14,22 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_MEDIAUTILS_SERVICEUTILITIES_H
+#define ANDROID_MEDIAUTILS_SERVICEUTILITIES_H
+
 #include <unistd.h>
 
+#include <android/content/pm/IPackageManagerNative.h>
 #include <binder/IMemory.h>
 #include <binder/PermissionController.h>
 #include <cutils/multiuser.h>
 #include <private/android_filesystem_config.h>
 
+#include <map>
+#include <optional>
+#include <string>
+#include <vector>
+
 namespace android {
 
 // Audio permission utilities
@@ -72,4 +81,31 @@
 bool dumpAllowed();
 bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
 status_t checkIMemory(const sp<IMemory>& iMemory);
+
+class MediaPackageManager {
+public:
+    /** Query the PackageManager to check if all apps of an UID allow playback capture. */
+    bool allowPlaybackCapture(uid_t uid) {
+        auto result = doIsAllowed(uid);
+        if (!result) {
+            mPackageManagerErrors++;
+        }
+        return result.value_or(false);
+    }
+    void dump(int fd, int spaces = 0) const;
+private:
+    static constexpr const char* nativePackageManagerName = "package_native";
+    std::optional<bool> doIsAllowed(uid_t uid);
+    sp<content::pm::IPackageManagerNative> retreivePackageManager();
+    sp<content::pm::IPackageManagerNative> mPackageManager; // To check apps manifest
+    uint_t mPackageManagerErrors = 0;
+    struct Package {
+        std::string name;
+        bool playbackCaptureAllowed = false;
+    };
+    using Packages = std::vector<Package>;
+    std::map<uid_t, Packages> mDebugLog;
+};
 }
+
+#endif // ANDROID_MEDIAUTILS_SERVICEUTILITIES_H
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index f72f44a..9e4eebc 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -90,7 +90,7 @@
 LOCAL_SHARED_LIBRARIES += libmedia_helper
 LOCAL_SHARED_LIBRARIES += libmediametrics
 
-LOCAL_SHARED_LIBRARIES += libhidlbase libxml2
+LOCAL_SHARED_LIBRARIES += libbinder libhidlbase libxml2
 
 ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1)
 LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index d31ce53..4c1ca8c 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -20,7 +20,6 @@
 #include "AudioPolicyService.h"
 #include "TypeConverter.h"
 #include <media/MediaAnalyticsItem.h>
-#include <mediautils/ServiceUtilities.h>
 #include <media/AudioPolicy.h>
 #include <utils/Log.h>
 
@@ -167,7 +166,7 @@
     return mAudioPolicyManager->getOutput(stream);
 }
 
-status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
+status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *originalAttr,
                                               audio_io_handle_t *output,
                                               audio_session_t session,
                                               audio_stream_type_t *stream,
@@ -191,9 +190,13 @@
                 "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
         uid = callingUid;
     }
+    audio_attributes_t attr = *originalAttr;
+    if (!mPackageManager.allowPlaybackCapture(uid)) {
+        attr.flags |= AUDIO_FLAG_NO_CAPTURE;
+    }
     audio_output_flags_t originalFlags = flags;
     AutoCallerClear acc;
-    status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
+    status_t result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid,
                                                  config,
                                                  &flags, selectedDeviceId, portId,
                                                  secondaryOutputs);
@@ -209,14 +212,14 @@
         *selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
         *portId = AUDIO_PORT_HANDLE_NONE;
         secondaryOutputs->clear();
-        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
+        result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid, config,
                                                        &flags, selectedDeviceId, portId,
                                                        secondaryOutputs);
     }
 
     if (result == NO_ERROR) {
         sp <AudioPlaybackClient> client =
-            new AudioPlaybackClient(*attr, *output, uid, pid, session, *selectedDeviceId, *stream);
+            new AudioPlaybackClient(attr, *output, uid, pid, session, *selectedDeviceId, *stream);
         mAudioPlaybackClients.add(*portId, client);
     }
     return result;
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 76ac191..1d72931 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -551,6 +551,8 @@
             mAudioPolicyManager->dump(fd);
         }
 
+        mPackageManager.dump(fd);
+
         if (locked) mLock.unlock();
     }
     return NO_ERROR;
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 8cd6e81..7fc52dd 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -31,6 +31,7 @@
 #include <media/ToneGenerator.h>
 #include <media/AudioEffect.h>
 #include <media/AudioPolicy.h>
+#include <mediautils/ServiceUtilities.h>
 #include "AudioPolicyEffects.h"
 #include "managerdefault/AudioPolicyManager.h"
 #include <android/hardware/BnSensorPrivacyListener.h>
@@ -784,6 +785,8 @@
 
     DefaultKeyedVector< audio_port_handle_t, sp<AudioRecordClient> >   mAudioRecordClients;
     DefaultKeyedVector< audio_port_handle_t, sp<AudioPlaybackClient> >   mAudioPlaybackClients;
+
+    MediaPackageManager mPackageManager; // To check allowPlaybackCapture
 };
 
 } // namespace android