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

Commit 086995c6 authored by Eric Laurent's avatar Eric Laurent Committed by Android Git Automerger
Browse files

am b4499f84: Merge "audio routing update listener" into lmp-preview-dev

* commit 'b4499f84605faf06479cbb793e945d0af50fdead':
  audio routing update listener
parents ec2f4b0a 8725859e
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()
@@ -3116,17 +3119,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);
            }
        }
    }

}