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

Commit b078b2b3 authored by Eric Laurent's avatar Eric Laurent Committed by Automerger Merge Worker
Browse files

Merge "audio policy: report app ops silencing audio recordings" into sc-dev am: 4450fca8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/15161844

Change-Id: I2e4aa57e4d6d9313917737ee2ed24ab9c3a1e430
parents 76d32c89 4450fca8
Loading
Loading
Loading
Loading
+1 −39
Original line number Diff line number Diff line
@@ -21,42 +21,6 @@
    #error This header file should only be included from AudioFlinger.h
#endif

// Checks and monitors app ops for audio record
class OpRecordAudioMonitor : public RefBase {
public:
    ~OpRecordAudioMonitor() override;
    bool hasOp() const;
    int32_t getOp() const { return mAppOp; }

    static sp<OpRecordAudioMonitor> createIfNeeded(const AttributionSourceState& attributionSource,
            const audio_attributes_t& attr);

private:
    OpRecordAudioMonitor(const AttributionSourceState& attributionSource, int32_t appOp);

    void onFirstRef() override;

    AppOpsManager mAppOpsManager;

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

    private:
        const wp<OpRecordAudioMonitor> mMonitor;
    };

    sp<RecordAudioOpCallback> mOpCallback;
    // called by RecordAudioOpCallback when the app op for this OpRecordAudioMonitor is updated
    // in AppOp callback and in onFirstRef()
    void checkOp();

    std::atomic_bool mHasOp;
    const AttributionSourceState mAttributionSource;
    const int32_t mAppOp;
};

// record track
class RecordTrack : public TrackBase {
public:
@@ -107,7 +71,7 @@ public:
                                { return (mFlags & AUDIO_INPUT_FLAG_DIRECT) != 0; }

            void        setSilenced(bool silenced) { if (!isPatchTrack()) mSilenced = silenced; }
            bool        isSilenced() const;
            bool        isSilenced() const { return mSilenced; }

            status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);

@@ -154,8 +118,6 @@ private:

            bool                               mSilenced;

            // used to enforce the audio record app op corresponding to this track's audio source
            sp<OpRecordAudioMonitor>           mOpRecordAudioMonitor;
            std::string                        mSharedAudioPackageName = {};
            int32_t                            mStartFrames = -1;
};
+0 −112
Original line number Diff line number Diff line
@@ -2247,109 +2247,6 @@ void AudioFlinger::PlaybackThread::PatchTrack::restartIfDisabled()
// ----------------------------------------------------------------------------


// ----------------------------------------------------------------------------
//      AppOp for audio recording
// -------------------------------

#undef LOG_TAG
#define LOG_TAG "AF::OpRecordAudioMonitor"

// static
sp<AudioFlinger::RecordThread::OpRecordAudioMonitor>
AudioFlinger::RecordThread::OpRecordAudioMonitor::createIfNeeded(
            const AttributionSourceState& attributionSource, const audio_attributes_t& attr)
{
    if (isServiceUid(attributionSource.uid)) {
        ALOGV("not silencing record for service %s",
                attributionSource.toString().c_str());
        return nullptr;
    }

    // Capturing from FM TUNER output is not controlled by an app op
    // because it does not affect users privacy as does capturing from an actual microphone.
    if (attr.source == AUDIO_SOURCE_FM_TUNER) {
        ALOGV("not muting FM TUNER capture for uid %d", attributionSource.uid);
        return nullptr;
    }

    AttributionSourceState checkedAttributionSource = AudioFlinger::checkAttributionSourcePackage(
            attributionSource);
    if (!checkedAttributionSource.packageName.has_value()
            || checkedAttributionSource.packageName.value().size() == 0) {
        return nullptr;
    }
    return new OpRecordAudioMonitor(checkedAttributionSource, getOpForSource(attr.source));
}

AudioFlinger::RecordThread::OpRecordAudioMonitor::OpRecordAudioMonitor(
        const AttributionSourceState& attributionSource, int32_t appOp)
        : mHasOp(true), mAttributionSource(attributionSource), mAppOp(appOp)
{
}

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

void AudioFlinger::RecordThread::OpRecordAudioMonitor::onFirstRef()
{
    checkOp();
    mOpCallback = new RecordAudioOpCallback(this);
    ALOGV("start watching op %d for %s", mAppOp, mAttributionSource.toString().c_str());
    // TODO: We need to always watch AppOpsManager::OP_RECORD_AUDIO too
    // since it controls the mic permission for legacy apps.
    mAppOpsManager.startWatchingMode(mAppOp, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
        mAttributionSource.packageName.value_or(""))),
        mOpCallback);
}

bool AudioFlinger::RecordThread::OpRecordAudioMonitor::hasOp() const {
    return mHasOp.load();
}

// Called by RecordAudioOpCallback when the app op corresponding to this OpRecordAudioMonitor
// is updated in AppOp callback and in onFirstRef()
// Note this method is never called (and never to be) for audio server / root track
// due to the UID in createIfNeeded(). As a result for those record track, it's:
// - not called from constructor,
// - not called from RecordAudioOpCallback because the callback is not installed in this case
void AudioFlinger::RecordThread::OpRecordAudioMonitor::checkOp()
{
    // TODO: We need to always check AppOpsManager::OP_RECORD_AUDIO too
    // since it controls the mic permission for legacy apps.
    const int32_t mode = mAppOpsManager.checkOp(mAppOp,
            mAttributionSource.uid, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
                mAttributionSource.packageName.value_or(""))));
    const bool hasIt = (mode == AppOpsManager::MODE_ALLOWED);
    // verbose logging only log when appOp changed
    ALOGI_IF(hasIt != mHasOp.load(),
            "App op %d missing, %ssilencing record %s",
            mAppOp, hasIt ? "un" : "", mAttributionSource.toString().c_str());
    mHasOp.store(hasIt);
}

AudioFlinger::RecordThread::OpRecordAudioMonitor::RecordAudioOpCallback::RecordAudioOpCallback(
        const wp<OpRecordAudioMonitor>& monitor) : mMonitor(monitor)
{ }

void AudioFlinger::RecordThread::OpRecordAudioMonitor::RecordAudioOpCallback::opChanged(int32_t op,
            const String16& packageName) {
    UNUSED(packageName);
    sp<OpRecordAudioMonitor> monitor = mMonitor.promote();
    if (monitor != NULL) {
        if (op != monitor->getOp()) {
            return;
        }
        monitor->checkOp();
    }
}



#undef LOG_TAG
#define LOG_TAG "AF::RecordHandle"

@@ -2450,7 +2347,6 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
        mRecordBufferConverter(NULL),
        mFlags(flags),
        mSilenced(false),
        mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(attributionSource, attr)),
        mStartFrames(startFrames)
{
    if (mCblk == NULL) {
@@ -2709,14 +2605,6 @@ void AudioFlinger::RecordThread::RecordTrack::updateTrackFrameInfo(
    mServerLatencyMs.store(latencyMs);
}

bool AudioFlinger::RecordThread::RecordTrack::isSilenced() const {
    if (mSilenced) {
        return true;
    }
    // The monitor is only created for record tracks that can be silenced.
    return mOpRecordAudioMonitor ? !mOpRecordAudioMonitor->hasOp() : false;
}

status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones(
        std::vector<media::MicrophoneInfo>* activeMicrophones)
{
+1 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ cc_library_shared {
        "libmedia_helper",
        "libmediametrics",
        "libmediautils",
        "libpermission",
        "libsensorprivacy",
        "libutils",
        "audioclient-types-aidl-cpp",
+2 −1
Original line number Diff line number Diff line
@@ -674,7 +674,8 @@ Status AudioPolicyService::getInputForAttr(const media::AudioAttributesInternal&

        sp<AudioRecordClient> client = new AudioRecordClient(attr, input, session, portId,
                                                             selectedDeviceId, adjAttributionSource,
                                                             canCaptureOutput, canCaptureHotword);
                                                             canCaptureOutput, canCaptureHotword,
                                                             mAudioCommandThread);
        mAudioRecordClients.add(portId, client);
    }

+139 −1
Original line number Diff line number Diff line
@@ -730,7 +730,10 @@ void AudioPolicyService::updateUidStates_l()
                    && !(isTopOrLatestSensitive || current->canCaptureOutput))
                && canCaptureIfInCallOrCommunication(current);

        if (isVirtualSource(source)) {
        if (!current->hasOp()) {
            // Never allow capture if app op is denied
            allowCapture = false;
        } else if (isVirtualSource(source)) {
            // Allow capture for virtual (remote submix, call audio TX or RX...) sources
            allowCapture = true;
        } else if (mUidPolicy->isAssistantUid(currentUid)) {
@@ -830,6 +833,19 @@ bool AudioPolicyService::isVirtualSource(audio_source_t source)
    return false;
}

/* static */
bool AudioPolicyService::isAppOpSource(audio_source_t source)
{
    switch (source) {
        case AUDIO_SOURCE_FM_TUNER:
        case AUDIO_SOURCE_ECHO_REFERENCE:
            return false;
        default:
            break;
    }
    return true;
}

void AudioPolicyService::setAppState_l(sp<AudioRecordClient> client, app_state_t state)
{
    AutoCallerClear acc;
@@ -1418,6 +1434,109 @@ binder::Status AudioPolicyService::SensorPrivacyPolicy::onSensorPrivacyChanged(b
    return binder::Status::ok();
}

// -----------  AudioPolicyService::OpRecordAudioMonitor implementation ----------

// static
sp<AudioPolicyService::OpRecordAudioMonitor>
AudioPolicyService::OpRecordAudioMonitor::createIfNeeded(
            const AttributionSourceState& attributionSource, const audio_attributes_t& attr,
            wp<AudioCommandThread> commandThread)
{
    if (isServiceUid(attributionSource.uid)) {
        ALOGV("not silencing record for service %s",
                attributionSource.toString().c_str());
        return nullptr;
    }

    if (!AudioPolicyService::isAppOpSource(attr.source)) {
        ALOGD("not monitoring app op for uid %d and source %d",
                attributionSource.uid, attr.source);
        return nullptr;
    }

    if (!attributionSource.packageName.has_value()
            || attributionSource.packageName.value().size() == 0) {
        return nullptr;
    }
    return new OpRecordAudioMonitor(attributionSource, getOpForSource(attr.source), commandThread);
}

AudioPolicyService::OpRecordAudioMonitor::OpRecordAudioMonitor(
        const AttributionSourceState& attributionSource, int32_t appOp,
        wp<AudioCommandThread> commandThread) :
            mHasOp(true), mAttributionSource(attributionSource), mAppOp(appOp),
            mCommandThread(commandThread)
{
}

AudioPolicyService::OpRecordAudioMonitor::~OpRecordAudioMonitor()
{
    if (mOpCallback != 0) {
        mAppOpsManager.stopWatchingMode(mOpCallback);
    }
    mOpCallback.clear();
}

void AudioPolicyService::OpRecordAudioMonitor::onFirstRef()
{
    checkOp();
    mOpCallback = new RecordAudioOpCallback(this);
    ALOGV("start watching op %d for %s", mAppOp, mAttributionSource.toString().c_str());
    // TODO: We need to always watch AppOpsManager::OP_RECORD_AUDIO too
    // since it controls the mic permission for legacy apps.
    mAppOpsManager.startWatchingMode(mAppOp, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
        mAttributionSource.packageName.value_or(""))),
        mOpCallback);
}

bool AudioPolicyService::OpRecordAudioMonitor::hasOp() const {
    return mHasOp.load();
}

// Called by RecordAudioOpCallback when the app op corresponding to this OpRecordAudioMonitor
// is updated in AppOp callback and in onFirstRef()
// Note this method is never called (and never to be) for audio server / root track
// due to the UID in createIfNeeded(). As a result for those record track, it's:
// - not called from constructor,
// - not called from RecordAudioOpCallback because the callback is not installed in this case
void AudioPolicyService::OpRecordAudioMonitor::checkOp(bool updateUidStates)
{
    // TODO: We need to always check AppOpsManager::OP_RECORD_AUDIO too
    // since it controls the mic permission for legacy apps.
    const int32_t mode = mAppOpsManager.checkOp(mAppOp,
            mAttributionSource.uid, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
                mAttributionSource.packageName.value_or(""))));
    const bool hasIt = (mode == AppOpsManager::MODE_ALLOWED);
    // verbose logging only log when appOp changed
    ALOGI_IF(hasIt != mHasOp.load(),
            "App op %d missing, %ssilencing record %s",
            mAppOp, hasIt ? "un" : "", mAttributionSource.toString().c_str());
    mHasOp.store(hasIt);

    if (updateUidStates) {
          sp<AudioCommandThread> commandThread = mCommandThread.promote();
          if (commandThread != nullptr) {
              commandThread->updateUidStatesCommand();
          }
    }
}

AudioPolicyService::OpRecordAudioMonitor::RecordAudioOpCallback::RecordAudioOpCallback(
        const wp<OpRecordAudioMonitor>& monitor) : mMonitor(monitor)
{ }

void AudioPolicyService::OpRecordAudioMonitor::RecordAudioOpCallback::opChanged(int32_t op,
            const String16& packageName __unused) {
    sp<OpRecordAudioMonitor> monitor = mMonitor.promote();
    if (monitor != NULL) {
        if (op != monitor->getOp()) {
            return;
        }
        monitor->checkOp(true);
    }
}


// -----------  AudioPolicyService::AudioCommandThread implementation ----------

AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name,
@@ -1634,6 +1753,17 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
                    mLock.lock();
                    } break;

                case UPDATE_UID_STATES: {
                    ALOGV("AudioCommandThread() processing updateUID states");
                    svc = mService.promote();
                    if (svc == 0) {
                        break;
                    }
                    mLock.unlock();
                    svc->updateUidStates();
                    mLock.lock();
                    } break;

                default:
                    ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                }
@@ -1847,6 +1977,14 @@ void AudioPolicyService::AudioCommandThread::updateAudioPortListCommand()
    sendCommand(command);
}

void AudioPolicyService::AudioCommandThread::updateUidStatesCommand()
{
    sp<AudioCommand> command = new AudioCommand();
    command->mCommand = UPDATE_UID_STATES;
    ALOGV("AudioCommandThread() adding update UID states");
    sendCommand(command);
}

void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand()
{
    sp<AudioCommand>command = new AudioCommand();
Loading