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

Commit 700e7347 authored by Eric Laurent's avatar Eric Laurent
Browse files

audio routing update listener

Implement audio port and audio patch
callback infrastructure for clients to receive
notifications when audio routing changes occur via the
OnAudioPortUpdateListener interface.

Bug: 14815883.

Change-Id: I32cbba64eca7369871aec235ff100de1f0c2d344
parent b69681c8
Loading
Loading
Loading
Loading
+121 −1
Original line number Diff line number Diff line
@@ -99,6 +99,9 @@ static struct {
    // other fields unused by JNI
} gAudioPatchFields;

static const char* const kEventHandlerClassPathName =
        "android/media/AudioPortEventHandler";
static jmethodID gPostEventFromNative;

enum AudioError {
    kAudioStatusOk = 0,
@@ -106,8 +109,85 @@ enum AudioError {
    kAudioStatusMediaServerDied = 100
};

enum  {
    AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1,
    AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2,
    AUDIOPORT_EVENT_SERVICE_DIED = 3,
};

#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5

// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class JNIAudioPortCallback: public AudioSystem::AudioPortCallback
{
public:
    JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
    ~JNIAudioPortCallback();

    virtual void onAudioPortListUpdate();
    virtual void onAudioPatchListUpdate();
    virtual void onServiceDied();

private:
    void sendEvent(int event);

    jclass      mClass;     // Reference to AudioPortEventHandlerDelegate class
    jobject     mObject;    // Weak ref to AudioPortEventHandlerDelegate Java object to call on
};

JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
{

    // Hold onto the SoundTriggerModule class for use in calling the static method
    // that posts events to the application thread.
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        ALOGE("Can't find class %s", kEventHandlerClassPathName);
        return;
    }
    mClass = (jclass)env->NewGlobalRef(clazz);

    // We use a weak reference so the SoundTriggerModule object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    mObject  = env->NewGlobalRef(weak_thiz);
}

JNIAudioPortCallback::~JNIAudioPortCallback()
{
    // remove global references
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    env->DeleteGlobalRef(mObject);
    env->DeleteGlobalRef(mClass);
}

void JNIAudioPortCallback::sendEvent(int event)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();

    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
                              event, 0, 0, NULL);
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        env->ExceptionClear();
    }
}

void JNIAudioPortCallback::onAudioPortListUpdate()
{
    sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED);
}

void JNIAudioPortCallback::onAudioPatchListUpdate()
{
    sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED);
}

void JNIAudioPortCallback::onServiceDied()
{
    sendEvent(AUDIOPORT_EVENT_SERVICE_DIED);
}

static int check_AudioSystem_Command(status_t status)
{
    switch (status) {
@@ -1145,6 +1225,26 @@ exit:
    return jStatus;
}

static void
android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    ALOGV("eventHandlerSetup");

    sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this);

    AudioSystem::setAudioPortCallback(callback);
}

static void
android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz)
{
    ALOGV("eventHandlerFinalize");

    sp<JNIAudioPortCallback> callback;

    AudioSystem::setAudioPortCallback(callback);
}

// ----------------------------------------------------------------------------

static JNINativeMethod gMethods[] = {
@@ -1184,6 +1284,15 @@ static JNINativeMethod gMethods[] = {
};


static JNINativeMethod gEventHandlerMethods[] = {
    {"native_setup",
        "(Ljava/lang/Object;)V",
        (void *)android_media_AudioSystem_eventHandlerSetup},
    {"native_finalize",
        "()V",
        (void *)android_media_AudioSystem_eventHandlerFinalize},
};

int register_android_media_AudioSystem(JNIEnv *env)
{

@@ -1265,8 +1374,19 @@ int register_android_media_AudioSystem(JNIEnv *env)
    gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle",
                                                "Landroid/media/AudioHandle;");

    jclass eventHandlerClass = env->FindClass(kEventHandlerClassPathName);
    gPostEventFromNative = env->GetStaticMethodID(eventHandlerClass, "postEventFromNative",
                                            "(Ljava/lang/Object;IIILjava/lang/Object;)V");


    AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);

    return AndroidRuntime::registerNativeMethods(env,
    int status = AndroidRuntime::registerNativeMethods(env,
                kClassPathName, gMethods, NELEM(gMethods));

    if (status == 0) {
        status = AndroidRuntime::registerNativeMethods(env,
                kEventHandlerClassPathName, gEventHandlerMethods, NELEM(gEventHandlerMethods));
    }
    return status;
}
+7 −2
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.view.KeyEvent;
import java.util.HashMap;
import java.util.ArrayList;


/**
 * AudioManager provides access to volume and ringer mode control.
 * <p>
@@ -61,6 +62,7 @@ public class AudioManager {
    private final boolean mUseVolumeKeySounds;
    private final Binder mToken = new Binder();
    private static String TAG = "AudioManager";
    AudioPortEventHandler mAudioPortEventHandler;

    /**
     * Broadcast intent, a hint for applications that audio is about to become
@@ -438,6 +440,7 @@ public class AudioManager {
                com.android.internal.R.bool.config_useMasterVolume);
        mUseVolumeKeySounds = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_useVolumeKeySounds);
        mAudioPortEventHandler = new AudioPortEventHandler(this);
    }

    private static IAudioService getService()
@@ -3112,17 +3115,19 @@ public class AudioManager {
    }

    /**
     * Register an audio port update listener.
     * Register an audio port list update listener.
     * @hide
     */
    public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
        mAudioPortEventHandler.registerListener(l);
    }

    /**
     * Unregister an audio port update listener.
     * Unregister an audio port list update listener.
     * @hide
     */
    public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
        mAudioPortEventHandler.unregisterListener(l);
    }

    //
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.util.ArrayList;
import java.lang.ref.WeakReference;

/**
 * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
 * posted from JNI
 * @hide
 */

class AudioPortEventHandler {
    private final Handler mHandler;
    private ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners;
    private AudioManager mAudioManager;

    private static String TAG = "AudioPortEventHandler";

    private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1;
    private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2;
    private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
    private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;

    AudioPortEventHandler(AudioManager audioManager) {
        mAudioManager = audioManager;
        mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();

        // find the looper for our new event handler
        Looper looper = Looper.myLooper();
        if (looper == null) {
            throw new IllegalArgumentException("Calling thread not associated with a looper");
        }

        mHandler = new Handler(looper) {
            @Override
            public void handleMessage(Message msg) {
                Log.i(TAG, "handleMessage: "+msg.what);
                ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
                synchronized (this) {
                    if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
                        listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
                        if (mListeners.contains(msg.obj)) {
                            listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
                        }
                    } else {
                        listeners = mListeners;
                    }
                }
                if (listeners.isEmpty()) {
                    return;
                }
                // reset audio port cache if the event corresponds to a change coming
                // from audio policy service or if mediaserver process died.
                if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
                        msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
                        msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
                    mAudioManager.resetAudioPortGeneration();
                }
                ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
                ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
                if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
                    int status = mAudioManager.updateAudioPortCache(ports, patches);
                    if (status != AudioManager.SUCCESS) {
                        return;
                    }
                }

                switch (msg.what) {
                case AUDIOPORT_EVENT_NEW_LISTENER:
                case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
                    AudioPort[] portList = ports.toArray(new AudioPort[0]);
                    for (int i = 0; i < listeners.size(); i++) {
                        listeners.get(i).OnAudioPortListUpdate(portList);
                    }
                    if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
                        break;
                    }
                    // FALL THROUGH

                case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
                    AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
                    for (int i = 0; i < listeners.size(); i++) {
                        listeners.get(i).OnAudioPatchListUpdate(patchList);
                    }
                    break;

                case AUDIOPORT_EVENT_SERVICE_DIED:
                    for (int i = 0; i < listeners.size(); i++) {
                        listeners.get(i).OnServiceDied();
                    }
                    break;

                default:
                    break;
                }
            }
        };

        native_setup(new WeakReference<AudioPortEventHandler>(this));
    }
    private native void native_setup(Object module_this);

    @Override
    protected void finalize() {
        native_finalize();
    }
    private native void native_finalize();

    void registerListener(AudioManager.OnAudioPortUpdateListener l) {
        synchronized (this) {
            mListeners.add(l);
        }
        if (mHandler != null) {
            Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l);
            mHandler.sendMessage(m);
        }
    }

    void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
        synchronized (this) {
            mListeners.remove(l);
        }
    }

    Handler handler() {
        return mHandler;
    }

    @SuppressWarnings("unused")
    private static void postEventFromNative(Object module_ref,
                                            int what, int arg1, int arg2, Object obj) {
        AudioPortEventHandler eventHandler =
                (AudioPortEventHandler)((WeakReference)module_ref).get();
        if (eventHandler == null) {
            return;
        }

        if (eventHandler != null) {
            Handler handler = eventHandler.handler();
            if (handler != null) {
                Message m = handler.obtainMessage(what, arg1, arg2, obj);
                handler.sendMessage(m);
            }
        }
    }

}