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

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

Merge changes from topic "gps no sound" into qt-qpr1-dev

* changes:
  Update playback state only for navigation sound
  Monitor AudioManger Playback state to update the AVRCP playback state
parents 2b0e7562 e7f50211
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ import android.os.Looper;
import android.os.Message;
import android.util.Log;

import com.android.bluetooth.Utils;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -47,6 +50,7 @@ public class BrowsablePlayerConnector {
    private static final int MSG_CONNECT_CB = 1;
    private static final int MSG_TIMEOUT = 2;

    private static BrowsablePlayerConnector sInjectConnector;
    private Handler mHandler;
    private Context mContext;
    private PlayerListCallback mCallback;
@@ -58,11 +62,19 @@ public class BrowsablePlayerConnector {
        void run(List<BrowsedPlayerWrapper> result);
    }

    private static void setInstanceForTesting(BrowsablePlayerConnector connector) {
        Utils.enforceInstrumentationTestMode();
        sInjectConnector = connector;
    }

    static BrowsablePlayerConnector connectToPlayers(
            Context context,
            Looper looper,
            List<ResolveInfo> players,
            PlayerListCallback cb) {
        if (sInjectConnector != null) {
            return sInjectConnector;
        }
        if (cb == null) {
            Log.wtfStack(TAG, "Null callback passed");
            return null;
+5 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.bluetooth.avrcp;

import android.media.session.MediaSession;
import android.os.Looper;
import android.util.Log;

/**
@@ -28,6 +29,10 @@ class GPMWrapper extends MediaPlayerWrapper {
    private static final String TAG = "AvrcpGPMWrapper";
    private static final boolean DEBUG = true;

    GPMWrapper(MediaController controller, Looper looper) {
        super(controller, looper);
    }

    @Override
    boolean isMetadataSynced() {
        if (getQueue() == null) {
+125 −11
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
@@ -33,6 +36,7 @@ import android.util.Log;
import android.view.KeyEvent;

import com.android.bluetooth.Utils;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
@@ -79,6 +83,8 @@ public class MediaPlayerList {
    private Looper mLooper; // Thread all media player callbacks and timeouts happen on
    private PackageManager mPackageManager;
    private MediaSessionManager mMediaSessionManager;
    private MediaData mCurrMediaData = null;
    private final AudioManager mAudioManager;

    private Map<Integer, MediaPlayerWrapper> mMediaPlayers =
            Collections.synchronizedMap(new HashMap<Integer, MediaPlayerWrapper>());
@@ -88,6 +94,9 @@ public class MediaPlayerList {
            Collections.synchronizedMap(new HashMap<Integer, BrowsedPlayerWrapper>());
    private int mActivePlayerId = NO_ACTIVE_PLAYER;

    @VisibleForTesting
    private boolean mAudioPlaybackIsActive = false;

    private AvrcpTargetService.ListCallback mCallback;
    private BrowsablePlayerConnector mBrowsablePlayerConnector;

@@ -122,6 +131,9 @@ public class MediaPlayerList {
        pkgFilter.addDataScheme(PACKAGE_SCHEME);
        context.registerReceiver(mPackageChangedBroadcastReceiver, pkgFilter);

        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager.registerAudioPlaybackCallback(mAudioPlaybackCallback, new Handler(mLooper));

        mMediaSessionManager =
                (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
        mMediaSessionManager.addOnActiveSessionsChangedListener(
@@ -190,6 +202,8 @@ public class MediaPlayerList {
        mMediaSessionManager.setCallback(null, null);
        mMediaSessionManager = null;

        mAudioManager.unregisterAudioPlaybackCallback(mAudioPlaybackCallback);

        mMediaPlayerIds.clear();

        for (MediaPlayerWrapper player : mMediaPlayers.values()) {
@@ -275,7 +289,16 @@ public class MediaPlayerList {
        final MediaPlayerWrapper player = getActivePlayer();
        if (player == null) return null;

        return player.getPlaybackState();
        PlaybackState state = player.getPlaybackState();
        if (mAudioPlaybackIsActive
                && (state == null || state.getState() != PlaybackState.STATE_PLAYING)) {
            return new PlaybackState.Builder()
                .setState(PlaybackState.STATE_PLAYING,
                          state == null ? 0 : state.getPosition(),
                          1.0f)
                .build();
        }
        return state;
    }

    @NonNull
@@ -406,12 +429,8 @@ public class MediaPlayerList {
        }
    }

    // Adds the controller to the MediaPlayerList or updates the controller if we already had
    // a controller for a package. Returns the new ID of the controller where its added or its
    // previous value if it already existed. Returns -1 if the controller passed in is invalid
    int addMediaPlayer(android.media.session.MediaController controller) {
        if (controller == null) return -1;

    @VisibleForTesting
    int addMediaPlayer(MediaController controller) {
        // Each new player has an ID of 1 plus the highest ID. The ID 0 is reserved to signify that
        // there is no active player. If we already have a browsable player for the package, reuse
        // that key.
@@ -427,7 +446,7 @@ public class MediaPlayerList {
        if (mMediaPlayers.containsKey(playerId)) {
            d("Already have a controller for the player: " + packageName + ", updating instead");
            MediaPlayerWrapper player = mMediaPlayers.get(playerId);
            player.updateMediaController(MediaControllerFactory.wrap(controller));
            player.updateMediaController(controller);

            // If the media controller we updated was the active player check if the media updated
            if (playerId == mActivePlayerId) {
@@ -437,8 +456,8 @@ public class MediaPlayerList {
            return playerId;
        }

        MediaPlayerWrapper newPlayer = MediaPlayerWrapper.wrap(
                MediaControllerFactory.wrap(controller),
        MediaPlayerWrapper newPlayer = MediaPlayerWrapperFactory.wrap(
                controller,
                mLooper);

        Log.i(TAG, "Adding wrapped media player: " + packageName + " at key: "
@@ -448,6 +467,18 @@ public class MediaPlayerList {
        return playerId;
    }

    // Adds the controller to the MediaPlayerList or updates the controller if we already had
    // a controller for a package. Returns the new ID of the controller where its added or its
    // previous value if it already existed. Returns -1 if the controller passed in is invalid
    int addMediaPlayer(android.media.session.MediaController controller) {
        if (controller == null) {
            e("Trying to add a null MediaController");
            return -1;
        }

        return addMediaPlayer(MediaControllerFactory.wrap(controller));
    }

    void removeMediaPlayer(int playerId) {
        if (!mMediaPlayers.containsKey(playerId)) {
            e("Trying to remove nonexistent media player: " + playerId);
@@ -495,7 +526,12 @@ public class MediaPlayerList {
            sendFolderUpdate(true, true, false);
        }

        sendMediaUpdate(getActivePlayer().getCurrentMediaData());
        MediaData data = getActivePlayer().getCurrentMediaData();
        if (mAudioPlaybackIsActive) {
            data.state = mCurrMediaData.state;
            Log.d(TAG, "setActivePlayer mAudioPlaybackIsActive=true, state=" + data.state);
        }
        sendMediaUpdate(data);
    }

    // TODO (apanicke): Add logging for media key events in dumpsys
@@ -528,6 +564,8 @@ public class MediaPlayerList {
            data.queue.add(data.metadata);
        }

        Log.d(TAG, "sendMediaUpdate state=" + data.state);
        mCurrMediaData = data;
        mCallback.run(data);
    }

@@ -591,6 +629,78 @@ public class MediaPlayerList {
        }
    };

    void updateMediaForAudioPlayback() {
        MediaData currMediaData = null;
        PlaybackState currState = null;
        if (getActivePlayer() == null) {
            Log.d(TAG, "updateMediaForAudioPlayback: no active player");
            PlaybackState.Builder builder = new PlaybackState.Builder()
                    .setState(PlaybackState.STATE_STOPPED, 0L, 0L);
            List<Metadata> queue = new ArrayList<Metadata>();
            queue.add(Util.empty_data());
            currMediaData = new MediaData(
                    Util.empty_data(),
                    builder.build(),
                    queue
                );
        } else {
            currMediaData = getActivePlayer().getCurrentMediaData();
            currState = currMediaData.state;
        }

        if (currState != null
                && currState.getState() == PlaybackState.STATE_PLAYING) {
            Log.i(TAG, "updateMediaForAudioPlayback: Active player is playing, drop it");
            return;
        }

        if (mAudioPlaybackIsActive) {
            PlaybackState.Builder builder = new PlaybackState.Builder()
                    .setState(PlaybackState.STATE_PLAYING,
                        currState == null ? 0 : currState.getPosition(),
                        1.0f);
            currMediaData.state = builder.build();
        }
        Log.i(TAG, "updateMediaForAudioPlayback: update state=" + currMediaData.state);
        sendMediaUpdate(currMediaData);
    }

    @VisibleForTesting
    void injectAudioPlaybacActive(boolean isActive) {
        mAudioPlaybackIsActive = isActive;
        updateMediaForAudioPlayback();
    }

    private final AudioManager.AudioPlaybackCallback mAudioPlaybackCallback =
            new AudioManager.AudioPlaybackCallback() {
        @Override
        public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
            if (configs == null) {
                return;
            }
            boolean isActive = false;
            Log.v(TAG, "onPlaybackConfigChanged(): Configs list size=" + configs.size());
            for (AudioPlaybackConfiguration config : configs) {
                if (config.isActive() && (config.getAudioAttributes().getUsage()
                            == AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
                        && (config.getAudioAttributes().getContentType()
                            == AudioAttributes.CONTENT_TYPE_SPEECH)) {
                    if (DEBUG) {
                        Log.d(TAG, "onPlaybackConfigChanged(): config="
                                 + AudioPlaybackConfiguration.toLogFriendlyString(config));
                    }
                    isActive = true;
                }
            }
            if (isActive != mAudioPlaybackIsActive) {
                Log.d(TAG, "onPlaybackConfigChanged isActive=" + isActive
                        + ", mAudioPlaybackIsActive=" + mAudioPlaybackIsActive);
                mAudioPlaybackIsActive = isActive;
                updateMediaForAudioPlayback();
            }
        }
    };

    private final MediaPlayerWrapper.Callback mMediaPlayerCallback =
            new MediaPlayerWrapper.Callback() {
        @Override
@@ -605,6 +715,10 @@ public class MediaPlayerList {
                return;
            }

            if (mAudioPlaybackIsActive && (data.state.getState() != PlaybackState.STATE_PLAYING)) {
                Log.d(TAG, "Some audio playbacks are still active, drop it");
                return;
            }
            sendMediaUpdate(data);
        }
    };
+8 −27
Original line number Diff line number Diff line
@@ -54,10 +54,6 @@ class MediaPlayerWrapper {
    private final Object mCallbackLock = new Object();
    private Callback mRegisteredCallback = null;

    protected MediaPlayerWrapper() {
        mCurrentData = new MediaData(null, null, null);
    }

    public interface Callback {
        void mediaUpdatedCallback(MediaData data);
    }
@@ -80,30 +76,15 @@ class MediaPlayerWrapper {
        return true;
    }

    // TODO (apanicke): Implement a factory to make testing and creating interop wrappers easier
    static MediaPlayerWrapper wrap(MediaController controller, Looper looper) {
        if (controller == null || looper == null) {
            e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller
                    + " | Looper: " + looper);
            return null;
        }
    MediaPlayerWrapper(MediaController controller, Looper looper) {
        mMediaController = controller;
        mPackageName = controller.getPackageName();
        mLooper = looper;

        MediaPlayerWrapper newWrapper;
        if (controller.getPackageName().equals("com.google.android.music")) {
            Log.v(TAG, "Creating compatibility wrapper for Google Play Music");
            newWrapper = new GPMWrapper();
        } else {
            newWrapper = new MediaPlayerWrapper();
        }

        newWrapper.mMediaController = controller;
        newWrapper.mPackageName = controller.getPackageName();
        newWrapper.mLooper = looper;

        newWrapper.mCurrentData.queue = Util.toMetadataList(newWrapper.getQueue());
        newWrapper.mCurrentData.metadata = Util.toMetadata(newWrapper.getMetadata());
        newWrapper.mCurrentData.state = newWrapper.getPlaybackState();
        return newWrapper;
        mCurrentData = new MediaData(null, null, null);
        mCurrentData.queue = Util.toMetadataList(getQueue());
        mCurrentData.metadata = Util.toMetadata(getMetadata());
        mCurrentData.state = getPlaybackState();
    }

    void cleanup() {
+52 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 com.android.bluetooth.avrcp;

import android.os.Looper;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Provide a method to inject custom MediaControllerWrapper objects for testing. By using the
 * factory methods instead of calling the wrap method of MediaControllerWrapper directly, we can
 * inject a custom MediaControllerWrapper that can be used with JUnit and Mockito to set
 * expectations and validate behaviour in tests.
 */
public final class MediaPlayerWrapperFactory {
    private static MediaPlayerWrapper sInjectedWrapper;

    static MediaPlayerWrapper wrap(MediaController controller, Looper looper) {
        if (sInjectedWrapper != null) return sInjectedWrapper;
        if (controller == null || looper == null) {
            return null;
        }

        MediaPlayerWrapper newWrapper;
        if (controller.getPackageName().equals("com.google.android.music")) {
            newWrapper = new GPMWrapper(controller, looper);
        } else {
            newWrapper = new MediaPlayerWrapper(controller, looper);
        }
        return newWrapper;
    }

    @VisibleForTesting
    static void inject(MediaPlayerWrapper wrapper) {
        sInjectedWrapper = wrapper;
    }
}
Loading