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

Commit 0620de1c authored by Atneya Nair's avatar Atneya Nair
Browse files

audio: Add addtl permission sync behavior

The original addition of ther permission sync barrier just synchronized
up to handling permission change events within AudioService itself.

Modify the sysprop listener to be able to handle requests to manually
refresh the sys prop state, and trigger an event if any change occurred.
This entirely eliminates any potential raciness.

Test: atest CtsMediaAudioTestCases
Bug: 338089555
Flag: com.android.media.audio.audioserver_permissions
Change-Id: I357b7b9f36f109426273e8956a77aabfbc2a72f3
parent 2e148f18
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@ cc_library_shared_for_libandroid_runtime {
        "libtracing_perfetto",
        "libharfbuzz_ng",
        "liblog",
        "libmediautils",
        "libminikin",
        "libz",
        "server_configurable_flags",
+57 −27
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

//#define LOG_NDEBUG 0

#include <atomic>
#define LOG_TAG "AudioSystem-JNI"
#include <android/binder_ibinder_jni.h>
#include <android/binder_libbinder.h>
@@ -34,15 +35,16 @@
#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
#include <mediautils/jthread.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/jni_macros.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <sys/system_properties.h>
#include <utils/Log.h>

#include <thread>
#include <optional>
#include <sstream>
#include <memory>
@@ -57,6 +59,7 @@
#include "android_media_AudioMixerAttributes.h"
#include "android_media_AudioProfile.h"
#include "android_media_MicrophoneInfo.h"
#include "android_media_JNIUtils.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"

@@ -3375,42 +3378,53 @@ static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIE
class JavaSystemPropertyListener {
  public:
    JavaSystemPropertyListener(JNIEnv* env, jobject javaCallback, std::string sysPropName) :
            mCallback(env->NewGlobalRef(javaCallback)),
            mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}) {
        mListenerThread = std::thread([this]() mutable {
            JNIEnv* threadEnv = GetOrAttachJNIEnvironment(gVm);
            while (!mCleanupSignal.load()) {
                using namespace std::chrono_literals;
                // 1s timeout so this thread can read the cleanup signal to (slowly) be able to
                // be destroyed.
                std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
                if (newVal != "" && mLastVal != newVal) {
                    threadEnv->CallVoidMethod(mCallback, gRunnableClassInfo.run);
                    mLastVal = std::move(newVal);
            mCallback {javaCallback, env},
            mPi {__system_property_find(sysPropName.c_str())},
            mListenerThread([this](mediautils::stop_token stok) mutable {
                static const struct timespec close_delay = { .tv_sec = 1 };
                while (!stok.stop_requested()) {
                    uint32_t old_serial = mSerial.load();
                    uint32_t new_serial;
                    if (__system_property_wait(mPi, old_serial, &new_serial, &close_delay)) {
                        while (new_serial > old_serial) {
                            if (mSerial.compare_exchange_weak(old_serial, new_serial)) {
                                fireUpdate();
                                break;
                            }
                        }
                    }
            });
                }
            }) {}

    ~JavaSystemPropertyListener() {
        mCleanupSignal.store(true);
        mListenerThread.join();
        JNIEnv* env = GetOrAttachJNIEnvironment(gVm);
        env->DeleteGlobalRef(mCallback);
    void triggerUpdateIfChanged() {
        uint32_t old_serial = mSerial.load();
        uint32_t new_serial = __system_property_serial(mPi);
        while (new_serial > old_serial) {
            if (mSerial.compare_exchange_weak(old_serial, new_serial)) {
                fireUpdate();
                break;
            }
        }
    }

  private:
    jobject mCallback;
    android::base::CachedProperty mCachedProperty;
    std::thread mListenerThread;
    std::atomic<bool> mCleanupSignal{false};
    std::string mLastVal = "";
    void fireUpdate() {
        const auto threadEnv = GetOrAttachJNIEnvironment(gVm);
        threadEnv->CallVoidMethod(mCallback.get(), gRunnableClassInfo.run);
    }

    // Should outlive thread object
    const GlobalRef mCallback;
    const prop_info * const mPi;
    std::atomic<uint32_t> mSerial = 0;
    const mediautils::jthread mListenerThread;
};

// A logical set keyed by address
std::vector<std::unique_ptr<JavaSystemPropertyListener>> gSystemPropertyListeners;
std::mutex gSysPropLock{};

static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env,  jobject thiz,
static jlong android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env,  jobject thiz,
        jstring sysProp,
        jobject javaCallback) {
    ScopedUtfChars sysPropChars{env, sysProp};
@@ -3418,6 +3432,19 @@ static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env,
            std::string{sysPropChars.c_str()});
    std::unique_lock _l{gSysPropLock};
    gSystemPropertyListeners.push_back(std::move(listener));
    return reinterpret_cast<jlong>(gSystemPropertyListeners.back().get());
}

static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env,  jobject thiz,
        jlong nativeHandle) {
    std::unique_lock _l{gSysPropLock};
    const auto iter = std::find_if(gSystemPropertyListeners.begin(), gSystemPropertyListeners.end(),
            [nativeHandle](const auto& x) { return reinterpret_cast<jlong>(x.get()) == nativeHandle; });
    if (iter != gSystemPropertyListeners.end()) {
        (*iter)->triggerUpdateIfChanged();
    } else {
        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid handle");
    }
}


@@ -3595,8 +3622,11 @@ static const JNINativeMethod gMethods[] =
         MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
         MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
         MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
                                "(Ljava/lang/String;Ljava/lang/Runnable;)V",
                                "(Ljava/lang/String;Ljava/lang/Runnable;)J",
                                android_media_AudioSystem_listenForSystemPropertyChange),
         MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate",
                                "(J)V",
                                android_media_AudioSystem_triggerSystemPropertyUpdate),

        };

+10 −1
Original line number Diff line number Diff line
@@ -2655,7 +2655,16 @@ public class AudioSystem
    /**
     * Register a native listener for system property sysprop
     * @param callback the listener which fires when the property changes
     * @return a native handle for use in subsequent methods
     * @hide
     */
    public static native void listenForSystemPropertyChange(String sysprop, Runnable callback);
    public static native long listenForSystemPropertyChange(String sysprop, Runnable callback);

    /**
     * Trigger a sysprop listener update, if the property has been updated: synchronously validating
     * there are no pending sysprop changes.
     * @param handle the handle returned by {@link listenForSystemPropertyChange}
     * @hide
     */
    public static native void triggerSystemPropertyUpdate(long handle);
}
+3 −1
Original line number Diff line number Diff line
@@ -790,6 +790,7 @@ public class AudioService extends IAudioService.Stub
    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
    private final Executor mAudioServerLifecycleExecutor;
    private long mSysPropListenerNativeHandle;
    private final List<Future> mScheduledPermissionTasks = new ArrayList();
    private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -10640,7 +10641,7 @@ public class AudioService extends IAudioService.Stub
                    }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
                }
            };
            mAudioSystem.listenForSystemPropertyChange(
            mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    task);
        } else {
@@ -14708,6 +14709,7 @@ public class AudioService extends IAudioService.Stub
    @Override
    /** @see AudioManager#permissionUpdateBarrier() */
    public void permissionUpdateBarrier() {
        mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
        List<Future> snapshot;
        synchronized (mScheduledPermissionTasks) {
            snapshot = List.copyOf(mScheduledPermissionTasks);
+6 −2
Original line number Diff line number Diff line
@@ -748,8 +748,12 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
        return AudioSystem.setMasterMute(mute);
    }

    public void listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
        AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
    public long listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
        return AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
    }

    public void triggerSystemPropertyUpdate(long handle) {
        AudioSystem.triggerSystemPropertyUpdate(handle);
    }

    /**