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

Commit d5bad53a authored by Paul McLean's avatar Paul McLean
Browse files

(Re)routing Notification API in AudioTrack/AudioRecord.

Change-Id: If6594df7e3ed4c4a67878f98c05e3153633cf263
parent 7b4e8873
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -14900,6 +14900,7 @@ package android.media {
  public class AudioRecord {
    ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
    method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
    method public int getAudioFormat();
    method public int getAudioSessionId();
    method public int getAudioSource();
@@ -14910,6 +14911,7 @@ package android.media {
    method public int getNotificationMarkerPosition();
    method public int getPositionNotificationPeriod();
    method public int getRecordingState();
    method public android.media.AudioDeviceInfo getRoutedDevice();
    method public int getSampleRate();
    method public int getState();
    method public int read(byte[], int, int);
@@ -14920,6 +14922,7 @@ package android.media {
    method public int read(java.nio.ByteBuffer, int);
    method public int read(java.nio.ByteBuffer, int, int);
    method public void release();
    method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
    method public int setNotificationMarkerPosition(int);
    method public int setPositionNotificationPeriod(int);
    method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
@@ -14962,6 +14965,7 @@ package android.media {
    ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
    ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
    ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
    method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
    method public int attachAuxEffect(int);
    method public void flush();
    method public int getAudioFormat();
@@ -14979,6 +14983,7 @@ package android.media {
    method public int getPlaybackRate();
    method public int getPositionNotificationPeriod();
    method public android.media.AudioDeviceInfo getPreferredOutputDevice();
    method public android.media.AudioDeviceInfo getRoutedDevice();
    method public int getSampleRate();
    method public int getState();
    method public int getStreamType();
@@ -14987,6 +14992,7 @@ package android.media {
    method public void play() throws java.lang.IllegalStateException;
    method public void release();
    method public int reloadStaticData();
    method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
    method public int setAuxEffectSendLevel(float);
    method public int setLoopPoints(int, int, int);
    method public int setNotificationMarkerPosition(int);
@@ -16347,6 +16353,14 @@ package android.media {
    method public abstract void onAudioDeviceConnection();
  }
  public abstract interface OnAudioRecordRoutingListener {
    method public abstract void onAudioRecordRouting(android.media.AudioRecord);
  }
  public abstract interface OnAudioTrackRoutingListener {
    method public abstract void onAudioTrackRouting(android.media.AudioTrack);
  }
  public final class Rating implements android.os.Parcelable {
    method public int describeContents();
    method public float getPercentRating();
+14 −0
Original line number Diff line number Diff line
@@ -16110,6 +16110,7 @@ package android.media {
  public class AudioRecord {
    ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
    ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
    method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
    method public int getAudioFormat();
    method public int getAudioSessionId();
    method public int getAudioSource();
@@ -16120,6 +16121,7 @@ package android.media {
    method public int getNotificationMarkerPosition();
    method public int getPositionNotificationPeriod();
    method public int getRecordingState();
    method public android.media.AudioDeviceInfo getRoutedDevice();
    method public int getSampleRate();
    method public int getState();
    method public int read(byte[], int, int);
@@ -16130,6 +16132,7 @@ package android.media {
    method public int read(java.nio.ByteBuffer, int);
    method public int read(java.nio.ByteBuffer, int, int);
    method public void release();
    method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
    method public int setNotificationMarkerPosition(int);
    method public int setPositionNotificationPeriod(int);
    method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
@@ -16174,6 +16177,7 @@ package android.media {
    ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
    ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
    ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
    method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
    method public int attachAuxEffect(int);
    method public void flush();
    method public int getAudioFormat();
@@ -16191,6 +16195,7 @@ package android.media {
    method public int getPlaybackRate();
    method public int getPositionNotificationPeriod();
    method public android.media.AudioDeviceInfo getPreferredOutputDevice();
    method public android.media.AudioDeviceInfo getRoutedDevice();
    method public int getSampleRate();
    method public int getState();
    method public int getStreamType();
@@ -16199,6 +16204,7 @@ package android.media {
    method public void play() throws java.lang.IllegalStateException;
    method public void release();
    method public int reloadStaticData();
    method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
    method public int setAuxEffectSendLevel(float);
    method public int setLoopPoints(int, int, int);
    method public int setNotificationMarkerPosition(int);
@@ -17562,6 +17568,14 @@ package android.media {
    method public abstract void onAudioDeviceConnection();
  }
  public abstract interface OnAudioRecordRoutingListener {
    method public abstract void onAudioRecordRouting(android.media.AudioRecord);
  }
  public abstract interface OnAudioTrackRoutingListener {
    method public abstract void onAudioTrackRouting(android.media.AudioTrack);
  }
  public final class Rating implements android.os.Parcelable {
    method public int describeContents();
    method public float getPercentRating();
+117 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Iterator;

import android.annotation.IntDef;
@@ -32,6 +33,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;

/**
@@ -113,6 +115,11 @@ public class AudioRecord
     */
    private static final int NATIVE_EVENT_NEW_POS = 3;

    /**
     * Event id denotes when the routing changes.
     */
    private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;

    private final static String TAG = "android.media.AudioRecord";

    /** @hide */
@@ -1117,7 +1124,7 @@ public class AudioRecord
     * Sets the listener the AudioRecord notifies when a previously set marker is reached or
     * for each periodic record head position update.
     * Use this method to receive AudioRecord events in the Handler associated with another
     * thread than the one in which you created the AudioTrack instance.
     * thread than the one in which you created the AudioRecord instance.
     * @param listener
     * @param handler the Handler that will receive the event notification messages.
     */
@@ -1158,6 +1165,115 @@ public class AudioRecord
    }


    //--------------------------------------------------------------------------
    // (Re)Routing Info
    //--------------------
    /**
     * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
     */
    public AudioDeviceInfo getRoutedDevice() {
        return null;
    }

    /**
     * The message sent to apps when the routing of this AudioRecord changes if they provide
     * a {#link Handler} object to addOnAudioRecordRoutingListener().
     */
    private ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>
        mRoutingChangeListeners =
            new ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>();

    /**
     * Adds an {@link OnAudioRecordRoutingListener} to receive notifications of routing changes
     * on this AudioRecord.
     */
    public void addOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener,
            android.os.Handler handler) {
        if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
            synchronized (mRoutingChangeListeners) {
                mRoutingChangeListeners.put(
                    listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
            }
        }
    }

    /**
     * Removes an {@link OnAudioRecordRoutingListener} which has been previously added
     * to receive notifications of changes to the set of connected audio devices.
     */
    public void removeOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener) {
        synchronized (mRoutingChangeListeners) {
            if (mRoutingChangeListeners.containsKey(listener)) {
                mRoutingChangeListeners.remove(listener);
            }
        }
    }

    /**
     * Helper class to handle the forwarding of native events to the appropriate listener
     * (potentially) handled in a different thread
     */
    private class NativeRoutingEventHandlerDelegate {
        private final Handler mHandler;

        NativeRoutingEventHandlerDelegate(final AudioRecord record,
                                   final OnAudioRecordRoutingListener listener,
                                   Handler handler) {
            // find the looper for our new event handler
            Looper looper;
            if (handler != null) {
                looper = handler.getLooper();
            } else {
                // no given handler, use the looper the AudioRecord was created in
                looper = mInitializationLooper;
            }

            // construct the event handler with this looper
            if (looper != null) {
                // implement the event handler delegate
                mHandler = new Handler(looper) {
                    @Override
                    public void handleMessage(Message msg) {
                        if (record == null) {
                            return;
                        }
                        switch(msg.what) {
                        case NATIVE_EVENT_ROUTING_CHANGE:
                            if (listener != null) {
                                listener.onAudioRecordRouting(record);
                            }
                            break;
                        default:
                            loge("Unknown native event type: " + msg.what);
                            break;
                        }
                    }
                };
            } else {
                mHandler = null;
            }
        }

        Handler getHandler() {
            return mHandler;
        }
    }
    /**
     * Sends device list change notification to all listeners.
     */
    private void broadcastRoutingChange() {
        Collection<NativeRoutingEventHandlerDelegate> values;
        synchronized (mRoutingChangeListeners) {
            values = mRoutingChangeListeners.values();
        }
        for(NativeRoutingEventHandlerDelegate delegate : values) {
            Handler handler = delegate.getHandler();
            if (handler != null) {
                handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
            }
        }
    }

    /**
     * Sets the period at which the listener is called, if set with
     * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or
+122 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.NioUtils;
import java.util.Collection;

import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -35,6 +36,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.app.IAppOpsService;
@@ -174,6 +176,12 @@ public class AudioTrack
     */
    private static final int NATIVE_EVENT_NEW_POS = 4;

    /**
     * Event id denotes when the routing changes.
     */
    private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;


    private final static String TAG = "android.media.AudioTrack";


@@ -222,7 +230,7 @@ public class AudioTrack
    /**
     * Handler for events coming from the native code.
     */
    private NativeEventHandlerDelegate mEventHandlerDelegate;
    private NativePositionEventHandlerDelegate mEventHandlerDelegate;
    /**
     * Looper associated with the thread that creates the AudioTrack instance.
     */
@@ -1216,7 +1224,7 @@ public class AudioTrack
    public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
                                                    Handler handler) {
        if (listener != null) {
            mEventHandlerDelegate = new NativeEventHandlerDelegate(this, listener, handler);
            mEventHandlerDelegate = new NativePositionEventHandlerDelegate(this, listener, handler);
        } else {
            mEventHandlerDelegate = null;
        }
@@ -1884,6 +1892,66 @@ public class AudioTrack
        return mPreferredDevice;
    }

    //--------------------------------------------------------------------------
    // (Re)Routing Info
    //--------------------
    /**
     * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
     */
    public AudioDeviceInfo getRoutedDevice() {
        return null;
    }

    /**
     * The message sent to apps when the routing of this AudioTrack changes if they provide
     * a {#link Handler} object to addOnAudioTrackRoutingListener().
     */
    private ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>
        mRoutingChangeListeners =
            new ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>();

    /**
     * Adds an {@link OnAudioTrackRoutingListener} to receive notifications of routing changes
     * on this AudioTrack.
     */
    public void addOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener,
            android.os.Handler handler) {
        if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
            synchronized (mRoutingChangeListeners) {
                mRoutingChangeListeners.put(
                    listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
            }
        }
    }

    /**
     * Removes an {@link OnAudioTrackRoutingListener} which has been previously added
     * to receive notifications of changes to the set of connected audio devices.
     */
    public void removeOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener) {
        synchronized (mRoutingChangeListeners) {
            if (mRoutingChangeListeners.containsKey(listener)) {
                mRoutingChangeListeners.remove(listener);
            }
        }
    }

    /**
     * Sends device list change notification to all listeners.
     */
    private void broadcastRoutingChange() {
        Collection<NativeRoutingEventHandlerDelegate> values;
        synchronized (mRoutingChangeListeners) {
            values = mRoutingChangeListeners.values();
        }
        for(NativeRoutingEventHandlerDelegate delegate : values) {
            Handler handler = delegate.getHandler();
            if (handler != null) {
                handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
            }
        }
    }

    //---------------------------------------------------------
    // Interface definitions
    //--------------------
@@ -1912,10 +1980,10 @@ public class AudioTrack
     * Helper class to handle the forwarding of native events to the appropriate listener
     * (potentially) handled in a different thread
     */
    private class NativeEventHandlerDelegate {
    private class NativePositionEventHandlerDelegate {
        private final Handler mHandler;

        NativeEventHandlerDelegate(final AudioTrack track,
        NativePositionEventHandlerDelegate(final AudioTrack track,
                                   final OnPlaybackPositionUpdateListener listener,
                                   Handler handler) {
            // find the looper for our new event handler
@@ -1963,6 +2031,55 @@ public class AudioTrack
        }
    }

    /**
     * Helper class to handle the forwarding of native events to the appropriate listener
     * (potentially) handled in a different thread
     */
    private class NativeRoutingEventHandlerDelegate {
        private final Handler mHandler;

        NativeRoutingEventHandlerDelegate(final AudioTrack track,
                                   final OnAudioTrackRoutingListener listener,
                                   Handler handler) {
            // find the looper for our new event handler
            Looper looper;
            if (handler != null) {
                looper = handler.getLooper();
            } else {
                // no given handler, use the looper the AudioTrack was created in
                looper = mInitializationLooper;
            }

            // construct the event handler with this looper
            if (looper != null) {
                // implement the event handler delegate
                mHandler = new Handler(looper) {
                    @Override
                    public void handleMessage(Message msg) {
                        if (track == null) {
                            return;
                        }
                        switch(msg.what) {
                        case NATIVE_EVENT_ROUTING_CHANGE:
                            if (listener != null) {
                                listener.onAudioTrackRouting(track);
                            }
                            break;
                        default:
                            loge("Unknown native event type: " + msg.what);
                            break;
                        }
                    }
                };
            } else {
                mHandler = null;
            }
        }

        Handler getHandler() {
            return mHandler;
        }
    }

    //---------------------------------------------------------
    // Java methods called from the native side
@@ -1976,7 +2093,7 @@ public class AudioTrack
            return;
        }

        NativeEventHandlerDelegate delegate = track.mEventHandlerDelegate;
        NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
        if (delegate != null) {
            Handler handler = delegate.getHandler();
            if (handler != null) {
+29 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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;

/**
 * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
 * {@link AudioDevicesManager}
 */
public interface OnAudioRecordRoutingListener {
    /**
     * Called when the routing of an AudioRecord changes from either and explicit or
     * policy rerouting.
     */
    public void onAudioRecordRouting(AudioRecord audioRecord);
}
Loading