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

Commit f8a6f1da authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add AudioRouting interface in MediaPlayer"

parents 17761e3c 6e5a628a
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -23008,8 +23008,9 @@ package android.media {
    field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
  }
  public class MediaPlayer implements android.media.VolumeAutomation {
  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
    ctor public MediaPlayer();
    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
    method public void addTimedTextSource(java.lang.String, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
    method public void addTimedTextSource(android.content.Context, android.net.Uri, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
    method public void addTimedTextSource(java.io.FileDescriptor, java.lang.String) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -23031,6 +23032,8 @@ package android.media {
    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
    method public android.os.PersistableBundle getMetrics();
    method public android.media.PlaybackParams getPlaybackParams();
    method public android.media.AudioDeviceInfo getPreferredDevice();
    method public android.media.AudioDeviceInfo getRoutedDevice();
    method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
    method public android.media.SyncParams getSyncParams();
    method public android.media.MediaTimestamp getTimestamp();
@@ -23046,6 +23049,7 @@ package android.media {
    method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
    method public void release();
    method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
    method public void reset();
    method public void restoreKeys(byte[]) throws android.media.MediaPlayer.NoDrmSchemeException;
    method public void seekTo(long, int);
@@ -23082,6 +23086,7 @@ package android.media {
    method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
    method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
    method public void setPlaybackParams(android.media.PlaybackParams);
    method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
    method public void setScreenOnWhilePlaying(boolean);
    method public void setSurface(android.view.Surface);
    method public void setSyncParams(android.media.SyncParams);
+6 −1
Original line number Diff line number Diff line
@@ -24901,8 +24901,9 @@ package android.media {
    field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
  }
  public class MediaPlayer implements android.media.VolumeAutomation {
  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
    ctor public MediaPlayer();
    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
    method public void addTimedTextSource(java.lang.String, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
    method public void addTimedTextSource(android.content.Context, android.net.Uri, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
    method public void addTimedTextSource(java.io.FileDescriptor, java.lang.String) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -24924,6 +24925,8 @@ package android.media {
    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
    method public android.os.PersistableBundle getMetrics();
    method public android.media.PlaybackParams getPlaybackParams();
    method public android.media.AudioDeviceInfo getPreferredDevice();
    method public android.media.AudioDeviceInfo getRoutedDevice();
    method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
    method public android.media.SyncParams getSyncParams();
    method public android.media.MediaTimestamp getTimestamp();
@@ -24939,6 +24942,7 @@ package android.media {
    method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
    method public void release();
    method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
    method public void reset();
    method public void restoreKeys(byte[]) throws android.media.MediaPlayer.NoDrmSchemeException;
    method public void seekTo(long, int);
@@ -24975,6 +24979,7 @@ package android.media {
    method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
    method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
    method public void setPlaybackParams(android.media.PlaybackParams);
    method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
    method public void setScreenOnWhilePlaying(boolean);
    method public void setSurface(android.view.Surface);
    method public void setSyncParams(android.media.SyncParams);
+6 −1
Original line number Diff line number Diff line
@@ -23211,8 +23211,9 @@ package android.media {
    field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
  }
  public class MediaPlayer implements android.media.VolumeAutomation {
  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
    ctor public MediaPlayer();
    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
    method public void addTimedTextSource(java.lang.String, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
    method public void addTimedTextSource(android.content.Context, android.net.Uri, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
    method public void addTimedTextSource(java.io.FileDescriptor, java.lang.String) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -23234,6 +23235,8 @@ package android.media {
    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
    method public android.os.PersistableBundle getMetrics();
    method public android.media.PlaybackParams getPlaybackParams();
    method public android.media.AudioDeviceInfo getPreferredDevice();
    method public android.media.AudioDeviceInfo getRoutedDevice();
    method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
    method public android.media.SyncParams getSyncParams();
    method public android.media.MediaTimestamp getTimestamp();
@@ -23249,6 +23252,7 @@ package android.media {
    method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
    method public void release();
    method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
    method public void reset();
    method public void restoreKeys(byte[]) throws android.media.MediaPlayer.NoDrmSchemeException;
    method public void seekTo(long, int);
@@ -23285,6 +23289,7 @@ package android.media {
    method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
    method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
    method public void setPlaybackParams(android.media.PlaybackParams);
    method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
    method public void setScreenOnWhilePlaying(boolean);
    method public void setSurface(android.view.Surface);
    method public void setSyncParams(android.media.SyncParams);
+163 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
import android.util.Pair;
import android.util.ArrayMap;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.widget.VideoView;
@@ -58,6 +59,7 @@ import android.media.SubtitleData;
import android.media.SubtitleTrack.RenderingWidget;
import android.media.SyncParams;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;

import libcore.io.IoBridge;
@@ -577,6 +579,7 @@ import java.util.Vector;
public class MediaPlayer extends PlayerBase
                         implements SubtitleController.Listener
                                  , VolumeAutomation
                                  , AudioRouting
{
    /**
       Constant to retrieve only the new metadata since the last
@@ -1417,6 +1420,155 @@ public class MediaPlayer extends PlayerBase

    private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);

    //--------------------------------------------------------------------------
    // Explicit Routing
    //--------------------
    private AudioDeviceInfo mPreferredDevice = null;

    /**
     * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
     * the output from this MediaPlayer.
     * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source.
     *  If deviceInfo is null, default routing is restored.
     * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
     * does not correspond to a valid audio device.
     */
    @Override
    public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
        if (deviceInfo != null && !deviceInfo.isSink()) {
            return false;
        }
        int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0;
        boolean status = native_setOutputDevice(preferredDeviceId);
        if (status == true) {
            synchronized (this) {
                mPreferredDevice = deviceInfo;
            }
        }
        return status;
    }

    /**
     * Returns the selected output specified by {@link #setPreferredDevice}. Note that this
     * is not guaranteed to correspond to the actual device being used for playback.
     */
    @Override
    public AudioDeviceInfo getPreferredDevice() {
        synchronized (this) {
            return mPreferredDevice;
        }
    }

    /**
     * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer
     * Note: The query is only valid if the MediaPlayer is currently playing.
     * If the player is not playing, the returned device can be null or correspond to previously
     * selected device when the player was last active.
     */
    @Override
    public AudioDeviceInfo getRoutedDevice() {
        int deviceId = native_getRoutedDeviceId();
        if (deviceId == 0) {
            return null;
        }
        AudioDeviceInfo[] devices =
                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
        for (int i = 0; i < devices.length; i++) {
            if (devices[i].getId() == deviceId) {
                return devices[i];
            }
        }
        return null;
    }

    /*
     * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
     */
    private void enableNativeRoutingCallbacksLocked(boolean enabled) {
        if (mRoutingChangeListeners.size() == 0) {
            native_enableDeviceCallback(enabled);
        }
    }

    /**
     * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
     * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)}
     * by an app to receive (re)routing notifications.
     */
    @GuardedBy("mRoutingChangeListeners")
    private ArrayMap<AudioRouting.OnRoutingChangedListener,
            NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();

    /**
     * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
     * changes on this MediaPlayer.
     * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
     * notifications of rerouting events.
     * @param handler  Specifies the {@link Handler} object for the thread on which to execute
     * the callback. If <code>null</code>, the handler on the main looper will be used.
     */
    @Override
    public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
            Handler handler) {
        synchronized (mRoutingChangeListeners) {
            if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
                enableNativeRoutingCallbacksLocked(true);
                mRoutingChangeListeners.put(
                        listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
            }
        }
    }

    /**
     * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
     * to receive rerouting notifications.
     * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
     * to remove.
     */
    @Override
    public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
        synchronized (mRoutingChangeListeners) {
            if (mRoutingChangeListeners.containsKey(listener)) {
                mRoutingChangeListeners.remove(listener);
                enableNativeRoutingCallbacksLocked(false);
            }
        }
    }

    /**
     * Helper class to handle the forwarding of native events to the appropriate listener
     * (potentially) handled in a different thread
     */
    private class NativeRoutingEventHandlerDelegate {
        private MediaPlayer mMediaPlayer;
        private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
        private Handler mHandler;

        NativeRoutingEventHandlerDelegate(final MediaPlayer mediaPlayer,
                final AudioRouting.OnRoutingChangedListener listener, Handler handler) {
            mMediaPlayer = mediaPlayer;
            mOnRoutingChangedListener = listener;
            mHandler = handler != null ? handler : mEventHandler;
        }

        void notifyClient() {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (mOnRoutingChangedListener != null) {
                            mOnRoutingChangedListener.onRoutingChanged(mMediaPlayer);
                        }
                    }
                });
            }
        }
    }

    private native final boolean native_setOutputDevice(int deviceId);
    private native final int native_getRoutedDeviceId();
    private native final void native_enableDeviceCallback(boolean enabled);

    /**
     * Set the low-level power management behavior for this MediaPlayer.  This
     * can be used when the MediaPlayer is not playing through a SurfaceHolder
@@ -3176,6 +3328,7 @@ public class MediaPlayer extends PlayerBase
    private static final int MEDIA_SUBTITLE_DATA = 201;
    private static final int MEDIA_META_DATA = 202;
    private static final int MEDIA_DRM_INFO = 210;
    private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;

    private TimeProvider mTimeProvider;

@@ -3414,6 +3567,16 @@ public class MediaPlayer extends PlayerBase
            case MEDIA_NOP: // interface test message - ignore
                break;

            case MEDIA_AUDIO_ROUTING_CHANGED:
                AudioManager.resetAudioPortGeneration();
                synchronized (mRoutingChangeListeners) {
                    for (NativeRoutingEventHandlerDelegate delegate
                            : mRoutingChangeListeners.values()) {
                        delegate.notifyClient();
                    }
                }
                return;

            default:
                Log.e(TAG, "Unknown message type " + msg.what);
                return;
+43 −0
Original line number Diff line number Diff line
@@ -1387,6 +1387,44 @@ static void android_media_MediaPlayer_releaseDrm(JNIEnv *env, jobject thiz)
// Modular DRM end
// ----------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////////////////
// AudioRouting begin
static jboolean android_media_MediaPlayer_setOutputDevice(JNIEnv *env, jobject thiz, jint device_id)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        return false;
    }
    return mp->setOutputDevice(device_id) == NO_ERROR;
}

static jint android_media_MediaPlayer_getRoutedDeviceId(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        return AUDIO_PORT_HANDLE_NONE;
    }
    return mp->getRoutedDeviceId();
}

static void android_media_MediaPlayer_enableDeviceCallback(
        JNIEnv* env, jobject thiz, jboolean enabled)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        return;
    }

    status_t status = mp->enableAudioDeviceCallback(enabled);
    if (status != NO_ERROR) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        ALOGE("enable device callback failed: %d", status);
    }
}

// AudioRouting end
// ----------------------------------------------------------------------------

static const JNINativeMethod gMethods[] = {
    {
        "nativeSetDataSource",
@@ -1448,6 +1486,11 @@ static const JNINativeMethod gMethods[] = {
    // Modular DRM
    { "_prepareDrm", "([B[B)V",                                 (void *)android_media_MediaPlayer_prepareDrm },
    { "_releaseDrm", "()V",                                     (void *)android_media_MediaPlayer_releaseDrm },

    // AudioRouting
    {"native_setOutputDevice", "(I)Z",                          (void *)android_media_MediaPlayer_setOutputDevice},
    {"native_getRoutedDeviceId", "()I",                         (void *)android_media_MediaPlayer_getRoutedDeviceId},
    {"native_enableDeviceCallback", "(Z)V",                     (void *)android_media_MediaPlayer_enableDeviceCallback},
};

// This function only registers the native methods